[FIX] product,stock: print lot when provided instead of product

When Print Labels was revamped in odoo/odoo#69690 it was decided that
only product barcodes would be printed. Unfortunately we want SN/Lot
barcodes to be printed instead when they are filled in, so we make this
the case when applicable (i.e. when printing Transfer Quantites + qtys
done).

closes odoo/odoo#80079

Task: 2678336
X-original-commit: 105587e9e411442fd8e6d30efaa35b90b0e09959
Signed-off-by: Arnold Moyaux <arm@odoo.com>
This commit is contained in:
Tiffany Chang (tic)
2021-11-19 12:50:58 +00:00
parent 7bda571cd7
commit 0d882a4e49
5 changed files with 75 additions and 27 deletions
+18 -3
View File
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import _, models
from odoo.exceptions import UserError
@@ -8,11 +10,24 @@ def _prepare_data(env, data):
# change product ids by actual product object to get access to fields in xml template
# we needed to pass ids because reports only accepts native python types (int, float, strings, ...)
if data.get('active_model') == 'product.template':
quantity_by_product = {env['product.template'].with_context(display_default_code=False).browse(int(p)): q for p, q in data.get('quantity_by_product').items()}
Product = env['product.template'].with_context(display_default_code=False)
elif data.get('active_model') == 'product.product':
quantity_by_product = {env['product.product'].with_context(display_default_code=False).browse(int(p)): q for p, q in data.get('quantity_by_product').items()}
Product = env['product.product'].with_context(display_default_code=False)
else:
raise UserError(_('Product model not defined, Please contact your administrator.'))
total = 0
quantity_by_product = defaultdict(list)
for p, q in data.get('quantity_by_product').items():
product = Product.browse(int(p))
quantity_by_product[product].append((product.barcode, q))
total += q
if data.get('custom_barcodes'):
# we expect custom barcodes format as: {product: [(barcode, qty_of_barcode)]}
for product, barcodes_qtys in data.get('custom_barcodes').items():
quantity_by_product[Product.browse(int(product))] += (barcodes_qtys)
total += sum(qty for _, qty in barcodes_qtys)
layout_wizard = env['product.label.layout'].browse(data.get('layout_wizard'))
if not layout_wizard:
return {}
@@ -21,7 +36,7 @@ def _prepare_data(env, data):
'quantity': quantity_by_product,
'rows': layout_wizard.rows,
'columns': layout_wizard.columns,
'page_numbers': (sum(quantity_by_product.values()) - 1) // (layout_wizard.rows * layout_wizard.columns) + 1,
'page_numbers': (total - 1) // (layout_wizard.rows * layout_wizard.columns) + 1,
'price_included': data.get('price_included'),
'extra_html': layout_wizard.extra_html,
}
@@ -12,9 +12,9 @@
<div class="o_label_data">
<div class="text-center o_label_left_column">
<span class="text-nowrap" t-field="product.default_code"/>
<t t-if="product.barcode">
<div t-field="product.barcode" t-options="{'widget': 'barcode', 'symbology': 'auto', 'img_style': barcode_size}"/>
<span class="text-center" t-field="product.barcode"/>
<t t-if="barcode">
<div t-out="barcode" t-options="{'widget': 'barcode', 'symbology': 'auto', 'img_style': barcode_size}"/>
<span class="text-center" t-out="barcode"/>
</t>
</div>
<div class="text-right" style="line-height:normal">
@@ -52,9 +52,9 @@
</div>
<div class= "text-center o_label_small_barcode">
<span class="text-nowrap" t-field="product.default_code"/>
<t t-if="product.barcode">
<div t-field="product.barcode" style="padding:0" t-options="{'widget': 'barcode', 'symbology': 'auto', 'img_style': barcode_size}"/>
<span class="text-center" t-field="product.barcode"/>
<t t-if="barcode">
<div t-out="barcode" style="padding:0" t-options="{'widget': 'barcode', 'symbology': 'auto', 'img_style': barcode_size}"/>
<span class="text-center" t-out="barcode"/>
</t>
</div>
</div>
@@ -88,9 +88,9 @@
</div>
</t>
<div class= "text-center o_label_small_barcode">
<t t-if="product.barcode">
<div t-field="product.barcode" style="padding:0" t-options="{'widget': 'barcode', 'symbology': 'auto', 'img_style': barcode_size}"/>
<span class="text-center" t-field="product.barcode"/>
<t t-if="barcode">
<div t-out="barcode" style="padding:0" t-options="{'widget': 'barcode', 'symbology': 'auto', 'img_style': barcode_size}"/>
<span class="text-center" t-out="barcode"/>
</t>
</div>
</div>
@@ -101,9 +101,12 @@
<div class="o_label_sheet o_label_dymo" t-att-style="padding_page">
<div class="o_label_full" t-att-style="table_style">
<div class= "text-left o_label_small_barcode">
<t t-if="product.barcode">
<t t-if="barcode">
<!-- `quiet=0` to remove the left and right margins on the barcode -->
<div t-field="product.barcode" style="padding:0" t-options="{'widget': 'barcode', 'quiet': 0, 'symbology': 'auto', 'img_style': barcode_size}"/>
<div t-out="barcode" style="padding:0" t-options="{'widget': 'barcode', 'quiet': 0, 'symbology': 'auto', 'img_style': barcode_size}"/>
<div class="o_label_name" style="line-height: 130%;height:2.7em;background-color: transparent;">
<span t-out="barcode"/>
</div>
</t>
</div>
<div class="o_label_name" style="line-height: 130%;height:2.7em;background-color: transparent;">
@@ -151,12 +154,19 @@
<t t-if="not current_quantity and quantity">
<t t-set="current_data" t-value="quantity.popitem()"/>
<t t-set="product" t-value="current_data[0]"/>
<t t-set="current_quantity" t-value="current_data[1]"/>
<t t-set="barcode_and_qty" t-value="current_data[1].pop()"/>
<t t-set="barcode" t-value="barcode_and_qty[0]"/>
<t t-set="current_quantity" t-value="barcode_and_qty[1]"/>
</t>
<t t-if="current_quantity">
<t t-set="make_invisible" t-value="False"/>
<t t-set="current_quantity" t-value="current_quantity - 1"/>
</t>
<t t-elif="current_data and current_data[1]">
<t t-set="barcode_and_qty" t-value="current_data[1].pop()"/>
<t t-set="barcode" t-value="barcode_and_qty[0]"/>
<t t-set="current_quantity" t-value="barcode_and_qty[1] - 1"/>
</t>
<t t-else="">
<t t-set="make_invisible" t-value="True"/>
</t>
@@ -177,10 +187,13 @@
<t t-set="barcode_size" t-value="'width:45.5mm;height:7.5mm'"/>
<t t-set="table_style" t-value="'width:100%;height:32mm;'"/>
<t t-set="padding_page" t-value="'padding: 2mm'"/>
<t t-foreach="quantity.items()" t-as="qty_by_product">
<t t-set="product" t-value="qty_by_product[0]"/>
<t t-foreach="range(qty_by_product[1])" t-as="qty">
<t t-call="product.report_simple_label_dymo"/>
<t t-foreach="quantity.items()" t-as="barcode_and_qty_by_product">
<t t-set="product" t-value="barcode_and_qty_by_product[0]"/>
<t t-foreach="barcode_and_qty_by_product[1]" t-as="barcode_and_qty">
<t t-set="barcode" t-value="barcode_and_qty[0]"/>
<t t-foreach="range(barcode_and_qty[1])" t-as="qty">
<t t-call="product.report_simple_label_dymo"/>
</t>
</t>
</t>
</t>
+14 -2
View File
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import _, models
from odoo.exceptions import UserError
@@ -11,10 +13,20 @@ class ReportProductLabel(models.AbstractModel):
def _get_report_values(self, docids, data):
if data.get('active_model') == 'product.template':
data['quantity'] = {self.env['product.template'].browse(int(p)): q for p, q in data.get('quantity_by_product').items()}
Product = self.env['product.template']
elif data.get('active_model') == 'product.product':
data['quantity'] = {self.env['product.product'].browse(int(p)): q for p, q in data.get('quantity_by_product').items()}
Product = self.env['product.product']
else:
raise UserError(_('Product model not defined, Please contact your administrator.'))
quantity_by_product = defaultdict(list)
for p, q in data.get('quantity_by_product').items():
product = Product.browse(int(p))
quantity_by_product[product].append((product.barcode, q))
if data.get('custom_barcodes'):
# we expect custom barcodes to be: {product: [(barcode, qty_of_barcode)]}
for product, barcodes_qtys in data.get('custom_barcodes').items():
quantity_by_product[Product.browse(int(product))] += (barcodes_qtys)
data['quantity'] = quantity_by_product
return data
+9 -6
View File
@@ -2,10 +2,12 @@
<odoo>
<data>
<template id="label_product_product_view">
<t t-foreach="quantity.items()" t-as="qty_by_product">
<t t-set="product" t-value="qty_by_product[0]"/>
<t t-foreach="range(qty_by_product[1])" t-as="qty">
<t t-translation="off">
<t t-foreach="quantity.items()" t-as="barcode_and_qty_by_product">
<t t-set="product" t-value="barcode_and_qty_by_product[0]"/>
<t t-foreach="barcode_and_qty_by_product[1]" t-as="barcode_and_qty">
<t t-set="barcode" t-value="barcode_and_qty[0]"/>
<t t-foreach="range(barcode_and_qty[1])" t-as="qty">
<t t-translation="off">
^XA
^FT100,80^A0N,40,30^FD<t t-esc="product.display_name"/>^FS
<t t-if="product.default_code and len(product.default_code) &gt; 15">
@@ -25,14 +27,15 @@
^A0N,66,48^FH^FD<t t-esc="product.currency_id.symbol"/><t t-esc="product.list_price" t-options='{"widget": "float", "precision": 2}'/>^FS
</t>
</t>
<t t-if="product.barcode">
<t t-if="barcode">
^FO100,160^BY3
^BCN,100,Y,N,N
^FD<t t-esc="product.barcode"/>^FS
^FD<t t-esc="barcode"/>^FS
</t>
^XZ
</t>
</t>
</t>
</t>
</template>
<template id="label_lot_template_view">
@@ -25,10 +25,15 @@ class ProductLabelLayout(models.TransientModel):
if self.picking_quantity == 'picking' and self.move_line_ids:
qties = defaultdict(int)
custom_barcodes = defaultdict(list)
uom_unit = self.env.ref('uom.product_uom_categ_unit', raise_if_not_found=False)
for line in self.move_line_ids:
if line.product_uom_id.category_id == uom_unit:
if (line.lot_id or line.lot_name) and int(line.qty_done):
custom_barcodes[line.product_id.id].append((line.lot_id.name or line.lot_name, int(line.qty_done)))
continue
qties[line.product_id.id] += line.qty_done
# Pass only products with some quantity done to the report
data['quantity_by_product'] = {p: int(q) for p, q in qties.items() if q}
data['custom_barcodes'] = custom_barcodes
return xml_id, data