diff --git a/modules/purchase_trade/docs/business-rules.md b/modules/purchase_trade/docs/business-rules.md index 033aaf4..ae10214 100644 --- a/modules/purchase_trade/docs/business-rules.md +++ b/modules/purchase_trade/docs/business-rules.md @@ -691,6 +691,34 @@ Owner technique: `a completer` - Priorite: - `importante` +### BR-PT-023 - Lots Management separe matching, side et shipping status + +- Intent: rendre le rapport `lot.report` exploitable sans melanger le statut de + matching, le sens achat/vente et l'avancement logistique. +- Description: + - Le filtre `Matching status` ne porte que le matching commercial: + `All`, `Matched`, `Not Matched`. + - Le filtre `Side` permet de lire: + `All`, `Purchase`, `Sale`. + - Le filtre `Shipping status` porte uniquement le lien `shipment_in` du + `lot.lot` ou du `lot.qt`. +- Resultat attendu: + - `Unshipped`: aucun `shipment_in` lie au `lot.lot` ou au `lot.qt`. + - `Scheduled`: un `shipment_in` est lie et son etat est `draft`. + - `Shipped`: un `shipment_in` est lie et son etat est `started`. + - `Received`: un `shipment_in` est lie et son etat est `received` ou `done`. + - Le rapport affiche une colonne `Shipping status`. + - Le rapport affiche `Purchase Delivery Period` depuis la ligne achat et + `Sale Delivery Period` depuis la ligne vente; l'ancien champ mixte + `Del Period` ne doit plus prendre la periode sale comme fallback achat. +- Shipment type: + - Sur `stock.shipment.in` et dans `lot.report`, `Shipment Type` vaut + `Dropship` si `from_location.type = supplier` et + `to_location.type = customer`. + - Dans tous les autres cas, `Shipment Type` vaut `Inbound`. +- Priorite: + - `importante` + ## 4) Exemples concrets ### Exemple E1 - Augmentation simple diff --git a/modules/purchase_trade/lot.py b/modules/purchase_trade/lot.py index a4708f4..e0abdde 100755 --- a/modules/purchase_trade/lot.py +++ b/modules/purchase_trade/lot.py @@ -1218,7 +1218,10 @@ class LotQt( # OpenPosition.create_from_purchase_line(pl) @classmethod - def getQuery(cls,purchase=None,sale=None,shipment=None,type=None,state=None,qttype=None,supplier=None,client=None,ps=None,lot_status=None,group=None,product=None,location=None,origin=None,finished=False): + def getQuery(cls, purchase=None, sale=None, shipment=None, type=None, + state=None, qttype=None, supplier=None, client=None, ps=None, + lot_status=None, group=None, product=None, location=None, + origin=None, finished=False, shipping_status=None): pool = Pool() LotQt = pool.get('lot.qt') lqt = LotQt.__table__() @@ -1264,14 +1267,14 @@ class LotQt( wh = ((lqt.lot_quantity > 0) & ((lp.lot_type == 'virtual') | (ls.lot_type == 'virtual'))) #wh &= (((lqt.create_date >= asof) & ((lqt.create_date-datetime.timedelta(1)) <= todate))) - if ps == 'P': - wh &= ((lqt.lot_p != None) & (lqt.lot_s == None)) - if not finished: - wh &= (pl.finished == False) - elif ps == 'S': - wh &= (((lqt.lot_s != None) & (lqt.lot_p == None)) | ((lqt.lot_s != None) & (lqt.lot_p != None) & (lp.lot_type == 'virtual'))) - if not finished: - wh &= (sl.finished == False) + if ps == 'P': + wh &= (lqt.lot_p != None) + if not finished: + wh &= (pl.finished == False) + elif ps == 'S': + wh &= (lqt.lot_s != None) + if not finished: + wh &= (sl.finished == False) if purchase: wh &= (pu.id == purchase) if sale: @@ -1284,12 +1287,19 @@ class LotQt( wh &= (lqt.lot_product == product) if location: wh &= (Case((lqt.lot_shipment_in > 0, si.to_location),else_=Case((lqt.lot_shipment_internal > 0, sin.to_location),else_=pu.to_location)) == location) - if type=='matched': - wh &= ((ls.sale_line > 0) & (lp.line > 0)) - elif type=='not matched': - wh &= (((ls.sale_line > 0) & (lp.line == None)) | ((ls.sale_line == None) & (lp.line > 0))) - elif type=='shipped': - wh &= (lqt.lot_shipment_in > 0) + if type=='matched': + wh &= ((ls.sale_line > 0) & (lp.line > 0)) + elif type=='not matched': + wh &= (((ls.sale_line > 0) & (lp.line == None)) | ((ls.sale_line == None) & (lp.line > 0))) + if shipping_status == 'unshipped': + wh &= (lqt.lot_shipment_in == None) + elif shipping_status == 'scheduled': + wh &= ((lqt.lot_shipment_in != None) & (si.state == 'draft')) + elif shipping_status == 'shipped': + wh &= ((lqt.lot_shipment_in != None) & (si.state == 'started')) + elif shipping_status == 'received': + wh &= ((lqt.lot_shipment_in != None) + & (si.state.in_(['received', 'done']))) if qttype=='virtual': wh &= ((lp.lot_type == 'virtual') | (ls.lot_type == 'virtual')) elif qttype=='physic': @@ -1310,9 +1320,18 @@ class LotQt( if client and sa: wh &= (sa.party == client) - AvQt = Case(((lqt.lot_p>0) & (lqt.lot_s>0), 0),else_=Case((lqt.lot_p>0,lqt.lot_quantity),else_=-lqt.lot_quantity)) - MaQt = Case(((lqt.lot_p>0) & (lqt.lot_s>0), lqt.lot_quantity),else_=0) - query = ( + AvQt = Case(((lqt.lot_p>0) & (lqt.lot_s>0), 0),else_=Case((lqt.lot_p>0,lqt.lot_quantity),else_=-lqt.lot_quantity)) + MaQt = Case(((lqt.lot_p>0) & (lqt.lot_s>0), lqt.lot_quantity),else_=0) + ShippingStatus = Case( + (lqt.lot_shipment_in == None, 'unshipped'), + else_=Case( + (si.state == 'draft', 'scheduled'), + else_=Case( + (si.state == 'started', 'shipped'), + else_=Case( + (si.state.in_(['received', 'done']), 'received'), + else_=None)))) + query = ( lqt.join(lp,'LEFT',condition=lqt.lot_p == lp.id) .join(pl,'LEFT',condition=lp.line == pl.id) .join(pu,'LEFT', condition=pl.purchase == pu.id) @@ -1349,11 +1368,8 @@ class LotQt( Case((lp.id>0, lp.lot_himself),else_=ls.lot_himself).as_('r_lot_himself'), Case((lp.id>0, lp.lot_container),else_=ls.lot_container).as_('r_lot_container'), Case((lp.id>0, lp.line),else_=None).as_('r_line'), - Case( - (((lqt.lot_s != None) & (lqt.lot_p == None) & (sl.id > 0)), - sl.del_period), - else_=Case((pl.id>0, pl.del_period),else_=None) - ).as_('r_del_period'), + Case((pl.id>0, pl.del_period), else_=None).as_('r_del_period'), + Case((sl.id>0, sl.del_period), else_=None).as_('r_sale_del_period'), Case((pu.id>0, pu.id),else_=None).as_('r_purchase'), Case((sa.id>0, sa.id),else_=None).as_('r_sale'), Case((ls.id>0, ls.sale_line),else_=None).as_('r_sale_line'), @@ -1364,18 +1380,19 @@ class LotQt( Case((lqt.lot_shipment_in > 0, si.to_location),else_=Case((lqt.lot_shipment_internal > 0, sin.to_location),else_=pu.to_location)).as_('r_from_t'), Literal(None).as_('r_lot_pur_inv_state'), Literal(None).as_('r_lot_sale_inv_state'), - Literal(None).as_('r_lot_pur_inv'), - Literal(None).as_('r_lot_sal_inv'), - Literal(None).as_('r_lot_state'), - where=wh - ) - ) - - wh2 = (lp.lot_type == 'physic') - if ps == 'P': - wh2 &= (lp.sale_line == None) - elif ps == 'S': - wh2 &= (lp.sale_line != None) + Literal(None).as_('r_lot_pur_inv'), + Literal(None).as_('r_lot_sal_inv'), + Literal(None).as_('r_lot_state'), + ShippingStatus.as_('r_shipping_status'), + where=wh + ) + ) + + wh2 = (lp.lot_type == 'physic') + if ps == 'P': + wh2 &= (lp.line != None) + elif ps == 'S': + wh2 &= (lp.sale_line != None) if purchase: wh2 &= (pu.id == purchase) if sale: @@ -1392,8 +1409,15 @@ class LotQt( wh2 &= (lp.sale_line != None) elif type=='not matched': wh2 &= (lp.sale_line == None) - elif type=='shipped': - wh2 &= (lp.lot_shipment_in > 0) + if shipping_status == 'unshipped': + wh2 &= (lp.lot_shipment_in == None) + elif shipping_status == 'scheduled': + wh2 &= ((lp.lot_shipment_in != None) & (si.state == 'draft')) + elif shipping_status == 'shipped': + wh2 &= ((lp.lot_shipment_in != None) & (si.state == 'started')) + elif shipping_status == 'received': + wh2 &= ((lp.lot_shipment_in != None) + & (si.state.in_(['received', 'done']))) if qttype=='virtual': wh2 &= False if lot_status: @@ -1412,8 +1436,17 @@ class LotQt( if client and sa: wh2 &= (sa.party == client) - AvQt2 = Case((lp.sale_line>0, 0),else_=lqh.quantity * (puom.factor / puoml.factor)) - MaQt2 = Case((lp.sale_line>0, lqh.quantity * (puom.factor / puoml.factor)),else_=0) + AvQt2 = Case((lp.sale_line>0, 0),else_=lqh.quantity * (puom.factor / puoml.factor)) + MaQt2 = Case((lp.sale_line>0, lqh.quantity * (puom.factor / puoml.factor)),else_=0) + ShippingStatus2 = Case( + (lp.lot_shipment_in == None, 'unshipped'), + else_=Case( + (si.state == 'draft', 'scheduled'), + else_=Case( + (si.state == 'started', 'shipped'), + else_=Case( + (si.state.in_(['received', 'done']), 'received'), + else_=None)))) query2 = ( lp.join(lqh, "LEFT", condition=(lp.id == lqh.lot) & (lp.lot_state == lqh.quantity_type)) @@ -1462,6 +1495,7 @@ class LotQt( lp.lot_container.as_("r_lot_container"), lp.line.as_("r_line"), pl.del_period.as_("r_del_period"), + sl.del_period.as_("r_sale_del_period"), Case((pu.id > 0, pu.id), else_=None).as_("r_purchase"), Case((sa.id > 0, sa.id), else_=None).as_("r_sale"), lp.sale_line.as_("r_sale_line"), @@ -1472,12 +1506,13 @@ class LotQt( Case((lp.lot_shipment_in > 0, si.to_location), else_=Case((lp.lot_shipment_internal > 0, sin.to_location), else_=pu.to_location)).as_("r_from_t"), lp.lot_pur_inv_state.as_('r_lot_pur_inv_state'), lp.lot_sale_inv_state.as_('r_lot_sale_inv_state'), - Case((final.id > 0, final.id), else_=prov.id).as_('r_lot_pur_inv'), - Case((final_s.id > 0, final_s.id), else_=prov_s.id).as_('r_lot_sal_inv'), - lp.lot_state.as_('r_lot_state'), - where=wh2, - ) - ) + Case((final.id > 0, final.id), else_=prov.id).as_('r_lot_pur_inv'), + Case((final_s.id > 0, final_s.id), else_=prov_s.id).as_('r_lot_sal_inv'), + lp.lot_state.as_('r_lot_state'), + ShippingStatus2.as_('r_shipping_status'), + where=wh2, + ) + ) if group=='by_physic': ship_group = Case((lp.lot_shipment_in>0, lp.lot_shipment_in),else_=Case((lp.lot_shipment_internal>0, lp.lot_shipment_internal),else_=Case((lp.lot_shipment_out>0, lp.lot_shipment_out),else_=None))) @@ -1528,6 +1563,7 @@ class LotQt( Max(lp.lot_container).as_("r_lot_container"), lp.line.as_("r_line"), Max(pl.del_period).as_("r_del_period"), + Max(sl.del_period).as_("r_sale_del_period"), Max(Case((pu.id > 0, pu.id), else_=None)).as_("r_purchase"), Max(Case((sa.id > 0, sa.id), else_=None)).as_("r_sale"), lp.sale_line.as_("r_sale_line"), @@ -1538,11 +1574,12 @@ class LotQt( Max(Case((lp.lot_shipment_in > 0, si.to_location), else_=Case((lp.lot_shipment_internal > 0, si.to_location), else_=pu.to_location))).as_("r_from_t"), Max(lp.lot_pur_inv_state).as_('r_lot_pur_inv_state'), Max(lp.lot_sale_inv_state).as_('r_lot_sale_inv_state'), - Max(Case((final.id > 0, final.id), else_=prov.id)).as_('r_lot_pur_inv'), - Max(Case((final_s.id > 0, final_s.id), else_=prov_s.id)).as_('r_lot_sal_inv'), - Max(lp.lot_state).as_('r_lot_state'), - where=wh2, - group_by=[lp.line,lp.lot_shipment_in,lp.lot_shipment_internal,lp.lot_shipment_out,lp.sale_line] + Max(Case((final.id > 0, final.id), else_=prov.id)).as_('r_lot_pur_inv'), + Max(Case((final_s.id > 0, final_s.id), else_=prov_s.id)).as_('r_lot_sal_inv'), + Max(lp.lot_state).as_('r_lot_state'), + Max(ShippingStatus2).as_('r_shipping_status'), + where=wh2, + group_by=[lp.line,lp.lot_shipment_in,lp.lot_shipment_internal,lp.lot_shipment_out,lp.sale_line] ) ) @@ -1582,6 +1619,7 @@ class LotQt( union.r_lot_container.as_("r_lot_container"), union.r_line.as_("r_line"), union.r_del_period.as_("r_del_period"), + union.r_sale_del_period.as_("r_sale_del_period"), union.r_purchase.as_("r_purchase"), union.r_sale.as_("r_sale"), union.r_sale_line.as_("r_sale_line"), @@ -1592,10 +1630,11 @@ class LotQt( union.r_from_t.as_("r_from_t"), union.r_lot_pur_inv_state.as_('r_lot_pur_inv_state'), union.r_lot_sale_inv_state.as_('r_lot_sale_inv_state'), - union.r_lot_pur_inv.as_('r_lot_pur_inv'), - union.r_lot_sal_inv.as_('r_lot_sal_inv'), - union.r_lot_state.as_('r_lot_state'), - order_by=[union.r_line.desc]) + union.r_lot_pur_inv.as_('r_lot_pur_inv'), + union.r_lot_sal_inv.as_('r_lot_sal_inv'), + union.r_lot_state.as_('r_lot_state'), + union.r_shipping_status.as_('r_shipping_status'), + order_by=[union.r_line.desc]) return ordered_union class LotReport( @@ -1657,15 +1696,30 @@ class LotReport( r_lot_unit_line = fields.Function(fields.Many2One('product.uom', "Unit"),'get_unit') r_lot_price = fields.Function(fields.Numeric("Price", digits='r_lot_unit_line'),'get_lot_price') r_lot_price_sale = fields.Function(fields.Numeric("Price", digits='r_lot_unit_line'),'get_lot_sale_price') - r_del_period = fields.Many2One('product.month', "Delivery Period") + r_del_period = fields.Many2One( + 'product.month', "Purchase Delivery Period") + r_sale_del_period = fields.Many2One( + 'product.month', "Sale Delivery Period") r_sale_line = fields.Many2One('sale.line',"S. line") r_sale = fields.Many2One('sale.sale',"Sale") r_tot = fields.Numeric("Qt tot", digits='r_lot_unit_line') r_supplier = fields.Many2One('party.party',"Supplier") - r_client = fields.Many2One('party.party',"Client") - r_from_l = fields.Many2One('stock.location', 'From') - r_from_t = fields.Many2One('stock.location', 'To') - sh_icon = fields.Function(fields.Char("Shipment Icon"), 'on_change_with_sh_icon') + r_client = fields.Many2One('party.party',"Client") + r_from_l = fields.Many2One('stock.location', 'From') + r_from_t = fields.Many2One('stock.location', 'To') + r_shipping_status = fields.Selection([ + (None, ''), + ('unshipped', 'Unshipped'), + ('scheduled', 'Scheduled'), + ('shipped', 'Shipped'), + ('received', 'Received'), + ], "Shipping status") + r_shipment_type = fields.Function(fields.Selection([ + (None, ''), + ('dropship', 'Dropship'), + ('inbound', 'Inbound'), + ], "Shipment Type"), 'get_shipment_type') + sh_icon = fields.Function(fields.Char("Shipment Icon"), 'on_change_with_sh_icon') qt_icon = fields.Function(fields.Char("Qt Icon"), 'on_change_with_qt_icon') r_shipment_origin = fields.Function( fields.Reference( @@ -1775,14 +1829,24 @@ class LotReport( } return "pivotjson:" + json.dumps(pivot_data) - def get_shipment_origin(self, name): - if self.r_lot_shipment_in: - return 'stock.shipment.in,' + str(self.r_lot_shipment_in.id) - elif self.r_lot_shipment_out: - return 'stock.shipment.out,' + str(self.r_lot_shipment_out.id) - elif self.r_lot_shipment_internal: - return 'stock.shipment.internal,' + str(self.r_lot_shipment_internal.id) - return None + def get_shipment_origin(self, name): + if self.r_lot_shipment_in: + return 'stock.shipment.in,' + str(self.r_lot_shipment_in.id) + elif self.r_lot_shipment_out: + return 'stock.shipment.out,' + str(self.r_lot_shipment_out.id) + elif self.r_lot_shipment_internal: + return 'stock.shipment.internal,' + str(self.r_lot_shipment_internal.id) + return None + + @fields.depends('r_lot_shipment_in', 'r_from_l', 'r_from_t') + def get_shipment_type(self, name): + if self.r_lot_shipment_in: + return self.r_lot_shipment_in.shipment_type + if (self.r_from_l and self.r_from_t + and self.r_from_l.type == 'supplier' + and self.r_from_t.type == 'customer'): + return 'dropship' + return 'inbound' @classmethod def __setup__(cls): @@ -1865,14 +1929,32 @@ class LotReport( state = context.get('state') wh = context.get('wh') mode = context.get('mode') - group = context.get('group') - origin = context.get('origin') - supplier = context.get('supplier') - #asof = context.get('asof') - #todate = context.get('todate') - finished = context.get('finished') - query = LotQt.getQuery(purchase,sale,shipment,type,state,None,supplier,None,None,wh,group,product,location,origin,finished) - return query + group = context.get('group') + origin = context.get('origin') + supplier = context.get('supplier') + client = context.get('client') + ps = context.get('ps') + shipping_status = context.get('shipping_status') + #asof = context.get('asof') + #todate = context.get('todate') + finished = context.get('finished') + query = LotQT.getQuery( + purchase=purchase, + sale=sale, + shipment=shipment, + type=type, + state=state, + supplier=supplier, + client=client, + ps=ps, + lot_status=wh, + group=group, + product=product, + location=location, + origin=origin, + finished=finished, + shipping_status=shipping_status) + return query @classmethod def search_count(cls, domain, active_test=True, context=None): @@ -1917,21 +1999,33 @@ class LotContext(ModelView): asof = fields.Date("As of") todate = fields.Date("To") - purchase = fields.Many2One('purchase.purchase', "Purchase") - supplier = fields.Many2One('party.party', "Supplier") - sale = fields.Many2One('sale.sale', "Sale") - shipment = fields.Many2One('stock.shipment.in', "Shipment") - product = fields.Many2One('product.product',"Product") - location = fields.Many2One('stock.location',"Location") - type = fields.Selection([ - ('all', 'All'), - ('matched', 'Matched'), - ('not matched', 'Not matched'), - ('shipped', 'Shipped') - ], 'Type') - state = fields.Selection([ - ('all', 'All'), - ('available', 'Available'), + purchase = fields.Many2One('purchase.purchase', "Purchase") + supplier = fields.Many2One('party.party', "Supplier") + client = fields.Many2One('party.party', "Client") + sale = fields.Many2One('sale.sale', "Sale") + shipment = fields.Many2One('stock.shipment.in', "Shipment") + product = fields.Many2One('product.product',"Product") + location = fields.Many2One('stock.location',"Location") + type = fields.Selection([ + ('all', 'All'), + ('matched', 'Matched'), + ('not matched', 'Not Matched'), + ], 'Matching status') + shipping_status = fields.Selection([ + ('all', 'All'), + ('unshipped', 'Unshipped'), + ('scheduled', 'Scheduled'), + ('shipped', 'Shipped'), + ('received', 'Received'), + ], "Shipping status") + ps = fields.Selection([ + ('all', 'All'), + ('P', 'Purchase'), + ('S', 'Sale'), + ], "Side") + state = fields.Selection([ + ('all', 'All'), + ('available', 'Available'), ('reserved', 'Reserved'), ('locked', 'Locked') ], 'State') @@ -1980,8 +2074,16 @@ class LotContext(ModelView): return Date.today() @classmethod - def default_type(cls): - return 'all' + def default_type(cls): + return 'all' + + @classmethod + def default_shipping_status(cls): + return 'all' + + @classmethod + def default_ps(cls): + return 'all' @classmethod def default_group(cls): @@ -2004,11 +2106,15 @@ class LotContext(ModelView): return Transaction().context.get('sale', None) @classmethod - def get_context(cls, fields_names=None): - fields_names = fields_names.copy() if fields_names is not None else [] - fields_names.append('purchase') - fields_names.append('sale') - return super().get_context(fields_names=fields_names) + def get_context(cls, fields_names=None): + fields_names = fields_names.copy() if fields_names is not None else [] + for field_name in [ + 'purchase', 'sale', 'shipment', 'supplier', 'client', + 'product', 'location', 'type', 'shipping_status', 'ps', + 'state', 'wh', 'mode', 'group', 'origin', 'finished']: + if field_name not in fields_names: + fields_names.append(field_name) + return super().get_context(fields_names=fields_names) class LotShipping(Wizard): diff --git a/modules/purchase_trade/stock.py b/modules/purchase_trade/stock.py index 1c8b285..620eb06 100755 --- a/modules/purchase_trade/stock.py +++ b/modules/purchase_trade/stock.py @@ -403,11 +403,16 @@ class ShipmentWR(ModelSQL,ModelView): class ShipmentIn(metaclass=PoolMeta): __name__ = 'stock.shipment.in' - from_location = fields.Many2One('stock.location', 'From location') - to_location = fields.Many2One('stock.location', 'To location') - transport_type = fields.Selection([ - ('vessel', 'Vessel'), - ('truck', 'Truck'), + from_location = fields.Many2One('stock.location', 'From location') + to_location = fields.Many2One('stock.location', 'To location') + shipment_type = fields.Function(fields.Selection([ + (None, ''), + ('dropship', 'Dropship'), + ('inbound', 'Inbound'), + ], "Shipment Type"), 'get_shipment_type') + transport_type = fields.Selection([ + ('vessel', 'Vessel'), + ('truck', 'Truck'), ('other', 'Other'), ], 'Transport type') vessel = fields.Many2One('trade.vessel',"Vessel") @@ -477,6 +482,14 @@ class ShipmentIn(metaclass=PoolMeta): if self.vessel: return self.vessel.vessel_type + @fields.depends('from_location', 'to_location') + def get_shipment_type(self, name=None): + if (self.from_location and self.to_location + and self.from_location.type == 'supplier' + and self.to_location.type == 'customer'): + return 'dropship' + return 'inbound' + def _get_report_primary_move(self): moves = list(self.incoming_moves or self.moves or []) return moves[0] if moves else None diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py index 75c0d19..8d9bc5f 100644 --- a/modules/purchase_trade/tests/test_module.py +++ b/modules/purchase_trade/tests/test_module.py @@ -781,6 +781,37 @@ class PurchaseTradeTestCase(ModuleTestCase): fee_model.save.assert_not_called() product_model.get_by_name.assert_not_called() + @with_transaction() + def test_shipment_in_type_detects_dropship(self): + 'shipment type is dropship from supplier directly to customer' + ShipmentIn = Pool().get('stock.shipment.in') + shipment = ShipmentIn() + shipment.from_location = Mock(type='supplier') + shipment.to_location = Mock(type='customer') + + self.assertEqual(shipment.get_shipment_type(), 'dropship') + + @with_transaction() + def test_shipment_in_type_defaults_to_inbound(self): + 'shipment type is inbound for non supplier-to-customer routes' + ShipmentIn = Pool().get('stock.shipment.in') + shipment = ShipmentIn() + shipment.from_location = Mock(type='supplier') + shipment.to_location = Mock(type='storage') + + self.assertEqual(shipment.get_shipment_type(), 'inbound') + + @with_transaction() + def test_lot_report_shipment_type_uses_linked_shipment(self): + 'lot report shipment type mirrors the linked shipment_in' + LotReport = Pool().get('lot.report') + report = LotReport() + report.r_lot_shipment_in = Mock(shipment_type='dropship') + report.r_from_l = Mock(type='storage') + report.r_from_t = Mock(type='storage') + + self.assertEqual(report.get_shipment_type(None), 'dropship') + def test_purchase_line_default_pricing_rule_comes_from_configuration(self): 'purchase line pricing_rule defaults to the purchase_trade singleton value' PurchaseLine = Pool().get('purchase.line') diff --git a/modules/purchase_trade/view/lot_report_context_form.xml b/modules/purchase_trade/view/lot_report_context_form.xml index 8d51948..b854c3a 100755 --- a/modules/purchase_trade/view/lot_report_context_form.xml +++ b/modules/purchase_trade/view/lot_report_context_form.xml @@ -11,12 +11,18 @@