Files
odoo_source/addons/stock/report/report_package_barcode.xml
T
Tiffany Chang (tic) b27a638bec [IMP] stock: print gs1 product qtys in package w/content report
When a product has a valid EAN barcode, we would like to include the qty
of the product + its uom in its GS1 datamatrix (dm). There are a lot of
hardcoded values in report in order to fit GS1 standard:

- GS1 UoM codes are ALWAYS 3 digits (to indicate which UoM) + 1 digit
  (to indicate number of digits) EXCEPT for Units UoM. Because we cannot
  update a dict in QWeb, we check for both whether it's a supported GS1
  UoM or a Unit separately rather than 1 general check.
- We hardcode the GS1 units value as '30' in this case (there are 2
  options) because the code is specific to qty of a product (not qty of
  product within a SSCC GS1 dm, there is a different value for that).

There are some limitations to this implementation due to
incompatibilities between Odoo + GS1, specifically:

- some Odoo UoMs are not included as GS1 UoMs => won't be included in dm
- # of Odoo quantity digits can exceed the number of digits allowed in
  GS1 (8 digits for Units, 6 digits for all other UoMs, including
  decimal values from rounding precision, e.g. 1 cm with rounding
  precision 0.000001 has 7 digits as 1.000000) => won't be included in
  dm in this case
- Odoo treats "fl oz" (Volume) and "oz" (Weight) as separate UoMs but
  GS1 does not => we only handle weight for now
- Odoo supports non-integer values for Unit UoM but GS1 does not => we
  round quantity to an integer value.

Note that implementation includes flexibility for users so they can
add/remove UoMs from the GS1 nomenclature rules as wanted/needed. This
includes user created custom codes (e.g. 90, 92-99 + extra character -
code isn't required to start with these numbers, but it won't be GS1
compliant) to handle UoMs not included in GS1 specifications as long as
they are correctly configured as quantities if user wants to be able to
rescan quantities back in with barcode app and match the 6 digits or
less quantitiy requirement when printing.

Part of Task: 2494740
Related ENT PR: odoo/enterprise#23323

Part-of: odoo/odoo#82389
2022-01-31 12:06:06 +00:00

