diff --git a/modules/purchase_trade/stock.py b/modules/purchase_trade/stock.py index 35bcda9..ebf1891 100755 --- a/modules/purchase_trade/stock.py +++ b/modules/purchase_trade/stock.py @@ -1758,6 +1758,58 @@ class ShipmentIn(metaclass=PoolMeta): price = Decimal(str(getattr(line, 'unit_price', 0) or 0)) return quantity * price + @classmethod + def _report_lot_quantity_for_line(cls, lot, line): + get_quantity = getattr(lot, 'get_current_quantity_converted', None) + unit = getattr(line, 'unit', None) + if callable(get_quantity): + try: + return Decimal(str(get_quantity(unit=unit) or 0)) + except TypeError: + try: + return Decimal(str(get_quantity() or 0)) + except TypeError: + pass + + quantity = Decimal(str(getattr(lot, 'lot_quantity', 0) or 0)) + from_unit = getattr(lot, 'lot_unit_line', None) + if quantity and from_unit and unit and from_unit != unit: + try: + Uom = Pool().get('product.uom') + quantity = Decimal(str( + Uom.compute_qty(from_unit, float(quantity), unit) or 0)) + except KeyError: + pass + return quantity + + def _get_report_linkage_lot_quantities_by_line(self, lines, get_line): + line_keys = {self._report_record_key(line) for line in lines} + quantities = {} + for lot in self._get_report_linkage_lots(): + if getattr(lot, 'lot_type', None) != 'physic': + continue + line = get_line(lot) + if not line: + continue + key = self._report_record_key(line) + if key not in line_keys: + continue + quantities[key] = ( + quantities.get(key, Decimal('0')) + + self._report_lot_quantity_for_line(lot, line)) + return quantities + + def _report_linkage_line_quantity(self, line, lot_quantities): + key = self._report_record_key(line) + if key in lot_quantities: + return lot_quantities[key] + return self._report_line_quantity(line) + + def _report_linkage_line_amount(self, line, lot_quantities): + quantity = self._report_linkage_line_quantity(line, lot_quantities) + price = Decimal(str(getattr(line, 'unit_price', 0) or 0)) + return quantity * price + def _get_report_linkage_product(self): lines = ( self._get_report_linkage_purchase_lines() @@ -1924,9 +1976,16 @@ class ShipmentIn(metaclass=PoolMeta): def _get_report_linkage_summary_rows(self): purchase_lines = self._get_report_linkage_purchase_lines() sale_lines = self._get_report_linkage_sale_lines() + purchase_quantities = self._get_report_linkage_lot_quantities_by_line( + purchase_lines, self._report_linkage_lot_purchase_line) + sale_quantities = self._get_report_linkage_lot_quantities_by_line( + sale_lines, self._report_linkage_lot_sale_line) purchase_total = -sum( - self._report_line_amount(line) for line in purchase_lines) - sale_total = sum(self._report_line_amount(line) for line in sale_lines) + self._report_linkage_line_amount(line, purchase_quantities) + for line in purchase_lines) + sale_total = sum( + self._report_linkage_line_amount(line, sale_quantities) + for line in sale_lines) rows = [] if purchase_lines: @@ -1971,22 +2030,30 @@ class ShipmentIn(metaclass=PoolMeta): rows = [] purchase_lines = self._get_report_linkage_purchase_lines() sale_lines = self._get_report_linkage_sale_lines() + purchase_quantities = self._get_report_linkage_lot_quantities_by_line( + purchase_lines, self._report_linkage_lot_purchase_line) + sale_quantities = self._get_report_linkage_lot_quantities_by_line( + sale_lines, self._report_linkage_lot_sale_line) if purchase_lines: rows.append(('Purchase', '', '', '', '', '', '', '', '', '')) for line in purchase_lines: purchase = getattr(line, 'purchase', None) rows.append(self._get_report_linkage_movement_row( - 'Deal', purchase, line)) + 'Deal', purchase, line, purchase_quantities)) if sale_lines: rows.append(('Sale', '', '', '', '', '', '', '', '', '')) for line in sale_lines: sale = getattr(line, 'sale', None) rows.append(self._get_report_linkage_movement_row( - 'Deal', sale, line)) + 'Deal', sale, line, sale_quantities)) if purchase_lines or sale_lines: balance = ( - sum(self._report_line_quantity(line) for line in purchase_lines) - - sum(self._report_line_quantity(line) for line in sale_lines)) + sum(self._report_linkage_line_quantity( + line, purchase_quantities) + for line in purchase_lines) + - sum(self._report_linkage_line_quantity( + line, sale_quantities) + for line in sale_lines)) unit = self._report_unit_symbol( purchase_lines[0] if purchase_lines else sale_lines[0]) rows.append(( @@ -1997,10 +2064,13 @@ class ShipmentIn(metaclass=PoolMeta): '', '', '', '', '')) return rows - def _get_report_linkage_movement_row(self, kind, trade, line): + def _get_report_linkage_movement_row( + self, kind, trade, line, lot_quantities=None): + lot_quantities = lot_quantities or {} product = getattr(line, 'product', None) quantity = ' '.join(part for part in [ - self._format_report_quantity(self._report_line_quantity(line)), + self._format_report_quantity( + self._report_linkage_line_quantity(line, lot_quantities)), (self._report_unit_symbol(line) or '').upper()] if part) incoterm = getattr(trade, 'incoterm', None) period = getattr(line, 'period_at', None) or '' @@ -2046,15 +2116,24 @@ class ShipmentIn(metaclass=PoolMeta): def _get_report_linkage_detail_rows(self): rows = [] - for line in self._get_report_linkage_sale_lines(): + sale_lines = self._get_report_linkage_sale_lines() + purchase_lines = self._get_report_linkage_purchase_lines() + sale_quantities = self._get_report_linkage_lot_quantities_by_line( + sale_lines, self._report_linkage_lot_sale_line) + purchase_quantities = self._get_report_linkage_lot_quantities_by_line( + purchase_lines, self._report_linkage_lot_purchase_line) + for line in sale_lines: sale = getattr(line, 'sale', None) rows.append(self._get_report_linkage_detail_row( - 'A', 'Deal revenue', sale, line, self._report_line_amount(line))) - for line in self._get_report_linkage_purchase_lines(): + 'A', 'Deal revenue', sale, line, + self._report_linkage_line_amount(line, sale_quantities), + sale_quantities)) + for line in purchase_lines: purchase = getattr(line, 'purchase', None) rows.append(self._get_report_linkage_detail_row( 'A', 'Deal expense', purchase, line, - -self._report_line_amount(line))) + -self._report_linkage_line_amount(line, purchase_quantities), + purchase_quantities)) for pair in self._get_report_linkage_fee_pairs(): rows.append(self._get_report_linkage_fee_detail_row(pair)) estimated_total = sum((row[6] for row in rows), Decimal('0')) @@ -2068,11 +2147,14 @@ class ShipmentIn(metaclass=PoolMeta): validated_total, 'pnl')) return rows - def _get_report_linkage_detail_row(self, group, label, trade, line, amount): + def _get_report_linkage_detail_row( + self, group, label, trade, line, amount, lot_quantities=None): + lot_quantities = lot_quantities or {} currency = self._report_currency_code(trade) unit = self._report_unit_symbol(line).upper() unit_price = self._report_price(getattr(line, 'unit_price', 0)) - quantity = self._format_report_quantity(self._report_line_quantity(line)) + quantity = self._format_report_quantity( + self._report_linkage_line_quantity(line, lot_quantities)) return ( group, label, diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py index e7ae0e4..9a5ee16 100644 --- a/modules/purchase_trade/tests/test_module.py +++ b/modules/purchase_trade/tests/test_module.py @@ -7531,9 +7531,21 @@ description lot = SimpleNamespace( id=101, lot_type='physic', + lot_quantity=Decimal('40'), + lot_unit_line=unit, line=purchase_line, sale_line=sale_line) - shipment.incoming_moves = [SimpleNamespace(lot=lot)] + other_lot = SimpleNamespace( + id=102, + lot_type='physic', + lot_quantity=Decimal('20'), + lot_unit_line=unit, + line=purchase_line, + sale_line=sale_line) + shipment.incoming_moves = [ + SimpleNamespace(lot=lot), + SimpleNamespace(lot=other_lot), + ] shipment.vessel = SimpleNamespace(vessel_name='MV ATLANTIC') self.assertEqual( @@ -7545,8 +7557,8 @@ description shipment.report_linkage_bl_date, 'Thursday, April 30, 2026') self.assertIn('Purchases', shipment.report_linkage_summary_groups) self.assertIn('Sales', shipment.report_linkage_summary_groups) - self.assertIn('-8,000.000', shipment.report_linkage_summary_estimated) - self.assertIn('10,000.000', shipment.report_linkage_summary_estimated) + self.assertIn('-4,800.000', shipment.report_linkage_summary_estimated) + self.assertIn('6,000.000', shipment.report_linkage_summary_estimated) summary_rows = shipment._get_report_linkage_summary_rows() freight_rows = [ row for row in summary_rows @@ -7555,7 +7567,11 @@ description self.assertEqual(freight_rows[0][2], Decimal('-25')) self.assertIn('P-10', shipment.report_linkage_movement_references) self.assertIn('S-20', shipment.report_linkage_movement_references) - self.assertIn('100 MT', shipment.report_linkage_movement_quantities) + self.assertIn('60 MT', shipment.report_linkage_movement_quantities) + detail_rows = shipment._get_report_linkage_detail_rows() + deal_rows = [row for row in detail_rows if row[8] == 'line'] + self.assertTrue(deal_rows) + self.assertTrue(all(row[5] == '60' for row in deal_rows)) self.assertIn('80.000 USD/MT', shipment.report_linkage_pricing_input_prices) self.assertIn('100.000 USD/MT', shipment.report_linkage_pricing_input_prices) diff --git a/modules/purchase_trade/view/purchase_line_form.xml b/modules/purchase_trade/view/purchase_line_form.xml index a2e0571..7e5dc8d 100755 --- a/modules/purchase_trade/view/purchase_line_form.xml +++ b/modules/purchase_trade/view/purchase_line_form.xml @@ -140,7 +140,7 @@ this repository contains the full copyright notices and license terms. --> colspan="12" col="1" panel="card" icon="tryton-list" xalign="0" yalign="0" col_widths="1fr"> -