From 8a90216357bb4f27fc98d8d7538574371f438ef8 Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Thu, 9 Apr 2026 21:46:28 +0200 Subject: [PATCH] net gross --- modules/purchase_trade/invoice.py | 67 +++++++++++++++++++++ modules/purchase_trade/tests/test_module.py | 18 +++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/modules/purchase_trade/invoice.py b/modules/purchase_trade/invoice.py index c6fead5..7e3f505 100644 --- a/modules/purchase_trade/invoice.py +++ b/modules/purchase_trade/invoice.py @@ -146,6 +146,22 @@ class Invoice(metaclass=PoolMeta): return True return False + def _get_report_reused_lot_lines(self): + groups = {} + for line in self._get_report_invoice_lines(): + lots = self._get_report_preferred_lots(line) + if not lots: + continue + for lot in lots: + lot_id = getattr(lot, 'id', None) + key = lot_id if lot_id is not None else id(lot) + groups.setdefault(key, {'lot': lot, 'lines': []}) + groups[key]['lines'].append(line) + return { + key: value for key, value in groups.items() + if len(value['lines']) > 1 + } + @staticmethod def _convert_report_quantity(quantity, from_unit, to_unit): value = Decimal(str(quantity or 0)) @@ -170,6 +186,50 @@ class Invoice(metaclass=PoolMeta): cls._get_report_invoice_line_unit(line), ) + @classmethod + def _find_report_hist_entry_for_quantity(cls, lot, quantity, exclude_state_id=None): + target = Decimal(str(quantity or 0)).copy_abs() + for entry in list(getattr(lot, 'lot_hist', []) or []): + quantity_type = getattr(entry, 'quantity_type', None) + if getattr(quantity_type, 'id', None) == exclude_state_id: + continue + entry_quantity = Decimal(str(getattr(entry, 'quantity', 0) or 0)) + if entry_quantity == target: + return entry + return None + + def _get_report_reused_lot_gross_total(self): + reused_lots = self._get_report_reused_lot_lines() + if not reused_lots: + return None + total = Decimal(0) + for data in reused_lots.values(): + lot = data['lot'] + _, current_gross = self._get_report_lot_hist_weights(lot) + if current_gross is None: + continue + current_state = getattr(getattr(lot, 'lot_state', None), 'id', None) + negative_lines = [ + line for line in data['lines'] + if Decimal(str(getattr(line, 'quantity', 0) or 0)) < 0 + ] + previous_gross = Decimal(0) + matched = False + for line in negative_lines: + previous_entry = self._find_report_hist_entry_for_quantity( + lot, + self._get_report_invoice_line_quantity_from_line(line), + exclude_state_id=current_state, + ) + if not previous_entry: + continue + previous_gross += Decimal(str( + getattr(previous_entry, 'gross_quantity', 0) or 0)) + matched = True + if matched: + total += current_gross - previous_gross + return total + @staticmethod def _get_report_invoice_line_unit(line): lots = Invoice._get_report_preferred_lots(line) @@ -799,6 +859,13 @@ class Invoice(metaclass=PoolMeta): @property def report_gross(self): if self.lines: + reused_gross = self._get_report_reused_lot_gross_total() + if reused_gross is not None: + non_reused_total = sum( + self._get_report_invoice_line_weights(line)[1] + for line in self._get_report_invoice_lines() + if not self._report_invoice_line_reuses_lot(line)) + return non_reused_total + reused_gross return sum( self._get_report_invoice_line_weights(line)[1] for line in self._get_report_invoice_lines()) diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py index f6ec348..c999692 100644 --- a/modules/purchase_trade/tests/test_module.py +++ b/modules/purchase_trade/tests/test_module.py @@ -1189,11 +1189,26 @@ class PurchaseTradeTestCase(ModuleTestCase): mt = Mock(id=1, rec_name='MT') kg = Mock(id=2, rec_name='KILOGRAM') + current_type = Mock(id=100) + previous_type = Mock(id=200) shared_lot = Mock(id=10, lot_type='physic', lot_unit_line=kg) + shared_lot.lot_state = current_type shared_lot.get_hist_quantity.return_value = ( Decimal('999995'), - Decimal('999995'), + Decimal('999992'), ) + shared_lot.lot_hist = [ + Mock( + quantity_type=previous_type, + quantity=Decimal('999995'), + gross_quantity=Decimal('999998'), + ), + Mock( + quantity_type=current_type, + quantity=Decimal('999990'), + gross_quantity=Decimal('999992'), + ), + ] negative = Mock( type='line', @@ -1233,6 +1248,7 @@ class PurchaseTradeTestCase(ModuleTestCase): PoolMock.return_value.get.return_value = uom_model self.assertEqual(invoice.report_net, Decimal('-5.000')) + self.assertEqual(invoice.report_gross, Decimal('-6')) self.assertEqual( invoice.report_quantity_lines.splitlines(), [