[16.0][MIG] stock_picking_batch_report

This commit is contained in:
Christopher Ormaza
2023-10-18 09:39:27 -05:00
parent 89ffaeb680
commit c69a5298d3
3 changed files with 391 additions and 423 deletions

View File

@@ -3,7 +3,7 @@
{
"name": "Stock Picking Batch Report",
"version": "14.0.1.0.0",
"version": "16.0.1.0.0",
"author": "Open Source Integrators, " "Odoo Community Association (OCA)",
"license": "AGPL-3",
"summary": "Stock Picking Batch Report",

View File

@@ -8,19 +8,4 @@ class StockPikcingBatch(models.Model):
_inherit = "stock.picking.batch"
def get_out_pickings(self):
for rec in self:
# TO BE Modified Later
# if (
# rec.picking_type_id.warehouse_id
# and rec.picking_type_id.warehouse_id.delivery_steps != "pick_ship"
# ):
# raise UserError(
# _(
# "This report is only available for "
# "warehouses configured with 2-steps delivery"
# )
# )
out_pickings = (
rec.mapped("move_ids").mapped("move_dest_ids").mapped("picking_id")
)
return out_pickings
return self.mapped("move_ids.move_dest_ids.picking_id")

View File

@@ -1,333 +1,257 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<template id="stock_picking_batch.report_picking_batch">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t
t-set="move_line_ids"
t-value="o.picking_ids.mapped('move_line_ids').sorted(lambda line: (line.location_id.display_name, line.product_id.name))"
/>
<t
t-set="has_serial_number"
t-value="move_line_ids.filtered('lot_id')"
groups="stock.group_production_lot"
/>
<t
t-set="has_uom"
t-value="move_line_ids.filtered('product_uom_id')"
groups="uom.group_uom"
/>
<t t-call="web.external_layout">
<div class="page">
<div class="d-flex">
<div><h3>Batch : <span t-field="o.name" /></h3></div>
<div class="mr-auto">
<img
alt="Barcode"
t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Code128', quote_plus(o.name or ''), 600, 150)"
style="width:300px;height:50px"
/>
</div>
<template id="stock_picking_batch.report_picking_batch">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t
t-set="move_line_ids"
t-value="o.picking_ids.mapped('move_line_ids').sorted(lambda line: (line.location_id.display_name, line.product_id.name))"
/>
<t
t-set="has_serial_number"
t-value="move_line_ids.filtered('lot_id')"
groups="stock.group_production_lot"
/>
<t
t-set="has_uom"
t-value="move_line_ids.filtered('product_uom_id')"
groups="uom.group_uom"
/>
<t t-call="web.external_layout">
<div class="page">
<div class="d-flex">
<div><h3>Batch : <span t-field="o.name" /></h3></div>
<div class="mr-auto">
<img
alt="Barcode"
t-att-src="'/report/barcode/?barcode_type=%s&amp;value=%s&amp;width=%s&amp;height=%s' % ('Code128', quote_plus(o.name or ''), 600, 150)"
style="width:300px;height:50px"
/>
</div>
<div t-if="o.user_id">
<strong>Responsible:</strong>
<span t-field="o.user_id" />
</div><br />
<table class="table table-condensed">
<thead>
<tr>
<th width="27%">Location</th>
<th>Product</th>
<th t-if="has_serial_number" width="15%">
<strong>Lot/Serial Number</strong>
</th>
<th>Quantity</th>
<th width="23%">Transfer</th>
</div>
<div t-if="o.user_id">
<strong>Responsible:</strong>
<span t-field="o.user_id" />
</div><br />
<table class="table table-condensed">
<thead>
<tr>
<th width="27%">Location</th>
<th>Product</th>
<th t-if="has_serial_number" width="15%">
<strong>Lot/Serial Number</strong>
</th>
<th>Quantity</th>
<th width="23%">Transfer</th>
</tr>
</thead>
<tbody>
<tr
t-foreach="move_line_ids"
t-as="move_operation"
</tr>
</thead>
<tbody>
<tr t-foreach="move_line_ids" t-as="move_operation">
<td>
<span
t-esc="move_operation.mapped('location_id').display_name"
/>
</td>
<td>
<span
t-field="move_operation.display_name"
/>
</td>
<td
t-if="has_serial_number"
class="text-center h6"
width="15%"
>
<td>
<span
t-esc="move_operation.mapped('location_id').display_name"
/>
</td>
<td>
<span
t-field="move_operation.display_name"
/>
</td>
<td
t-if="has_serial_number"
class="text-center h6"
width="15%"
>
<img
t-if="move_operation.lot_id or move_operation.lot_name"
t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s&amp;humanreadable=1' % ('Code128', move_operation.lot_id.name, 600, 100)"
style="width:100%;height:35px;"
alt="Barcode"
/>
</td>
<td>
<t t-if="not has_package">
<t
t-if="move_operation.state == 'done'"
>
<span
t-esc="sum(move_operation.mapped('qty_done'))"
/>
</t>
<t t-else="">
<span
t-esc="sum(move_operation.mapped('product_uom_qty'))"
/>
</t>
</t>
<t t-if="has_package">
<img
t-if="move_operation.lot_id or move_operation.lot_name"
t-att-src="'/report/barcode/?barcode_type=%s&amp;value=%s&amp;width=%s&amp;height=%s&amp;humanreadable=1' % ('Code128', move_operation.lot_id.name, 600, 100)"
style="width:100%;height:35px;"
alt="Barcode"
/>
</td>
<td>
<t t-if="not has_package">
<t
t-if="move_operation.state == 'done'"
>
<span
t-esc="sum(move_operation.mapped('qty_done'))"
/>
</t>
<span
t-field="move_operation.product_uom_id"
groups="uom.group_uom"
/>
</td>
<td>
<span
t-esc="move_operation.mapped('picking_id').display_name"
/>
</td>
</tr>
</tbody>
</table>
<p style="page-break-after: always;" />
<t t-set="pickings" t-value="o.get_out_pickings()" />
<t t-foreach="pickings" t-as="picking">
<t
t-set="picking"
t-value="picking.with_context(lang=picking.partner_id.lang)"
/>
<t
t-set="partner"
t-value="picking.partner_id or (picking.move_lines and picking.move_lines[0].partner_id) or False"
/>
<t t-if="partner" name="partner_header">
<div class="row">
<div class="col-5 offset-7">
<div
t-esc="partner"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'
/>
</div>
</div>
</t>
<div class="page">
<h2>
<span t-field="picking.name" />
</h2>
<div class="row mt32 mb32">
<div
t-if="picking.origin"
class="col-auto"
name="div_origin"
>
<strong>Order:</strong>
<p t-field="picking.origin" />
</div>
<div
t-if="picking.state"
class="col-auto"
name="div_sched_date"
>
<strong>Shipping Date:</strong>
<t t-if="picking.state == 'done'">
<p t-field="picking.date_done" />
</t>
<t t-if="picking.state != 'done'">
<p
t-field="picking.scheduled_date"
/>
</t>
</div>
<div
t-if="picking.picking_type_id.code == 'outgoing' and picking.carrier_id"
class="col-auto"
>
<strong>Carrier:</strong>
<p t-field="picking.carrier_id" />
</div>
</div>
<table
class="table table-sm"
t-if="picking.state!='done'"
name="stock_move_table"
>
<thead>
<tr>
<th name="th_sm_product"><strong
>Product</strong></th>
<th name="th_sm_quantity"><strong
>Quantity</strong></th>
</tr>
</thead>
<tbody>
<t
t-set="lines"
t-value="picking.move_lines.filtered(lambda x: x.product_uom_qty)"
/>
<tr t-foreach="lines" t-as="move">
<td>
<span
t-field="move.product_id"
/>
<p
t-if="move.description_picking != move.product_id.name"
>
<span
t-field="move.description_picking"
/>
</p>
</td>
<td>
<span
t-field="move.product_uom_qty"
/>
<span
t-field="move.product_uom"
/>
</td>
</tr>
</tbody>
</table>
<table
class="table table-sm mt48"
t-if="picking.move_line_ids and picking.state=='done'"
name="stock_move_line_table"
>
<t
t-set="has_serial_number"
t-value="False"
/>
<t
t-set="has_serial_number"
t-value="picking.move_line_ids.mapped('lot_id')"
groups="stock.group_lot_on_delivery_slip"
/>
<thead>
<tr>
<th name="th_sml_product"><strong
>Product</strong></th>
<t
name="lot_serial"
t-if="has_serial_number"
>
<th>
Lot/Serial Number
</th>
</t>
<th
name="th_sml_quantity"
class="text-center"
><strong>Quantity</strong></th>
</tr>
</thead>
<tbody>
<!-- This part gets complicated with different use cases (additional use cases in extensions of this report):
1. If serial numbers are used and set to print on delivery slip => print lines as is, otherwise group them by overlapping
product + description + uom combinations
2. If any packages are assigned => split products up by package (or non-package) and then apply use case 1 -->
<!-- If has destination packages => create sections of corresponding products -->
<t
t-if="picking.has_packages"
name="has_packages"
>
<t
t-set="packages"
t-value="picking.move_line_ids.mapped('result_package_id')"
/>
<t
t-foreach="packages"
t-as="package"
>
<t
t-call="stock.stock_report_delivery_package_section_line"
/>
<t
t-set="package_move_lines"
t-value="picking.move_line_ids.filtered(lambda l: l.result_package_id == package)"
/>
<!-- If printing lots/serial numbers => keep products in original lines -->
<t t-if="has_serial_number">
<tr
t-foreach="package_move_lines"
t-as="move_line"
>
<t
t-call="stock.stock_report_delivery_has_serial_move_line"
/>
</tr>
</t>
<!-- If not printing lots/serial numbers => merge lines with same product+description+uom -->
<t t-else="">
<t
t-set="aggregated_lines"
t-value="package_move_lines._get_aggregated_product_quantities()"
/>
<t
t-call="stock.stock_report_delivery_aggregated_move_lines"
/>
</t>
</t>
<!-- Make sure we do another section for package-less products if they exist -->
<t
t-set="move_lines"
t-value="picking.move_line_ids.filtered(lambda l: not l.result_package_id)"
/>
<t
t-if="move_lines"
name="no_package_move_lines"
>
<t
t-call="stock.stock_report_delivery_no_package_section_line"
name="no_package_section"
/>
<t t-if="has_serial_number">
<tr
t-foreach="move_lines"
t-as="move_line"
>
<t
t-call="stock.stock_report_delivery_has_serial_move_line"
/>
</tr>
</t>
<t t-else="">
<t
t-set="aggregated_lines"
t-value="move_lines._get_aggregated_product_quantities()"
/>
<t t-if="aggregated_lines">
<t
t-call="stock.stock_report_delivery_aggregated_move_lines"
/>
</t>
</t>
</t>
</t>
<!-- No destination packages -->
<t t-else="">
<span
t-esc="sum(move_operation.mapped('move_id.product_uom_qty'))"
/>
</t>
</t>
<t t-if="has_package">
<span
t-esc="sum(move_operation.mapped('qty_done'))"
/>
</t>
<span
t-field="move_operation.product_uom_id"
groups="uom.group_uom"
/>
</td>
<td>
<span
t-esc="move_operation.mapped('picking_id').display_name"
/>
</td>
</tr>
</tbody>
</table>
<p style="page-break-after: always;" />
<t t-set="pickings" t-value="o.get_out_pickings()" />
<t t-foreach="pickings" t-as="picking">
<t
t-set="picking"
t-value="picking.with_context(lang=picking.partner_id.lang)"
/>
<t
t-set="partner"
t-value="picking.partner_id or (picking.move_ids and picking.move_ids[0].partner_id) or False"
/>
<t t-if="partner" name="partner_header">
<div class="row">
<div class="col-5 offset-7">
<div
t-esc="partner"
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True}'
/>
</div>
</div>
</t>
<div class="page">
<h2>
<span t-field="picking.name" />
</h2>
<div class="row mt32 mb32">
<div
t-if="picking.origin"
class="col-auto"
name="div_origin"
>
<strong>Order:</strong>
<p t-field="picking.origin" />
</div>
<div
t-if="picking.state"
class="col-auto"
name="div_sched_date"
>
<strong>Shipping Date:</strong>
<t t-if="picking.state == 'done'">
<p t-field="picking.date_done" />
</t>
<t t-if="picking.state != 'done'">
<p t-field="picking.scheduled_date" />
</t>
</div>
<div
t-if="picking.picking_type_id.code == 'outgoing' and picking.carrier_id"
class="col-auto"
>
<strong>Carrier:</strong>
<p t-field="picking.carrier_id" />
</div>
</div>
<table
class="table table-sm"
t-if="picking.state!='done'"
name="stock_move_table"
>
<thead>
<tr>
<th name="th_sm_product"><strong
>Product</strong></th>
<th name="th_sm_quantity"><strong
>Quantity</strong></th>
</tr>
</thead>
<tbody>
<t
t-set="lines"
t-value="picking.move_ids.filtered(lambda x: x.product_uom_qty)"
/>
<tr t-foreach="lines" t-as="move">
<td>
<span t-field="move.product_id" />
<p
t-if="move.description_picking != move.product_id.name"
>
<span
t-field="move.description_picking"
/>
</p>
</td>
<td>
<span
t-field="move.product_uom_qty"
/>
<span t-field="move.product_uom" />
</td>
</tr>
</tbody>
</table>
<table
class="table table-sm mt48"
t-if="picking.move_line_ids and picking.state=='done'"
name="stock_move_line_table"
>
<t t-set="has_serial_number" t-value="False" />
<t
t-set="has_serial_number"
t-value="picking.move_line_ids.mapped('lot_id')"
groups="stock.group_lot_on_delivery_slip"
/>
<thead>
<tr>
<th name="th_sml_product"><strong
>Product</strong></th>
<t
name="lot_serial"
t-if="has_serial_number"
>
<th>
Lot/Serial Number
</th>
</t>
<th
name="th_sml_quantity"
class="text-center"
><strong>Quantity</strong></th>
</tr>
</thead>
<tbody>
<!-- This part gets complicated with different use cases (additional use cases in extensions of this report):
1. If serial numbers are used and set to print on delivery slip => print lines as is, otherwise group them by overlapping
product + description + uom combinations
2. If any packages are assigned => split products up by package (or non-package) and then apply use case 1 -->
<!-- If has destination packages => create sections of corresponding products -->
<t
t-if="picking.has_packages"
name="has_packages"
>
<t
t-set="packages"
t-value="picking.move_line_ids.mapped('result_package_id')"
/>
<t t-foreach="packages" t-as="package">
<t
t-call="stock.stock_report_delivery_package_section_line"
/>
<t
t-set="package_move_lines"
t-value="picking.move_line_ids.filtered(lambda l: l.result_package_id == package)"
/>
<!-- If printing lots/serial numbers => keep products in original lines -->
<t t-if="has_serial_number">
<tr
t-foreach="picking.move_line_ids"
t-foreach="package_move_lines"
t-as="move_line"
>
<t
@@ -335,109 +259,168 @@
/>
</tr>
</t>
<!-- If not printing lots/serial numbers => merge lines with same product -->
<t
t-else=""
name="aggregated_move_lines"
>
<!-- If not printing lots/serial numbers => merge lines with same product+description+uom -->
<t t-else="">
<t
t-set="aggregated_lines"
t-value="picking.move_line_ids._get_aggregated_product_quantities()"
t-value="package_move_lines._get_aggregated_product_quantities()"
/>
<t
<t
t-call="stock.stock_report_delivery_aggregated_move_lines"
/>
</t>
</t>
</tbody>
</table>
<t
t-set="backorders"
t-value="picking.backorder_ids.filtered(lambda x: x.state not in ('done', 'cancel'))"
/>
<t t-if="picking.backorder_ids and backorders">
<p>
<span
>All items couldn't be shipped, the following items will be shipped as soon as they become available.</span>
</p>
<table
class="table table-sm"
name="stock_backorder_table"
>
<thead>
<tr>
<th name="th_sb_product"><strong
>Product</strong></th>
<th
name="th_sb_quantity"
class="text-center"
><strong>Quantity</strong></th>
</tr>
</thead>
<tbody>
<!-- Make sure we do another section for package-less products if they exist -->
<t
t-set="move_lines"
t-value="picking.move_line_ids.filtered(lambda l: not l.result_package_id)"
/>
<t
t-if="move_lines"
name="no_package_move_lines"
>
<t
t-foreach="backorders"
t-as="backorder"
>
<t
t-set="bo_lines"
t-value="backorder.move_lines.filtered(lambda x: x.product_uom_qty)"
/>
t-call="stock.stock_report_delivery_no_package_section_line"
name="no_package_section"
/>
<t t-if="has_serial_number">
<tr
t-foreach="bo_lines"
t-as="bo_line"
t-foreach="move_lines"
t-as="move_line"
>
<td>
<span
t-field="bo_line.product_id"
/>
<p
t-if="bo_line.description_picking != bo_line.product_id.name"
>
<span
t-field="bo_line.description_picking"
/>
</p>
</td>
<td class="text-center">
<span
t-field="bo_line.product_uom_qty"
/>
<span
t-field="bo_line.product_uom"
/>
</td>
<t
t-call="stock.stock_report_delivery_has_serial_move_line"
/>
</tr>
</t>
</tbody>
</table>
</t>
<div
t-if="picking.signature"
class="mt32 ml64 mr4"
name="signature"
<t t-else="">
<t
t-set="aggregated_lines"
t-value="move_lines._get_aggregated_product_quantities()"
/>
<t t-if="aggregated_lines">
<t
t-call="stock.stock_report_delivery_aggregated_move_lines"
/>
</t>
</t>
</t>
</t>
<!-- No destination packages -->
<t t-else="">
<!-- If printing lots/serial numbers => keep products in original lines -->
<t t-if="has_serial_number">
<tr
t-foreach="picking.move_line_ids"
t-as="move_line"
>
<t
t-call="stock.stock_report_delivery_has_serial_move_line"
/>
</tr>
</t>
<!-- If not printing lots/serial numbers => merge lines with same product -->
<t
t-else=""
name="aggregated_move_lines"
>
<t
t-set="aggregated_lines"
t-value="picking.move_line_ids._get_aggregated_product_quantities()"
/>
<t
t-call="stock.stock_report_delivery_aggregated_move_lines"
/>
</t>
</t>
</tbody>
</table>
<t
t-set="backorders"
t-value="picking.backorder_ids.filtered(lambda x: x.state not in ('done', 'cancel'))"
/>
<t t-if="picking.backorder_ids and backorders">
<p>
<span
>All items couldn't be shipped, the following items will be shipped as soon as they become available.</span>
</p>
<table
class="table table-sm"
name="stock_backorder_table"
>
<div class="offset-8">
<strong>Signature</strong>
</div>
<div class="offset-8">
<img
t-att-src="image_data_uri(picking.signature)"
style="max-height: 4cm; max-width: 8cm;"
/>
</div>
<div class="offset-8 text-center">
<p t-field="picking.partner_id.name" />
</div>
<thead>
<tr>
<th name="th_sb_product"><strong
>Product</strong></th>
<th
name="th_sb_quantity"
class="text-center"
><strong>Quantity</strong></th>
</tr>
</thead>
<tbody>
<t
t-foreach="backorders"
t-as="backorder"
>
<t
t-set="bo_lines"
t-value="backorder.move_ids.filtered(lambda x: x.product_uom_qty)"
/>
<tr
t-foreach="bo_lines"
t-as="bo_line"
>
<td>
<span
t-field="bo_line.product_id"
/>
<p
t-if="bo_line.description_picking != bo_line.product_id.name"
>
<span
t-field="bo_line.description_picking"
/>
</p>
</td>
<td class="text-center">
<span
t-field="bo_line.product_uom_qty"
/>
<span
t-field="bo_line.product_uom"
/>
</td>
</tr>
</t>
</tbody>
</table>
</t>
<div
t-if="picking.signature"
class="mt32 ml64 mr4"
name="signature"
>
<div class="offset-8">
<strong>Signature</strong>
</div>
<div class="offset-8">
<img
t-att-src="image_data_uri(picking.signature)"
style="max-height: 4cm; max-width: 8cm;"
/>
</div>
<div class="offset-8 text-center">
<p t-field="picking.partner_id.name" />
</div>
</div>
<p style="page-break-after: always;" />
</t>
</div>
</t>
</div>
<p style="page-break-after: always;" />
</t>
</div>
</t>
</t>
</template>
</data>
</t>
</t>
</template>
</odoo>