Lots Management Enhancement
This commit is contained in:
@@ -691,6 +691,34 @@ Owner technique: `a completer`
|
|||||||
- Priorite:
|
- Priorite:
|
||||||
- `importante`
|
- `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
|
## 4) Exemples concrets
|
||||||
|
|
||||||
### Exemple E1 - Augmentation simple
|
### Exemple E1 - Augmentation simple
|
||||||
|
|||||||
@@ -1218,7 +1218,10 @@ class LotQt(
|
|||||||
# OpenPosition.create_from_purchase_line(pl)
|
# OpenPosition.create_from_purchase_line(pl)
|
||||||
|
|
||||||
@classmethod
|
@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()
|
pool = Pool()
|
||||||
LotQt = pool.get('lot.qt')
|
LotQt = pool.get('lot.qt')
|
||||||
lqt = LotQt.__table__()
|
lqt = LotQt.__table__()
|
||||||
@@ -1265,11 +1268,11 @@ class LotQt(
|
|||||||
|
|
||||||
#wh &= (((lqt.create_date >= asof) & ((lqt.create_date-datetime.timedelta(1)) <= todate)))
|
#wh &= (((lqt.create_date >= asof) & ((lqt.create_date-datetime.timedelta(1)) <= todate)))
|
||||||
if ps == 'P':
|
if ps == 'P':
|
||||||
wh &= ((lqt.lot_p != None) & (lqt.lot_s == None))
|
wh &= (lqt.lot_p != None)
|
||||||
if not finished:
|
if not finished:
|
||||||
wh &= (pl.finished == False)
|
wh &= (pl.finished == False)
|
||||||
elif ps == 'S':
|
elif ps == 'S':
|
||||||
wh &= (((lqt.lot_s != None) & (lqt.lot_p == None)) | ((lqt.lot_s != None) & (lqt.lot_p != None) & (lp.lot_type == 'virtual')))
|
wh &= (lqt.lot_s != None)
|
||||||
if not finished:
|
if not finished:
|
||||||
wh &= (sl.finished == False)
|
wh &= (sl.finished == False)
|
||||||
if purchase:
|
if purchase:
|
||||||
@@ -1288,8 +1291,15 @@ class LotQt(
|
|||||||
wh &= ((ls.sale_line > 0) & (lp.line > 0))
|
wh &= ((ls.sale_line > 0) & (lp.line > 0))
|
||||||
elif type=='not matched':
|
elif type=='not matched':
|
||||||
wh &= (((ls.sale_line > 0) & (lp.line == None)) | ((ls.sale_line == None) & (lp.line > 0)))
|
wh &= (((ls.sale_line > 0) & (lp.line == None)) | ((ls.sale_line == None) & (lp.line > 0)))
|
||||||
elif type=='shipped':
|
if shipping_status == 'unshipped':
|
||||||
wh &= (lqt.lot_shipment_in > 0)
|
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':
|
if qttype=='virtual':
|
||||||
wh &= ((lp.lot_type == 'virtual') | (ls.lot_type == 'virtual'))
|
wh &= ((lp.lot_type == 'virtual') | (ls.lot_type == 'virtual'))
|
||||||
elif qttype=='physic':
|
elif qttype=='physic':
|
||||||
@@ -1312,6 +1322,15 @@ class LotQt(
|
|||||||
|
|
||||||
AvQt = Case(((lqt.lot_p>0) & (lqt.lot_s>0), 0),else_=Case((lqt.lot_p>0,lqt.lot_quantity),else_=-lqt.lot_quantity))
|
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)
|
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 = (
|
query = (
|
||||||
lqt.join(lp,'LEFT',condition=lqt.lot_p == lp.id)
|
lqt.join(lp,'LEFT',condition=lqt.lot_p == lp.id)
|
||||||
.join(pl,'LEFT',condition=lp.line == pl.id)
|
.join(pl,'LEFT',condition=lp.line == pl.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_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.lot_container),else_=ls.lot_container).as_('r_lot_container'),
|
||||||
Case((lp.id>0, lp.line),else_=None).as_('r_line'),
|
Case((lp.id>0, lp.line),else_=None).as_('r_line'),
|
||||||
Case(
|
Case((pl.id>0, pl.del_period), else_=None).as_('r_del_period'),
|
||||||
(((lqt.lot_s != None) & (lqt.lot_p == None) & (sl.id > 0)),
|
Case((sl.id>0, sl.del_period), else_=None).as_('r_sale_del_period'),
|
||||||
sl.del_period),
|
|
||||||
else_=Case((pl.id>0, pl.del_period),else_=None)
|
|
||||||
).as_('r_del_period'),
|
|
||||||
Case((pu.id>0, pu.id),else_=None).as_('r_purchase'),
|
Case((pu.id>0, pu.id),else_=None).as_('r_purchase'),
|
||||||
Case((sa.id>0, sa.id),else_=None).as_('r_sale'),
|
Case((sa.id>0, sa.id),else_=None).as_('r_sale'),
|
||||||
Case((ls.id>0, ls.sale_line),else_=None).as_('r_sale_line'),
|
Case((ls.id>0, ls.sale_line),else_=None).as_('r_sale_line'),
|
||||||
@@ -1367,13 +1383,14 @@ class LotQt(
|
|||||||
Literal(None).as_('r_lot_pur_inv'),
|
Literal(None).as_('r_lot_pur_inv'),
|
||||||
Literal(None).as_('r_lot_sal_inv'),
|
Literal(None).as_('r_lot_sal_inv'),
|
||||||
Literal(None).as_('r_lot_state'),
|
Literal(None).as_('r_lot_state'),
|
||||||
|
ShippingStatus.as_('r_shipping_status'),
|
||||||
where=wh
|
where=wh
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
wh2 = (lp.lot_type == 'physic')
|
wh2 = (lp.lot_type == 'physic')
|
||||||
if ps == 'P':
|
if ps == 'P':
|
||||||
wh2 &= (lp.sale_line == None)
|
wh2 &= (lp.line != None)
|
||||||
elif ps == 'S':
|
elif ps == 'S':
|
||||||
wh2 &= (lp.sale_line != None)
|
wh2 &= (lp.sale_line != None)
|
||||||
if purchase:
|
if purchase:
|
||||||
@@ -1392,8 +1409,15 @@ class LotQt(
|
|||||||
wh2 &= (lp.sale_line != None)
|
wh2 &= (lp.sale_line != None)
|
||||||
elif type=='not matched':
|
elif type=='not matched':
|
||||||
wh2 &= (lp.sale_line == None)
|
wh2 &= (lp.sale_line == None)
|
||||||
elif type=='shipped':
|
if shipping_status == 'unshipped':
|
||||||
wh2 &= (lp.lot_shipment_in > 0)
|
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':
|
if qttype=='virtual':
|
||||||
wh2 &= False
|
wh2 &= False
|
||||||
if lot_status:
|
if lot_status:
|
||||||
@@ -1414,6 +1438,15 @@ class LotQt(
|
|||||||
|
|
||||||
AvQt2 = Case((lp.sale_line>0, 0),else_=lqh.quantity * (puom.factor / puoml.factor))
|
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)
|
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 = (
|
query2 = (
|
||||||
lp.join(lqh, "LEFT", condition=(lp.id == lqh.lot) & (lp.lot_state == lqh.quantity_type))
|
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.lot_container.as_("r_lot_container"),
|
||||||
lp.line.as_("r_line"),
|
lp.line.as_("r_line"),
|
||||||
pl.del_period.as_("r_del_period"),
|
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((pu.id > 0, pu.id), else_=None).as_("r_purchase"),
|
||||||
Case((sa.id > 0, sa.id), else_=None).as_("r_sale"),
|
Case((sa.id > 0, sa.id), else_=None).as_("r_sale"),
|
||||||
lp.sale_line.as_("r_sale_line"),
|
lp.sale_line.as_("r_sale_line"),
|
||||||
@@ -1475,6 +1509,7 @@ class LotQt(
|
|||||||
Case((final.id > 0, final.id), else_=prov.id).as_('r_lot_pur_inv'),
|
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'),
|
Case((final_s.id > 0, final_s.id), else_=prov_s.id).as_('r_lot_sal_inv'),
|
||||||
lp.lot_state.as_('r_lot_state'),
|
lp.lot_state.as_('r_lot_state'),
|
||||||
|
ShippingStatus2.as_('r_shipping_status'),
|
||||||
where=wh2,
|
where=wh2,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1528,6 +1563,7 @@ class LotQt(
|
|||||||
Max(lp.lot_container).as_("r_lot_container"),
|
Max(lp.lot_container).as_("r_lot_container"),
|
||||||
lp.line.as_("r_line"),
|
lp.line.as_("r_line"),
|
||||||
Max(pl.del_period).as_("r_del_period"),
|
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((pu.id > 0, pu.id), else_=None)).as_("r_purchase"),
|
||||||
Max(Case((sa.id > 0, sa.id), else_=None)).as_("r_sale"),
|
Max(Case((sa.id > 0, sa.id), else_=None)).as_("r_sale"),
|
||||||
lp.sale_line.as_("r_sale_line"),
|
lp.sale_line.as_("r_sale_line"),
|
||||||
@@ -1541,6 +1577,7 @@ class LotQt(
|
|||||||
Max(Case((final.id > 0, final.id), else_=prov.id)).as_('r_lot_pur_inv'),
|
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(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(lp.lot_state).as_('r_lot_state'),
|
||||||
|
Max(ShippingStatus2).as_('r_shipping_status'),
|
||||||
where=wh2,
|
where=wh2,
|
||||||
group_by=[lp.line,lp.lot_shipment_in,lp.lot_shipment_internal,lp.lot_shipment_out,lp.sale_line]
|
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_lot_container.as_("r_lot_container"),
|
||||||
union.r_line.as_("r_line"),
|
union.r_line.as_("r_line"),
|
||||||
union.r_del_period.as_("r_del_period"),
|
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_purchase.as_("r_purchase"),
|
||||||
union.r_sale.as_("r_sale"),
|
union.r_sale.as_("r_sale"),
|
||||||
union.r_sale_line.as_("r_sale_line"),
|
union.r_sale_line.as_("r_sale_line"),
|
||||||
@@ -1595,6 +1633,7 @@ class LotQt(
|
|||||||
union.r_lot_pur_inv.as_('r_lot_pur_inv'),
|
union.r_lot_pur_inv.as_('r_lot_pur_inv'),
|
||||||
union.r_lot_sal_inv.as_('r_lot_sal_inv'),
|
union.r_lot_sal_inv.as_('r_lot_sal_inv'),
|
||||||
union.r_lot_state.as_('r_lot_state'),
|
union.r_lot_state.as_('r_lot_state'),
|
||||||
|
union.r_shipping_status.as_('r_shipping_status'),
|
||||||
order_by=[union.r_line.desc])
|
order_by=[union.r_line.desc])
|
||||||
return ordered_union
|
return ordered_union
|
||||||
|
|
||||||
@@ -1657,7 +1696,10 @@ class LotReport(
|
|||||||
r_lot_unit_line = fields.Function(fields.Many2One('product.uom', "Unit"),'get_unit')
|
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 = 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_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_line = fields.Many2One('sale.line',"S. line")
|
||||||
r_sale = fields.Many2One('sale.sale',"Sale")
|
r_sale = fields.Many2One('sale.sale',"Sale")
|
||||||
r_tot = fields.Numeric("Qt tot", digits='r_lot_unit_line')
|
r_tot = fields.Numeric("Qt tot", digits='r_lot_unit_line')
|
||||||
@@ -1665,6 +1707,18 @@ class LotReport(
|
|||||||
r_client = fields.Many2One('party.party',"Client")
|
r_client = fields.Many2One('party.party',"Client")
|
||||||
r_from_l = fields.Many2One('stock.location', 'From')
|
r_from_l = fields.Many2One('stock.location', 'From')
|
||||||
r_from_t = fields.Many2One('stock.location', 'To')
|
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')
|
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')
|
qt_icon = fields.Function(fields.Char("Qt Icon"), 'on_change_with_qt_icon')
|
||||||
r_shipment_origin = fields.Function(
|
r_shipment_origin = fields.Function(
|
||||||
@@ -1784,6 +1838,16 @@ class LotReport(
|
|||||||
return 'stock.shipment.internal,' + str(self.r_lot_shipment_internal.id)
|
return 'stock.shipment.internal,' + str(self.r_lot_shipment_internal.id)
|
||||||
return None
|
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
|
@classmethod
|
||||||
def __setup__(cls):
|
def __setup__(cls):
|
||||||
cls.r_line.search_unaccented = False
|
cls.r_line.search_unaccented = False
|
||||||
@@ -1868,10 +1932,28 @@ class LotReport(
|
|||||||
group = context.get('group')
|
group = context.get('group')
|
||||||
origin = context.get('origin')
|
origin = context.get('origin')
|
||||||
supplier = context.get('supplier')
|
supplier = context.get('supplier')
|
||||||
|
client = context.get('client')
|
||||||
|
ps = context.get('ps')
|
||||||
|
shipping_status = context.get('shipping_status')
|
||||||
#asof = context.get('asof')
|
#asof = context.get('asof')
|
||||||
#todate = context.get('todate')
|
#todate = context.get('todate')
|
||||||
finished = context.get('finished')
|
finished = context.get('finished')
|
||||||
query = LotQt.getQuery(purchase,sale,shipment,type,state,None,supplier,None,None,wh,group,product,location,origin,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
|
return query
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -1919,6 +2001,7 @@ class LotContext(ModelView):
|
|||||||
todate = fields.Date("To")
|
todate = fields.Date("To")
|
||||||
purchase = fields.Many2One('purchase.purchase', "Purchase")
|
purchase = fields.Many2One('purchase.purchase', "Purchase")
|
||||||
supplier = fields.Many2One('party.party', "Supplier")
|
supplier = fields.Many2One('party.party', "Supplier")
|
||||||
|
client = fields.Many2One('party.party', "Client")
|
||||||
sale = fields.Many2One('sale.sale', "Sale")
|
sale = fields.Many2One('sale.sale', "Sale")
|
||||||
shipment = fields.Many2One('stock.shipment.in', "Shipment")
|
shipment = fields.Many2One('stock.shipment.in', "Shipment")
|
||||||
product = fields.Many2One('product.product',"Product")
|
product = fields.Many2One('product.product',"Product")
|
||||||
@@ -1926,9 +2009,20 @@ class LotContext(ModelView):
|
|||||||
type = fields.Selection([
|
type = fields.Selection([
|
||||||
('all', 'All'),
|
('all', 'All'),
|
||||||
('matched', 'Matched'),
|
('matched', 'Matched'),
|
||||||
('not matched', 'Not matched'),
|
('not matched', 'Not Matched'),
|
||||||
('shipped', 'Shipped')
|
], 'Matching status')
|
||||||
], 'Type')
|
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([
|
state = fields.Selection([
|
||||||
('all', 'All'),
|
('all', 'All'),
|
||||||
('available', 'Available'),
|
('available', 'Available'),
|
||||||
@@ -1983,6 +2077,14 @@ class LotContext(ModelView):
|
|||||||
def default_type(cls):
|
def default_type(cls):
|
||||||
return 'all'
|
return 'all'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_shipping_status(cls):
|
||||||
|
return 'all'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_ps(cls):
|
||||||
|
return 'all'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_group(cls):
|
def default_group(cls):
|
||||||
return 'by_physic'
|
return 'by_physic'
|
||||||
@@ -2006,8 +2108,12 @@ class LotContext(ModelView):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_context(cls, fields_names=None):
|
def get_context(cls, fields_names=None):
|
||||||
fields_names = fields_names.copy() if fields_names is not None else []
|
fields_names = fields_names.copy() if fields_names is not None else []
|
||||||
fields_names.append('purchase')
|
for field_name in [
|
||||||
fields_names.append('sale')
|
'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)
|
return super().get_context(fields_names=fields_names)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -405,6 +405,11 @@ class ShipmentIn(metaclass=PoolMeta):
|
|||||||
|
|
||||||
from_location = fields.Many2One('stock.location', 'From location')
|
from_location = fields.Many2One('stock.location', 'From location')
|
||||||
to_location = fields.Many2One('stock.location', 'To 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([
|
transport_type = fields.Selection([
|
||||||
('vessel', 'Vessel'),
|
('vessel', 'Vessel'),
|
||||||
('truck', 'Truck'),
|
('truck', 'Truck'),
|
||||||
@@ -477,6 +482,14 @@ class ShipmentIn(metaclass=PoolMeta):
|
|||||||
if self.vessel:
|
if self.vessel:
|
||||||
return self.vessel.vessel_type
|
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):
|
def _get_report_primary_move(self):
|
||||||
moves = list(self.incoming_moves or self.moves or [])
|
moves = list(self.incoming_moves or self.moves or [])
|
||||||
return moves[0] if moves else None
|
return moves[0] if moves else None
|
||||||
|
|||||||
@@ -781,6 +781,37 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
|||||||
fee_model.save.assert_not_called()
|
fee_model.save.assert_not_called()
|
||||||
product_model.get_by_name.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):
|
def test_purchase_line_default_pricing_rule_comes_from_configuration(self):
|
||||||
'purchase line pricing_rule defaults to the purchase_trade singleton value'
|
'purchase line pricing_rule defaults to the purchase_trade singleton value'
|
||||||
PurchaseLine = Pool().get('purchase.line')
|
PurchaseLine = Pool().get('purchase.line')
|
||||||
|
|||||||
@@ -11,12 +11,18 @@
|
|||||||
<field name="shipment"/>
|
<field name="shipment"/>
|
||||||
<label name="supplier"/>
|
<label name="supplier"/>
|
||||||
<field name="supplier"/>
|
<field name="supplier"/>
|
||||||
|
<label name="client"/>
|
||||||
|
<field name="client"/>
|
||||||
<label name="product"/>
|
<label name="product"/>
|
||||||
<field name="product"/>
|
<field name="product"/>
|
||||||
<label name="location"/>
|
<label name="location"/>
|
||||||
<field name="location"/>
|
<field name="location"/>
|
||||||
<label name="type"/>
|
<label name="type"/>
|
||||||
<field name="type"/>
|
<field name="type"/>
|
||||||
|
<label name="shipping_status"/>
|
||||||
|
<field name="shipping_status"/>
|
||||||
|
<label name="ps"/>
|
||||||
|
<field name="ps"/>
|
||||||
<label name="state"/>
|
<label name="state"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<label name="wh"/>
|
<label name="wh"/>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
</field>
|
</field>
|
||||||
<field name="r_lot_p" width="60"/>
|
<field name="r_lot_p" width="60"/>
|
||||||
<field name="r_del_period" width="110"/>
|
<field name="r_del_period" width="110"/>
|
||||||
|
<field name="r_sale_del_period" width="110"/>
|
||||||
<field name="r_supplier" width="90"/>
|
<field name="r_supplier" width="90"/>
|
||||||
<field name="r_purchase" width="120"/>
|
<field name="r_purchase" width="120"/>
|
||||||
<field name="r_lot_pur_inv" width="120"/>
|
<field name="r_lot_pur_inv" width="120"/>
|
||||||
@@ -14,9 +15,11 @@
|
|||||||
<field name="r_lot_product" width="120"/>
|
<field name="r_lot_product" width="120"/>
|
||||||
<field name="r_from_l" width="80"/>
|
<field name="r_from_l" width="80"/>
|
||||||
<field name="r_from_t" width="80"/>
|
<field name="r_from_t" width="80"/>
|
||||||
|
<field name="r_shipment_type" width="90"/>
|
||||||
<field name="r_shipment_origin" width="160">
|
<field name="r_shipment_origin" width="160">
|
||||||
<prefix name="sh_icon"/>
|
<prefix name="sh_icon"/>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="r_shipping_status" width="100"/>
|
||||||
<field name="r_lot_status" width="80"/>
|
<field name="r_lot_status" width="80"/>
|
||||||
<field name="r_lot_av" width="80"/>
|
<field name="r_lot_av" width="80"/>
|
||||||
<field name="r_lot_quantity" symbol="r_lot_unit_line" width="120" sum="1"/>
|
<field name="r_lot_quantity" symbol="r_lot_unit_line" width="120" sum="1"/>
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="to_location"/>
|
<field name="to_location"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="/form/field[@name='to_location']" position="after">
|
<xpath expr="/form/field[@name='to_location']" position="after">
|
||||||
|
<label name="shipment_type"/>
|
||||||
|
<field name="shipment_type"/>
|
||||||
<label name="transport_type"/>
|
<label name="transport_type"/>
|
||||||
<field name="transport_type"/>
|
<field name="transport_type"/>
|
||||||
<label name="vessel"/>
|
<label name="vessel"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user