Pnl shipment
This commit is contained in:
@@ -2,10 +2,6 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='warehouse']" position="after">
|
||||
<label name="carrier"/>
|
||||
<field name="carrier"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@id='other']" position="before">
|
||||
<page id="costs" string="Costs">
|
||||
<label name="cost_used"/>
|
||||
|
||||
@@ -1969,11 +1969,12 @@ class LotQt(
|
||||
AvQt.as_('r_lot_quantity'),
|
||||
MaQt.as_('r_lot_matched'),
|
||||
Case((lp.id>0, lp.lot_product),else_=ls.lot_product).as_('r_lot_product'),
|
||||
Case((lp.id>0, lp.lot_type),else_=ls.lot_type).as_('r_lot_type'),
|
||||
lqt.lot_shipment_in.as_('r_lot_shipment_in'),
|
||||
lqt.lot_shipment_out.as_('r_lot_shipment_out'),
|
||||
lqt.lot_shipment_internal.as_('r_lot_shipment_internal'),
|
||||
lqt.lot_move.as_('r_lot_move'),
|
||||
Case((lp.id>0, lp.lot_type),else_=ls.lot_type).as_('r_lot_type'),
|
||||
lqt.lot_shipment_in.as_('r_lot_shipment_in'),
|
||||
lqt.lot_shipment_out.as_('r_lot_shipment_out'),
|
||||
lqt.lot_shipment_internal.as_('r_lot_shipment_internal'),
|
||||
si.vessel.as_('r_vessel'),
|
||||
lqt.lot_move.as_('r_lot_move'),
|
||||
lqt.lot_status.as_('r_lot_status'),
|
||||
Case((lp.lot_type=='physic',lp.lot_av),else_=lqt.lot_av).as_('r_lot_av'),
|
||||
Case((lp.id>0, lp.lot_premium),else_=ls.lot_premium).as_('r_lot_premium'),
|
||||
@@ -2115,11 +2116,12 @@ class LotQt(
|
||||
AvQt2.as_("r_lot_quantity"),
|
||||
MaQt2.as_("r_lot_matched"),
|
||||
lp.lot_product.as_("r_lot_product"),
|
||||
lp.lot_type.as_("r_lot_type"),
|
||||
lp.lot_shipment_in.as_("r_lot_shipment_in"),
|
||||
lp.lot_shipment_out.as_('r_lot_shipment_out'),
|
||||
lp.lot_shipment_internal.as_('r_lot_shipment_internal'),
|
||||
lp.move.as_("r_lot_move"),
|
||||
lp.lot_type.as_("r_lot_type"),
|
||||
lp.lot_shipment_in.as_("r_lot_shipment_in"),
|
||||
lp.lot_shipment_out.as_('r_lot_shipment_out'),
|
||||
lp.lot_shipment_internal.as_('r_lot_shipment_internal'),
|
||||
si.vessel.as_("r_vessel"),
|
||||
lp.move.as_("r_lot_move"),
|
||||
lp.lot_status.as_("r_lot_status"),
|
||||
lp.lot_av.as_("r_lot_av"),
|
||||
lp.lot_premium.as_("r_lot_premium"),
|
||||
@@ -2182,12 +2184,13 @@ class LotQt(
|
||||
Max(lp.lot_unit).as_("r_lot_unit"),
|
||||
Sum(AvQt2).as_("r_lot_quantity"),
|
||||
Sum(MaQt2).as_("r_lot_matched"),
|
||||
Max(lp.lot_product).as_("r_lot_product"),
|
||||
Max(lp.lot_type).as_("r_lot_type"),
|
||||
lp.lot_shipment_in.as_("r_lot_shipment_in"),
|
||||
lp.lot_shipment_internal.as_('r_lot_shipment_internal'),
|
||||
lp.lot_shipment_out.as_('r_lot_shipment_out'),
|
||||
Max(lp.move).as_("r_lot_move"),
|
||||
Max(lp.lot_product).as_("r_lot_product"),
|
||||
Max(lp.lot_type).as_("r_lot_type"),
|
||||
lp.lot_shipment_in.as_("r_lot_shipment_in"),
|
||||
lp.lot_shipment_out.as_('r_lot_shipment_out'),
|
||||
lp.lot_shipment_internal.as_('r_lot_shipment_internal'),
|
||||
Max(si.vessel).as_("r_vessel"),
|
||||
Max(lp.move).as_("r_lot_move"),
|
||||
Max(lp.lot_status).as_("r_lot_status"),
|
||||
Max(lp.lot_av).as_("r_lot_av"),
|
||||
Avg(lp.lot_premium).as_("r_lot_premium"),
|
||||
@@ -2240,10 +2243,11 @@ class LotQt(
|
||||
union.r_lot_matched.as_("r_lot_matched"),
|
||||
union.r_lot_product.as_("r_lot_product"),
|
||||
union.r_lot_type.as_("r_lot_type"),
|
||||
union.r_lot_shipment_in.as_("r_lot_shipment_in"),
|
||||
union.r_lot_shipment_out.as_('r_lot_shipment_out'),
|
||||
union.r_lot_shipment_internal.as_('r_lot_shipment_internal'),
|
||||
union.r_lot_move.as_("r_lot_move"),
|
||||
union.r_lot_shipment_in.as_("r_lot_shipment_in"),
|
||||
union.r_lot_shipment_out.as_('r_lot_shipment_out'),
|
||||
union.r_lot_shipment_internal.as_('r_lot_shipment_internal'),
|
||||
union.r_vessel.as_("r_vessel"),
|
||||
union.r_lot_move.as_("r_lot_move"),
|
||||
union.r_lot_status.as_("r_lot_status"),
|
||||
union.r_lot_av.as_("r_lot_av"),
|
||||
union.r_lot_premium.as_("r_lot_premium"),
|
||||
@@ -2320,10 +2324,11 @@ class LotReport(
|
||||
r_lot_matched = fields.Numeric("Matched Qt",digits='r_lot_unit_line')
|
||||
r_lot_premium = fields.Numeric("Prem/Disc", digits='r_lot_unit_line')
|
||||
r_lot_premium_sale = fields.Numeric("Prem/Disc", digits='r_lot_unit_line')
|
||||
r_lot_shipment_in = fields.Many2One('stock.shipment.in', "Shipment In")
|
||||
r_lot_shipment_out = fields.Many2One('stock.shipment.out', "Shipment Out")
|
||||
r_lot_shipment_internal = fields.Many2One('stock.shipment.internal', "Shipment Internal")
|
||||
r_lot_move = fields.Many2One('stock.move', "Move")
|
||||
r_lot_shipment_in = fields.Many2One('stock.shipment.in', "Shipment In")
|
||||
r_lot_shipment_out = fields.Many2One('stock.shipment.out', "Shipment Out")
|
||||
r_lot_shipment_internal = fields.Many2One('stock.shipment.internal', "Shipment Internal")
|
||||
r_vessel = fields.Many2One('trade.vessel', "Vessel")
|
||||
r_lot_move = fields.Many2One('stock.move', "Move")
|
||||
r_lot_parent = fields.Many2One('lot.lot',"Parent")
|
||||
r_lot_himself = fields.Many2One('lot.lot',"Lot")
|
||||
r_lot_container = fields.Char("Container")
|
||||
|
||||
@@ -686,11 +686,14 @@ class ShipmentIn(metaclass=PoolMeta):
|
||||
], 'Transport type')
|
||||
vessel = fields.Many2One('trade.vessel', "Vessel",
|
||||
states={'invisible': Eval('transport_type') == 'truck'})
|
||||
info = fields.Function(fields.Text("Info",states={'invisible': ~Eval('info',False)}),'get_info')
|
||||
anim = fields.Function(fields.Text(""),'get_anim')
|
||||
carte = fields.Function(fields.Text("",states={'invisible': ~Eval('info',False)}),'get_imo')
|
||||
fees = fields.One2Many('fee.fee','shipment_in',"Fees")
|
||||
lotqt = fields.One2Many('lot.qt','lot_shipment_in',"Lots")
|
||||
info = fields.Function(fields.Text("Info",states={'invisible': ~Eval('info',False)}),'get_info')
|
||||
anim = fields.Function(fields.Text(""),'get_anim')
|
||||
carte = fields.Function(fields.Text("",states={'invisible': ~Eval('info',False)}),'get_imo')
|
||||
fees = fields.One2Many('fee.fee','shipment_in',"Fees")
|
||||
pnl_lines = fields.Function(
|
||||
fields.One2Many('valuation.valuation.line', '', "Pnl"),
|
||||
'get_pnl_lines')
|
||||
lotqt = fields.One2Many('lot.qt','lot_shipment_in',"Lots")
|
||||
quantity = fields.Function(fields.Numeric("Quantity"),'get_quantity')
|
||||
unit = fields.Function(fields.Many2One('product.uom',"Unit"),'get_unit')
|
||||
bl_date = fields.Date("BL date")
|
||||
@@ -720,9 +723,12 @@ class ShipmentIn(metaclass=PoolMeta):
|
||||
sof = fields.One2Many('sof.statement', 'shipment',"Demurrage calculations")
|
||||
del_from = fields.Date("Delivery period from")
|
||||
del_to = fields.Date("to")
|
||||
estimated_date = fields.One2Many('pricing.estimated','shipment_in',"Estimated date")
|
||||
carrier_ = fields.Many2One('party.party',"Carrier")
|
||||
start_date = fields.Date("Start date")
|
||||
estimated_date = fields.One2Many('pricing.estimated','shipment_in',"Estimated date")
|
||||
carrier_ = fields.Many2One('party.party',"Carrier")
|
||||
operator = fields.Many2One(
|
||||
'party.party', "Operator",
|
||||
domain=[('categories.name', '=', 'OPERATOR')])
|
||||
start_date = fields.Date("Start date")
|
||||
travel_nb = fields.Char("Travel nb")
|
||||
receive_date = fields.Date("Reception date")
|
||||
receive_nb = fields.Char("Reception nb")
|
||||
@@ -1758,6 +1764,34 @@ class ShipmentIn(metaclass=PoolMeta):
|
||||
if self.lotqt:
|
||||
return sum([(e.lot_quantity if e.lot_quantity else 0) for e in self.lotqt])
|
||||
|
||||
def _pnl_lot_ids(self):
|
||||
lot_ids = []
|
||||
seen = set()
|
||||
|
||||
def add(lot):
|
||||
lot_id = getattr(lot, 'id', lot)
|
||||
if not lot_id or lot_id in seen:
|
||||
return
|
||||
seen.add(lot_id)
|
||||
lot_ids.append(lot_id)
|
||||
|
||||
for move in self.incoming_moves or []:
|
||||
add(getattr(move, 'lot', None))
|
||||
for lotqt in self.lotqt or []:
|
||||
add(getattr(lotqt, 'lot_p', None))
|
||||
add(getattr(lotqt, 'lot_s', None))
|
||||
return lot_ids
|
||||
|
||||
def get_pnl_lines(self, name=None):
|
||||
lot_ids = self._pnl_lot_ids()
|
||||
if not lot_ids:
|
||||
return []
|
||||
ValuationLine = Pool().get('valuation.valuation.line')
|
||||
lines = ValuationLine.search(
|
||||
[('lot', 'in', lot_ids)],
|
||||
order=[('date', 'DESC'), ('id', 'DESC')])
|
||||
return [line.id for line in lines]
|
||||
|
||||
@staticmethod
|
||||
def _unique_condition_ids(conditions):
|
||||
ids = []
|
||||
|
||||
@@ -2408,6 +2408,7 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
'sale and purchase trader/operator fields are filtered by TRADER/OPERATOR categories'
|
||||
Sale = Pool().get('sale.sale')
|
||||
Purchase = Pool().get('purchase.purchase')
|
||||
ShipmentIn = Pool().get('stock.shipment.in')
|
||||
|
||||
self.assertEqual(
|
||||
Sale.trader.domain, [('categories.name', '=', 'TRADER')])
|
||||
@@ -2417,6 +2418,34 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
Purchase.trader.domain, [('categories.name', '=', 'TRADER')])
|
||||
self.assertEqual(
|
||||
Purchase.operator.domain, [('categories.name', '=', 'OPERATOR')])
|
||||
self.assertEqual(
|
||||
ShipmentIn.operator.domain, [('categories.name', '=', 'OPERATOR')])
|
||||
|
||||
def test_shipment_in_pnl_lines_use_physical_and_open_lots(self):
|
||||
'shipment PnL uses valuation lines from physical and open lot links'
|
||||
ShipmentIn = Pool().get('stock.shipment.in')
|
||||
shipment = ShipmentIn()
|
||||
physical_lot = Mock(id=1)
|
||||
open_purchase_lot = Mock(id=2)
|
||||
open_sale_lot = Mock(id=3)
|
||||
shipment.incoming_moves = [
|
||||
Mock(lot=physical_lot),
|
||||
Mock(lot=None),
|
||||
]
|
||||
shipment.lotqt = [
|
||||
Mock(lot_p=open_purchase_lot, lot_s=open_sale_lot),
|
||||
Mock(lot_p=open_purchase_lot, lot_s=None),
|
||||
]
|
||||
valuation_model = Mock()
|
||||
valuation_model.search.return_value = [Mock(id=10), Mock(id=11)]
|
||||
|
||||
with patch('trytond.modules.purchase_trade.stock.Pool') as PoolMock:
|
||||
PoolMock.return_value.get.return_value = valuation_model
|
||||
self.assertEqual(shipment.get_pnl_lines(None), [10, 11])
|
||||
|
||||
valuation_model.search.assert_called_once_with(
|
||||
[('lot', 'in', [1, 2, 3])],
|
||||
order=[('date', 'DESC'), ('id', 'DESC')])
|
||||
|
||||
def test_sale_and_purchase_tolerance_option_defaults_to_empty(self):
|
||||
'sale and purchase tolerance option defaults to empty'
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<field name="r_shipment_origin" width="160">
|
||||
<prefix name="sh_icon"/>
|
||||
</field>
|
||||
<field name="r_vessel" width="130" optional="1"/>
|
||||
<field name="r_shipping_status" widget="badge" string="Shipping status" badge_colors="shipped:#16a34a,unshipped:#ef4444,scheduled:#f59e0b" width="120"/>
|
||||
<field name="r_lot_status"/>
|
||||
<field name="r_lot_av" width="80" optional="1" tree_invisible="1"/>
|
||||
|
||||
@@ -31,6 +31,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="vessel_type"/>
|
||||
<label name="cargo_mode"/>
|
||||
<field name="cargo_mode"/>
|
||||
<label name="operator"/>
|
||||
<field name="operator"/>
|
||||
<newline/>
|
||||
<label name="del_from" />
|
||||
<field name="del_from"/>
|
||||
@@ -80,6 +82,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<page string="Fees" col="4" id="fees">
|
||||
<field name="fees" colspan="4" mode="tree,form" view_ids="purchase_trade.fee_view_tree_sequence2,purchase_trade.fee_view_form"/>
|
||||
</page>
|
||||
<page string="Pnl" col="4" id="pnl">
|
||||
<field name="pnl_lines" colspan="4" mode="tree" view_ids="purchase_trade.valuation_view_tree_sequence3"/>
|
||||
</page>
|
||||
<page string="Estimated dates" col="4" id="estimated">
|
||||
<field name="estimated_date" />
|
||||
</page>
|
||||
|
||||
Reference in New Issue
Block a user