Invoice Melya template
This commit is contained in:
@@ -177,7 +177,7 @@ class Invoice(metaclass=PoolMeta):
|
||||
Snapshot = Pool().get('account.invoice.line.lot.weight')
|
||||
Lot = Pool().get('lot.lot')
|
||||
for line in self._get_report_invoice_lines():
|
||||
if (getattr(line, 'description', None) != 'Pro forma'
|
||||
if (getattr(line, 'description', None) not in {'Pro forma', 'Final'}
|
||||
or Decimal(str(getattr(line, 'quantity', 0) or 0)) <= 0
|
||||
or not getattr(line, 'lot', None)):
|
||||
continue
|
||||
@@ -961,6 +961,49 @@ class Invoice(metaclass=PoolMeta):
|
||||
def report_purchase_commission_total_display(self):
|
||||
return self._get_report_commission_total_display_for('purchase')
|
||||
|
||||
@staticmethod
|
||||
def _same_report_invoice_line(left, right):
|
||||
if not left or not right:
|
||||
return False
|
||||
left_id = getattr(left, 'id', None)
|
||||
right_id = getattr(right, 'id', None)
|
||||
if left_id is not None and right_id is not None:
|
||||
return left_id == right_id
|
||||
return left is right
|
||||
|
||||
def _get_report_invoice_line_lots(self, invoice_line):
|
||||
lots = []
|
||||
seen = set()
|
||||
|
||||
def add_lot(lot):
|
||||
if not lot or getattr(lot, 'lot_type', None) == 'virtual':
|
||||
return
|
||||
lot_id = getattr(lot, 'id', None)
|
||||
key = lot_id if lot_id is not None else id(lot)
|
||||
if key in seen:
|
||||
return
|
||||
seen.add(key)
|
||||
lots.append(lot)
|
||||
|
||||
add_lot(getattr(invoice_line, 'lot', None))
|
||||
trade = self._get_report_trade()
|
||||
trade_lines = getattr(trade, 'lines', []) if trade else []
|
||||
for line in trade_lines or []:
|
||||
for lot in getattr(line, 'lots', []) or []:
|
||||
if getattr(lot, 'lot_type', None) != 'physic':
|
||||
continue
|
||||
refs = [
|
||||
getattr(lot, 'sale_invoice_line', None),
|
||||
getattr(lot, 'sale_invoice_line_prov', None),
|
||||
getattr(lot, 'invoice_line', None),
|
||||
getattr(lot, 'invoice_line_prov', None),
|
||||
]
|
||||
if any(
|
||||
self._same_report_invoice_line(ref, invoice_line)
|
||||
for ref in refs):
|
||||
add_lot(lot)
|
||||
return lots
|
||||
|
||||
@staticmethod
|
||||
def _get_report_related_lots(line):
|
||||
lots = []
|
||||
@@ -1044,7 +1087,7 @@ class Invoice(metaclass=PoolMeta):
|
||||
net_total += padding
|
||||
gross_total += padding
|
||||
return net_total * sign, gross_total * sign
|
||||
lots = self._get_report_preferred_lots(line)
|
||||
lots = self._get_report_invoice_line_lots(line)
|
||||
if lots and self._report_invoice_line_reuses_lot(line):
|
||||
quantity = self._get_report_invoice_line_quantity_from_line(line)
|
||||
return quantity, quantity
|
||||
@@ -1093,7 +1136,11 @@ class Invoice(metaclass=PoolMeta):
|
||||
@staticmethod
|
||||
def _get_report_line_lot_keys(line):
|
||||
keys = []
|
||||
for lot in Invoice._get_report_preferred_lots(line):
|
||||
invoice = getattr(line, 'invoice', None)
|
||||
lots = (
|
||||
invoice._get_report_invoice_line_lots(line)
|
||||
if invoice else Invoice._get_report_preferred_lots(line))
|
||||
for lot in lots:
|
||||
lot_id = getattr(lot, 'id', None)
|
||||
keys.append(lot_id if lot_id is not None else id(lot))
|
||||
return tuple(sorted(keys))
|
||||
@@ -1112,7 +1159,7 @@ class Invoice(metaclass=PoolMeta):
|
||||
def _get_report_reused_lot_lines(self):
|
||||
groups = {}
|
||||
for line in self._get_report_invoice_lines():
|
||||
lots = self._get_report_preferred_lots(line)
|
||||
lots = self._get_report_invoice_line_lots(line)
|
||||
if not lots:
|
||||
continue
|
||||
for lot in lots:
|
||||
@@ -1237,7 +1284,10 @@ class Invoice(metaclass=PoolMeta):
|
||||
snapshots = Invoice._get_report_invoice_line_weight_snapshots(line)
|
||||
if snapshots and getattr(snapshots[0], 'unit', None):
|
||||
return snapshots[0].unit
|
||||
lots = Invoice._get_report_preferred_lots(line)
|
||||
invoice = getattr(line, 'invoice', None)
|
||||
lots = (
|
||||
invoice._get_report_invoice_line_lots(line)
|
||||
if invoice else Invoice._get_report_preferred_lots(line))
|
||||
if lots and getattr(lots[0], 'lot_unit_line', None):
|
||||
return lots[0].lot_unit_line
|
||||
return getattr(line, 'unit', None)
|
||||
@@ -1290,13 +1340,13 @@ class Invoice(metaclass=PoolMeta):
|
||||
return text.upper() if text else ''
|
||||
|
||||
def _get_report_purchase(self):
|
||||
purchases = list(self.purchases or [])
|
||||
purchases = list(getattr(self, 'purchases', None) or [])
|
||||
return purchases[0] if purchases else None
|
||||
|
||||
def _get_report_sale(self):
|
||||
# Bridge invoice templates to the originating sale so FODT files can
|
||||
# reuse stable sale.report_* properties instead of complex expressions.
|
||||
sales = list(self.sales or [])
|
||||
sales = list(getattr(self, 'sales', None) or [])
|
||||
return sales[0] if sales else None
|
||||
|
||||
def _get_report_trade(self):
|
||||
@@ -1347,7 +1397,7 @@ class Invoice(metaclass=PoolMeta):
|
||||
shipments = []
|
||||
seen = set()
|
||||
for line in self._get_report_invoice_lines():
|
||||
for lot in self._get_report_preferred_lots(line):
|
||||
for lot in self._get_report_invoice_line_lots(line):
|
||||
shipment = self._get_report_lot_shipment(lot)
|
||||
if not shipment:
|
||||
continue
|
||||
@@ -1367,15 +1417,6 @@ class Invoice(metaclass=PoolMeta):
|
||||
if not invoice_lines:
|
||||
return []
|
||||
|
||||
def _same_invoice_line(left, right):
|
||||
if not left or not right:
|
||||
return False
|
||||
left_id = getattr(left, 'id', None)
|
||||
right_id = getattr(right, 'id', None)
|
||||
if left_id is not None and right_id is not None:
|
||||
return left_id == right_id
|
||||
return left is right
|
||||
|
||||
trade = self._get_report_trade()
|
||||
trade_lines = getattr(trade, 'lines', []) if trade else []
|
||||
lots = []
|
||||
@@ -1390,7 +1431,7 @@ class Invoice(metaclass=PoolMeta):
|
||||
getattr(lot, 'invoice_line_prov', None),
|
||||
]
|
||||
if any(
|
||||
_same_invoice_line(ref, invoice_line)
|
||||
self._same_report_invoice_line(ref, invoice_line)
|
||||
for ref in refs for invoice_line in invoice_lines):
|
||||
lots.append(lot)
|
||||
return lots
|
||||
|
||||
@@ -5,7 +5,7 @@ import datetime
|
||||
from contextlib import nullcontext
|
||||
from decimal import Decimal
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import ANY, Mock, patch
|
||||
from unittest.mock import ANY, Mock, call, patch
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from trytond.pool import Pool
|
||||
@@ -8257,8 +8257,8 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
invoice.report_quantity_lines,
|
||||
'910.00 LBS (910.00 LBS)')
|
||||
|
||||
def test_invoice_validation_creates_weight_snapshot_for_proforma_lines(self):
|
||||
'invoice validation snapshots lot weights for pro forma invoice lines'
|
||||
def test_invoice_validation_creates_weight_snapshot_for_report_lines(self):
|
||||
'invoice validation snapshots lot weights for pro forma and final lines'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
lot_ref = Mock(id=10)
|
||||
@@ -8293,8 +8293,9 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
|
||||
invoice._create_lot_weight_snapshots()
|
||||
|
||||
snapshot_model.create_for_invoice_line.assert_called_once_with(
|
||||
proforma, lot)
|
||||
self.assertEqual(
|
||||
snapshot_model.create_for_invoice_line.call_args_list,
|
||||
[call(proforma, lot), call(final, lot)])
|
||||
|
||||
def test_invoice_report_lbs_converts_kilogram_to_lbs(self):
|
||||
'invoice lbs helper converts kilogram quantities with the proper uom ratio'
|
||||
@@ -8429,8 +8430,8 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
'999990.0 KILOGRAM (2204597.95 LBS)',
|
||||
])
|
||||
|
||||
def test_invoice_report_weights_use_single_virtual_lot_when_no_physical(self):
|
||||
'invoice uses the unique virtual lot hist when no physical lot exists'
|
||||
def test_invoice_report_weights_ignore_origin_virtual_lot(self):
|
||||
'invoice report falls back to invoice quantity instead of origin virtual lot'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
virtual = Mock(id=1, lot_type='virtual', lot_unit_line=Mock(rec_name='LBS'))
|
||||
@@ -8448,13 +8449,14 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
)
|
||||
invoice = Invoice()
|
||||
invoice.lines = [line]
|
||||
line.invoice = invoice
|
||||
|
||||
self.assertEqual(invoice.report_net, Decimal('930'))
|
||||
self.assertEqual(invoice.report_gross, Decimal('930'))
|
||||
self.assertEqual(invoice.report_weight_unit_upper, 'LBS')
|
||||
self.assertEqual(invoice.report_net, Decimal('1000'))
|
||||
self.assertEqual(invoice.report_gross, Decimal('1000'))
|
||||
self.assertEqual(invoice.report_weight_unit_upper, 'MT')
|
||||
|
||||
def test_invoice_report_weights_prefer_physical_lots_over_virtual(self):
|
||||
'invoice uses physical lot hist values whenever physical lots exist'
|
||||
def test_invoice_report_weights_use_lot_linked_to_invoice_line(self):
|
||||
'invoice uses the physical lot explicitly linked to the invoice line'
|
||||
Invoice = Pool().get('account.invoice')
|
||||
|
||||
virtual = Mock(id=1, lot_type='virtual', lot_unit_line=Mock(rec_name='LBS'))
|
||||
@@ -8467,16 +8469,23 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
Decimal('950'),
|
||||
Decimal('980'),
|
||||
)
|
||||
origin = Mock(lots=[virtual, physical])
|
||||
physical.sale_invoice_line = None
|
||||
line = Mock(
|
||||
id=100,
|
||||
type='line',
|
||||
quantity=Decimal('1000'),
|
||||
lot=virtual,
|
||||
origin=origin,
|
||||
unit=Mock(rec_name='MT'),
|
||||
)
|
||||
physical.sale_invoice_line_prov = line
|
||||
physical.invoice_line = None
|
||||
physical.invoice_line_prov = None
|
||||
origin = Mock(lots=[virtual, physical])
|
||||
line.origin = origin
|
||||
invoice = Invoice()
|
||||
invoice.lines = [line]
|
||||
invoice.sales = [Mock(lines=[origin])]
|
||||
line.invoice = invoice
|
||||
|
||||
self.assertEqual(invoice.report_net, Decimal('950'))
|
||||
self.assertEqual(invoice.report_gross, Decimal('980'))
|
||||
|
||||
Reference in New Issue
Block a user