141 lines
9.3 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="report_package_barcode">
<t t-call="web.basic_layout">
<!-- quantity patterns are always 3 digit codes + 1 digit for number of digits (excluding units) -->
<t t-set="uom_unit_id" t-value="env.ref('uom.product_uom_unit').id"/>
<t t-set="gs1_uom_patterns" t-value="{rule.associated_uom_id.id: rule.pattern[1:4] + str(len(str(rule.associated_uom_id.rounding).split('.')[1])) for rule in env['barcode.rule'].search([('associated_uom_id', '!=', False), ('associated_uom_id.id', '!=', uom_unit_id), ('is_gs1_nomenclature', '=', True)])}"/>
<t t-foreach="docs" t-as="o">
<t>
<div class="page">
<div class="oe_structure"/>
<table class="table table-condensed" style="border-bottom: 0px solid white !important;">
<tr>
<th>
<h1 t-field="o.name" class="mt0 float-left"/>
</th>
<th name="td_pk_barcode" style="text-align: center">
<t t-if="o.valid_sscc and env['ir.actions.report'].datamatrix_available()">
<div class="row text-left">
<t t-set="barcode" t-value="'00' + o.name"/>
<t t-if="o.pack_date" t-set="barcode" t-value="barcode + '13' + o.pack_date.strftime('%y%m%d')"/>
<div class="col-3" name="datamatrix_barcode" style="margin: 0px 20px 40px 60px" t-out="barcode" t-options="{'widget': 'barcode', 'symbology': 'DataMatrix', 'width': 100, 'height': 100}"/>
</div>
</t>
<t t-else="">
<div t-field="o.name" t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:300px;height:50px;'}"/>
<p t-field="o.name"/>
</t>
</th>
</tr>
</table>
<div class="row mt32 mb32">
<div t-if="o.pack_date" class="col-auto">
<strong>Pack Date:</strong>
<p t-field="o.pack_date"/>
</div>
<div t-if="o.package_type_id" class="o_packaging_type col-auto">
<strong>Package Type:</strong>
<p t-field="o.package_type_id.name"/>
</div>
</div>
<table class="table table-sm table-striped" style="border-bottom: 0px solid white !important;">
<t t-set="has_serial_number" t-value="o.quant_ids.mapped('lot_id')" />
<t t-set="has_ean_barcode" t-value="env['ir.actions.report'].datamatrix_available() and any(valid_ean for valid_ean in o.quant_ids.product_id.mapped('valid_ean'))" />
<thead>
<tr>
<th>Product</th>
<th name="th_quantity" class="text-right">Quantity</th>
<th name="th_uom" groups="uom.group_uom"/>
<th name="th_serial" class="text-right" t-if="has_serial_number">Lot/Serial Number</th>
<th name="th_barcode" class="text-right" t-if="has_ean_barcode"/>
</tr>
</thead>
<tbody>
<tr t-foreach="o.quant_ids" t-as="l">
<td>
<span t-field="l.product_id.name"/>
</td>
<td class="text-right">
<span t-field="l.quantity"/>
</td>
<td groups="uom.group_uom">
<span t-field="l.product_id.uom_id.name"/>
</td>
<td class="text-right" t-if="has_serial_number">
<t t-if="l.lot_id"><span t-field="l.lot_id.name"/></t>
</td>
<td class="text-right" t-if="has_ean_barcode">
<t t-if="l.product_id.valid_ean">
<t t-set="product_barcode" t-value="'01' + '0' * (14 - len(l.product_id.barcode)) + l.product_id.barcode"/>
<t t-set="gs1_pattern" t-value="gs1_uom_patterns.get(l.product_uom_id.id, False)"/>
<t t-if="(gs1_pattern or l.product_uom_id.id == uom_unit_id) and l.quantity &gt;= 0">
<t t-if="gs1_pattern">
<t t-set="qty_str" t-value="str(int(l.quantity/l.product_uom_id.rounding))"/>
<t t-if="len(qty_str) &lt;= 6" t-set="product_barcode" t-value="product_barcode + gs1_pattern + '0' * (6 - len(qty_str)) + qty_str"/>
</t>
<t t-else="">
<!-- GS1 have no decimal indicator for unit uom => round for now.. -->
<t t-set="qty_str" t-value="str(int(round(l.quantity)))"/>
<t t-if="len(qty_str) &lt;=8" t-set="product_barcode" t-value="product_barcode + '30' + '0' * (8 - len(qty_str)) + qty_str"/>
</t>
</t>
<!-- TODO: lot/sn ALWAYS has to be last part of barcode since left padding '0's = different sn/lot name match when scanning => fix when FNC1 can be used, -->
<t name="product_barcode_lot_datamatrix" t-if="l.product_id.tracking == 'lot' and l.lot_id" t-set="product_barcode" t-value="product_barcode + '10' + l.lot_id.name"/>
<t t-elif="l.product_id.tracking == 'serial' and l.lot_id" t-set="product_barcode" t-value="product_barcode + '21' + l.lot_id.name"/>
<span t-out="product_barcode" t-options="{'widget': 'barcode', 'symbology': 'DataMatrix', 'width': 45, 'height': 45}"/>
</t>
</td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</t>
</template>
<template id="report_package_barcode_small">
<t t-call="web.basic_layout">
<t t-foreach="docs" t-as="o">
<t>
<div class="page">
<div class="oe_structure"/>
<t t-if="o.valid_sscc and env['ir.actions.report'].datamatrix_available()">
<div class="row">
<t t-set="barcode" t-value="'00' + o.name"/>
<t t-if="o.pack_date" t-set="barcode" t-value="barcode + '13' + o.pack_date.strftime('%y%m%d')"/>
<div class="col-5 text-right" name="datamatrix_barcode" style="margin: 0px 20px 40px 60px" t-out="barcode" t-options="{'widget': 'barcode', 'symbology': 'DataMatrix', 'width': 200, 'height': 200}"/>
<div class="col-7 text-left" style="font-size:20px;">
<div class="row">SSCC: <span t-field="o.name"/></div>
<div t-if="o.pack_date" class="row">Pack Date: <span t-field="o.pack_date"/></div>
<div t-if="o.package_type_id" class="row" name="datamatrix_pack_type">Package Type: <span t-field="o.package_type_id.name"/></div>
</div>
</div>
</t>
<t t-else="">
<div class="row">
<div class="text-center col-12">
<div t-field="o.name" t-options="{'widget': 'barcode', 'width': 600, 'height': 100, 'img_style': 'width:600px;height:100px;'}"/>
<div t-field="o.name" style="font-size:20px;"/>
</div>
</div>
<div t-if="o.pack_date" class="col-12 text-center" style="font-size:24px; font-weight:bold;">Pack Date: <span t-field="o.pack_date"/></div>
<div class="row o_packaging_type" t-if="o.package_type_id">
<div class="col-12 text-center" style="font-size:24px; font-weight:bold;"><span>Package Type: </span><span t-field="o.package_type_id.name"/></div>
</div>
</t>
</div>
</t>
</t>
</t>
</template>
<template id="report_picking_packages">
<t t-set="docs" t-value="docs.move_line_ids.mapped('result_package_id')"/>
<t t-call="stock.report_package_barcode"/>
</template>
</data>
</odoo>