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 @@
+
+
+
+
+
+
diff --git a/modules/purchase_trade/view/lot_report_list.xml b/modules/purchase_trade/view/lot_report_list.xml
index c1268af..f814c75 100755
--- a/modules/purchase_trade/view/lot_report_list.xml
+++ b/modules/purchase_trade/view/lot_report_list.xml
@@ -4,6 +4,7 @@
+
@@ -14,9 +15,11 @@
+
+
diff --git a/modules/purchase_trade/view/shipment_in_form.xml b/modules/purchase_trade/view/shipment_in_form.xml
index 514eb3f..1067d9e 100755
--- a/modules/purchase_trade/view/shipment_in_form.xml
+++ b/modules/purchase_trade/view/shipment_in_form.xml
@@ -21,6 +21,8 @@ this repository contains the full copyright notices and license terms. -->
+
+