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
141 lines
9.3 KiB
XML
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 >= 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) <= 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) <=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>
|