From 3877ff0b6f0bd776197b2d8dc82437c857a866e5 Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Sun, 17 May 2026 09:50:22 +0200 Subject: [PATCH] Go to matching action --- modules/purchase_trade/__init__.py | 11 ++- modules/purchase_trade/lot.py | 93 +++++++++++++++++-- modules/purchase_trade/lot.xml | 55 +++++++---- modules/purchase_trade/tests/test_module.py | 63 +++++++++++++ .../view/lot_go_matching_start_form.xml | 12 +++ 5 files changed, 199 insertions(+), 35 deletions(-) create mode 100644 modules/purchase_trade/view/lot_go_matching_start_form.xml diff --git a/modules/purchase_trade/__init__.py b/modules/purchase_trade/__init__.py index e092b88..50245c0 100755 --- a/modules/purchase_trade/__init__.py +++ b/modules/purchase_trade/__init__.py @@ -248,11 +248,12 @@ def register(): sale.AnalyticDimensionAssignment, sale.PriceComposition, module='sale', type_='model') - Pool.register( - lot.LotShipping, - lot.LotMatching, - #lot.LotMatchingUnit, - lot.LotWeighing, + Pool.register( + lot.LotShipping, + lot.LotMatching, + lot.LotGoMatching, + #lot.LotMatchingUnit, + lot.LotWeighing, lot.CreateContracts, lot.LotUnmatch, lot.LotUnship, diff --git a/modules/purchase_trade/lot.py b/modules/purchase_trade/lot.py index 2e10970..e678c56 100755 --- a/modules/purchase_trade/lot.py +++ b/modules/purchase_trade/lot.py @@ -2612,9 +2612,9 @@ class LotContext(ModelView): def default_ps(cls): return 'all' - @classmethod - def default_group(cls): - return 'by_physic' + @classmethod + def default_group(cls): + return 'all' @classmethod def default_wh(cls): @@ -2801,9 +2801,62 @@ class LotMatching(Wizard): matching = StateTransition() - def transition_start(self): - return 'match' - + def transition_start(self): + return 'match' + + @staticmethod + def _record_id(record): + return record.id if record else None + + @classmethod + def _matching_lot_value(cls, lqt, side): + lot = lqt.lot_p if side == 'purchase' else lqt.lot_s + line = getattr(lot, 'line', None) + sale_line = getattr(lot, 'sale_line', None) + purchase = getattr(line, 'purchase', None) + sale = getattr(sale_line, 'sale', None) + party = ( + getattr(purchase, 'party', None) if side == 'purchase' + else getattr(sale, 'party', None)) + quantity = Decimal(str(lqt.lot_quantity or 0)) + if side == 'sale': + quantity = abs(quantity) + return { + 'lot_id': cls._record_id(lot), + 'lot_r_id': cls._record_id(lqt), + 'lot_purchase': cls._record_id(purchase), + 'lot_sale': cls._record_id(sale), + 'lot_shipment_in': cls._record_id(lqt.lot_shipment_in), + 'lot_shipment_internal': cls._record_id( + lqt.lot_shipment_internal), + 'lot_shipment_out': cls._record_id(lqt.lot_shipment_out), + 'lot_type': getattr(lot, 'lot_type', None), + 'lot_product': cls._record_id(getattr(lot, 'lot_product', None)), + 'lot_quantity': quantity, + 'lot_matched_qt': 0, + 'lot_cp': cls._record_id(party), + } + + @classmethod + def _selected_matching_defaults(cls, active_ids): + LotQt = Pool().get('lot.qt') + lot_p = [] + lot_s = [] + for active_id in active_ids or []: + if active_id <= 10000000: + continue + lqt = LotQt(active_id - 10000000) + if lqt.lot_p and lqt.lot_s: + continue + if lqt.lot_p: + lot_p.append(cls._matching_lot_value(lqt, 'purchase')) + elif lqt.lot_s: + lot_s.append(cls._matching_lot_value(lqt, 'sale')) + return { + 'lot_p': lot_p, + 'lot_s': lot_s, + } + def transition_matching(self): Warning = Pool().get('res.user.warning') LotQt = Pool().get('lot.qt') @@ -2831,10 +2884,30 @@ class LotMatching(Wizard): return 'end' - def end(self): - return 'reload' - -class LotMatchingStart(ModelView): + def end(self): + return 'reload' + + +class LotGoMatching(LotMatching): + "Go to matching" + __name__ = "lot.go_matching" + + match = StateView( + 'lot.matching.start', + 'purchase_trade.go_matching_view_form', [ + Button("Cancel", 'end', 'tryton-cancel'), + Button("Match", 'matching', 'tryton-ok', default=True), + ]) + + def default_match(self, fields): + context = Transaction().context + values = self._selected_matching_defaults( + context.get('active_ids') or []) + values['qt_type'] = 'all' + return values + + +class LotMatchingStart(ModelView): "Matching" __name__ = "lot.matching.start" lot_p = fields.One2Many('lot.matching.lot','lms',"Purchase") diff --git a/modules/purchase_trade/lot.xml b/modules/purchase_trade/lot.xml index db40f35..097680d 100755 --- a/modules/purchase_trade/lot.xml +++ b/modules/purchase_trade/lot.xml @@ -164,16 +164,21 @@ this repository contains the full copyright notices and license terms. --> tree lot_invoicing_inv_tree - - lot.matching.start - form - lot_matching_start_form - - - lot.matching.lot - tree - lot_matching_lot_tree - + + lot.matching.start + form + lot_matching_start_form + + + lot.matching.start + form + lot_go_matching_start_form + + + lot.matching.lot + tree + lot_matching_lot_tree + lot.matching.lot tree @@ -184,16 +189,26 @@ this repository contains the full copyright notices and license terms. --> lot.matching lot.report - - form_action - lot.report,-1 - - - - lot.weighing.start - form - lot_weighing_start_form - + + form_action + lot.report,-1 + + + + Go to matching + lot.go_matching + lot.report + + + form_action + lot.report,-1 + + + + lot.weighing.start + form + lot_weighing_start_form + lot.weighing.lot tree diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py index 7cd9fb3..dd6da10 100644 --- a/modules/purchase_trade/tests/test_module.py +++ b/modules/purchase_trade/tests/test_module.py @@ -2722,6 +2722,69 @@ class PurchaseTradeTestCase(ModuleTestCase): with self.assertRaises(UserError): lot_module.LotQt.match_lots([purchase_lot], []) + def test_go_matching_defaults_selected_open_lot_qts(self): + 'go to matching only preloads selected unmatched open lot.qt rows' + purchase = Mock(id=10, party=Mock(id=20)) + sale = Mock(id=30, party=Mock(id=40)) + product = Mock(id=50) + purchase_lot = Mock( + id=101, line=Mock(purchase=purchase), sale_line=None, + lot_type='virtual', lot_product=product) + sale_lot = Mock( + id=102, line=None, sale_line=Mock(sale=sale), + lot_type='virtual', lot_product=product) + matched_sale_lot = Mock( + id=103, line=None, sale_line=Mock(sale=sale), + lot_type='virtual', lot_product=product) + lot_qts = { + 1: Mock( + id=1, lot_p=purchase_lot, lot_s=None, + lot_quantity=Decimal('100'), + lot_shipment_in=None, lot_shipment_internal=None, + lot_shipment_out=None), + 2: Mock( + id=2, lot_p=None, lot_s=sale_lot, + lot_quantity=Decimal('80'), + lot_shipment_in=None, lot_shipment_internal=None, + lot_shipment_out=None), + 3: Mock( + id=3, lot_p=purchase_lot, lot_s=matched_sale_lot, + lot_quantity=Decimal('20'), + lot_shipment_in=None, lot_shipment_internal=None, + lot_shipment_out=None), + } + + class LotQtMock: + def __call__(self, _id): + return lot_qts[_id] + + pool = Mock() + pool.get.side_effect = ( + lambda name: LotQtMock() if name == 'lot.qt' else Mock()) + transaction = Mock() + transaction.context = { + 'active_ids': [10000001, 10000002, 10000003], + } + + with patch.object(lot_module, 'Pool', return_value=pool): + with patch.object( + lot_module, 'Transaction', return_value=transaction): + result = lot_module.LotGoMatching().default_match([]) + + self.assertEqual(result['qt_type'], 'all') + self.assertEqual(len(result['lot_p']), 1) + self.assertEqual(len(result['lot_s']), 1) + self.assertEqual(result['lot_p'][0]['lot_r_id'], 1) + self.assertEqual(result['lot_p'][0]['lot_quantity'], Decimal('100')) + self.assertEqual(result['lot_p'][0]['lot_cp'], 20) + self.assertEqual(result['lot_s'][0]['lot_r_id'], 2) + self.assertEqual(result['lot_s'][0]['lot_quantity'], Decimal('80')) + self.assertEqual(result['lot_s'][0]['lot_cp'], 40) + + def test_lot_context_group_defaults_to_all(self): + 'lots management opens without grouping by physical lot' + self.assertEqual(lot_module.LotContext.default_group(), 'all') + def test_contract_detail_defaults_sale_locations_from_dropship_purchase(self): 'create contracts copies supplier-to-customer locations from purchase to sale' supplier = Mock(id=1, type='supplier') diff --git a/modules/purchase_trade/view/lot_go_matching_start_form.xml b/modules/purchase_trade/view/lot_go_matching_start_form.xml new file mode 100644 index 0000000..50ed31f --- /dev/null +++ b/modules/purchase_trade/view/lot_go_matching_start_form.xml @@ -0,0 +1,12 @@ +
+ + + + + +