[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:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) > 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
|
||||
|
||||
Reference in New Issue
Block a user