[FIX] sale_stock: display lots on invoices

When printing an invoice with tracked products, if there are several
invoices linked to the sale order, the displayed lots may be incorrect

To reproduce the issue:
(Need sale_management)
1. In Settings, enable "Display Lots & Serial Numbers on Invoices"
2. Create two products P01, P02:
    - Storable
    - Tracked by lot
3. Update their quantity (>1)
4. Create and confirm a SO with 1 x P01 and 1 x P02
5. Deliver the products one by one
6. From the SO, create an invoice INV01
7. On INV01, remove the line associated to P02
8. Post INV01
9. From the SO, create and post the second invoice INV02
10. Print one of the invoices

Error: [1] When printing INV01 (which only contains P01), the lot of P02
is also displayed. [2] When printing INV02 (which only contains P02),
the lot of PO02 is not displayed

When rendering an invoice, `_get_invoiced_lot_values` is called and
provides all data about invoiced lots. However, there are two issues:
- [1] it considers all related SML, even the ones related to a product
not present in the invoice
- [2] when computing the date of the last invoice, it uses the same date
for all products in the invoice although it could be different depending
on the product.

OPW-2637107

closes odoo/odoo#83047

X-original-commit: 9ffd4a9c5755af241a7acc98bc97c790dbb1b84d
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
Adrien Widart
2022-01-20 09:10:57 +00:00
parent e1535583db
commit acbeb9b3c6
+18 -11
View File
@@ -31,8 +31,9 @@ class AccountMove(models.Model):
if self.state == 'draft':
return res
sale_orders = self.mapped('invoice_line_ids.sale_line_ids.order_id')
stock_move_lines = sale_orders.mapped('picking_ids.move_ids.move_line_ids')
sale_lines = self.invoice_line_ids.sale_line_ids
sale_orders = sale_lines.order_id
stock_move_lines = sale_lines.move_ids.filtered(lambda r: r.state == 'done').move_line_ids
# Get the other customer invoices and refunds.
ordered_invoice_ids = sale_orders.mapped('invoice_ids')\
@@ -48,27 +49,33 @@ class AccountMove(models.Model):
break
i += 1
# Get the previous invoice if any.
# Get the previous invoices if any.
previous_invoices = ordered_invoice_ids[:self_index]
last_invoice = previous_invoices[-1] if len(previous_invoices) else None
# Get the incoming and outgoing sml between self.invoice_date and the previous invoice (if any).
# Get the incoming and outgoing sml between self.invoice_date and the previous invoice (if any) of the related product.
write_dates = [wd for wd in self.invoice_line_ids.mapped('write_date') if wd]
self_datetime = max(write_dates) if write_dates else None
last_write_dates = last_invoice and [wd for wd in last_invoice.invoice_line_ids.mapped('write_date') if wd]
last_invoice_datetime = max(last_write_dates) if last_write_dates else None
last_invoice_datetime = dict()
for product in self.invoice_line_ids.product_id:
last_invoice = previous_invoices.filtered(lambda inv: product in inv.invoice_line_ids.product_id)
last_invoice = last_invoice[-1] if last_invoice else None
last_write_dates = last_invoice and [wd for wd in last_invoice.invoice_line_ids.mapped('write_date') if wd]
last_invoice_datetime[product] = max(last_write_dates) if last_write_dates else None
def _filter_incoming_sml(ml):
if ml.state == 'done' and ml.location_id.usage == 'customer' and ml.lot_id:
if last_invoice_datetime:
return last_invoice_datetime <= ml.date <= self_datetime
last_date = last_invoice_datetime.get(ml.product_id)
if last_date:
return last_date <= ml.date <= self_datetime
else:
return ml.date <= self_datetime
return False
def _filter_outgoing_sml(ml):
if ml.state == 'done' and ml.location_dest_id.usage == 'customer' and ml.lot_id:
if last_invoice_datetime:
return last_invoice_datetime <= ml.date <= self_datetime
last_date = last_invoice_datetime.get(ml.product_id)
if last_date:
return last_date <= ml.date <= self_datetime
else:
return ml.date <= self_datetime
return False