Delta neg
This commit is contained in:
@@ -482,17 +482,24 @@ class Fee(ModelSQL,ModelView):
|
|||||||
if quantity is None:
|
if quantity is None:
|
||||||
quantity = self.get_quantity()
|
quantity = self.get_quantity()
|
||||||
return Decimal(quantity or 0)
|
return Decimal(quantity or 0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _unsigned_amount(amount):
|
||||||
|
if amount is None:
|
||||||
|
return None
|
||||||
|
return abs(Decimal(amount))
|
||||||
|
|
||||||
def get_amount(self,name=None):
|
def get_amount(self,name=None):
|
||||||
sign = Decimal(1)
|
sign = Decimal(1)
|
||||||
if self.price:
|
if self.price:
|
||||||
# if self.p_r:
|
# if self.p_r:
|
||||||
# if self.p_r == 'pay':
|
# if self.p_r == 'pay':
|
||||||
# sign = -1
|
# sign = -1
|
||||||
if self.mode == 'lumpsum':
|
if self.mode == 'lumpsum':
|
||||||
return self.price * sign
|
return self._unsigned_amount(self.price * sign)
|
||||||
elif self.mode == 'ppack':
|
elif self.mode == 'ppack':
|
||||||
return round(self.price * self.quantity,2)
|
return self._unsigned_amount(
|
||||||
|
round(self.price * self.quantity,2))
|
||||||
elif self.mode == 'rate':
|
elif self.mode == 'rate':
|
||||||
if self.line:
|
if self.line:
|
||||||
if self.line.estimated_date:
|
if self.line.estimated_date:
|
||||||
@@ -503,7 +510,9 @@ class Fee(ModelSQL,ModelView):
|
|||||||
Decimal(self.price) / Decimal(100)
|
Decimal(self.price) / Decimal(100)
|
||||||
* Decimal(est_line.fin_int_delta) / Decimal(360)
|
* Decimal(est_line.fin_int_delta) / Decimal(360)
|
||||||
)
|
)
|
||||||
return round(factor * self.line.unit_price * self._get_amount_quantity() * sign,2)
|
return self._unsigned_amount(round(
|
||||||
|
factor * self.line.unit_price
|
||||||
|
* self._get_amount_quantity() * sign,2))
|
||||||
if self.sale_line:
|
if self.sale_line:
|
||||||
if self.sale_line.estimated_date:
|
if self.sale_line.estimated_date:
|
||||||
est_lines = [
|
est_lines = [
|
||||||
@@ -516,38 +525,38 @@ class Fee(ModelSQL,ModelView):
|
|||||||
Decimal(self.price) / Decimal(100)
|
Decimal(self.price) / Decimal(100)
|
||||||
* Decimal(est_line.fin_int_delta) / Decimal(360)
|
* Decimal(est_line.fin_int_delta) / Decimal(360)
|
||||||
)
|
)
|
||||||
return round(
|
return self._unsigned_amount(round(
|
||||||
factor * self.sale_line.unit_price
|
factor * self.sale_line.unit_price
|
||||||
* self._get_amount_quantity() * sign, 2)
|
* self._get_amount_quantity() * sign, 2))
|
||||||
|
|
||||||
elif self.mode == 'perqt':
|
elif self.mode == 'perqt':
|
||||||
if self.shipment_in:
|
if self.shipment_in:
|
||||||
StockMove = Pool().get('stock.move')
|
StockMove = Pool().get('stock.move')
|
||||||
sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)])
|
sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)])
|
||||||
if sm:
|
if sm:
|
||||||
unique_lots = {e.lot for e in sm if e.lot}
|
unique_lots = {e.lot for e in sm if e.lot}
|
||||||
return round(self.price * Decimal(sum([e.get_current_quantity_converted(0,self.unit) for e in unique_lots])) * sign,2)
|
return self._unsigned_amount(round(self.price * Decimal(sum([e.get_current_quantity_converted(0,self.unit) for e in unique_lots])) * sign,2))
|
||||||
LotQt = Pool().get('lot.qt')
|
LotQt = Pool().get('lot.qt')
|
||||||
lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id])
|
lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id])
|
||||||
if lqts:
|
if lqts:
|
||||||
return round(self.price * Decimal(lqts[0].lot_quantity) * sign,2)
|
return self._unsigned_amount(round(self.price * Decimal(lqts[0].lot_quantity) * sign,2))
|
||||||
|
|
||||||
return round((self.quantity if self.quantity else 0) * self.price * sign,2)
|
return self._unsigned_amount(round((self.quantity if self.quantity else 0) * self.price * sign,2))
|
||||||
elif self.mode == 'pprice':
|
elif self.mode == 'pprice':
|
||||||
if self.line:
|
if self.line:
|
||||||
return round(self.price / 100 * self.line.unit_price * (self.quantity if self.quantity else 0) * sign,2)
|
return self._unsigned_amount(round(self.price / 100 * self.line.unit_price * (self.quantity if self.quantity else 0) * sign,2))
|
||||||
if self.sale_line:
|
if self.sale_line:
|
||||||
return round(self.price / 100 * self.sale_line.unit_price * (self.quantity if self.quantity else 0) * sign,2)
|
return self._unsigned_amount(round(self.price / 100 * self.sale_line.unit_price * (self.quantity if self.quantity else 0) * sign,2))
|
||||||
if self.shipment_in:
|
if self.shipment_in:
|
||||||
StockMove = Pool().get('stock.move')
|
StockMove = Pool().get('stock.move')
|
||||||
sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)])
|
sm = StockMove.search(['shipment','=','stock.shipment.in,'+str(self.shipment_in.id)])
|
||||||
if sm:
|
if sm:
|
||||||
if sm[0].lot:
|
if sm[0].lot:
|
||||||
return round(self.price * Decimal(sum([e.lot.get_lot_price() for e in sm if e.lot])) / 100 * self.quantity * sign,2)
|
return self._unsigned_amount(round(self.price * Decimal(sum([e.lot.get_lot_price() for e in sm if e.lot])) / 100 * self.quantity * sign,2))
|
||||||
LotQt = Pool().get('lot.qt')
|
LotQt = Pool().get('lot.qt')
|
||||||
lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id])
|
lqts = LotQt.search(['lot_shipment_in','=',self.shipment_in.id])
|
||||||
if lqts:
|
if lqts:
|
||||||
return round(self.price * Decimal(lqts[0].lot_p.get_lot_price()) / 100 * lqts[0].lot_quantity * sign,2)
|
return self._unsigned_amount(round(self.price * Decimal(lqts[0].lot_p.get_lot_price()) / 100 * lqts[0].lot_quantity * sign,2))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def copy(cls, fees, default=None):
|
def copy(cls, fees, default=None):
|
||||||
|
|||||||
@@ -416,6 +416,53 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
|||||||
|
|
||||||
self.assertEqual(fee.get_amount(), Decimal('3.33'))
|
self.assertEqual(fee.get_amount(), Decimal('3.33'))
|
||||||
|
|
||||||
|
def test_sale_rate_fee_amount_stays_positive_with_negative_delta(self):
|
||||||
|
'sale rate fee display amount is unsigned even when financing delta is negative'
|
||||||
|
Fee = Pool().get('fee.fee')
|
||||||
|
fee = Fee()
|
||||||
|
fee.mode = 'rate'
|
||||||
|
fee.price = Decimal('12')
|
||||||
|
fee.fee_date = None
|
||||||
|
fee.quantity = Decimal('10')
|
||||||
|
fee.unit = Mock()
|
||||||
|
fee.shipment_in = None
|
||||||
|
fee.line = None
|
||||||
|
fee.sale_line = Mock(
|
||||||
|
unit_price=Decimal('100'),
|
||||||
|
lots=[],
|
||||||
|
estimated_date=[
|
||||||
|
Mock(
|
||||||
|
trigger='bldate',
|
||||||
|
estimated_date=datetime.date(2026, 5, 23),
|
||||||
|
fin_int_delta=-10,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(fee.get_amount(), Decimal('3.33'))
|
||||||
|
|
||||||
|
def test_fee_pnl_applies_pay_sign_to_unsigned_rate_amount(self):
|
||||||
|
'fee pnl sign is applied once from PAY/REC'
|
||||||
|
Valuation = Pool().get('valuation.valuation')
|
||||||
|
lot = Mock()
|
||||||
|
lot.get_current_quantity_converted.return_value = Decimal('10')
|
||||||
|
fee = Mock(
|
||||||
|
mode='rate',
|
||||||
|
price=Decimal('12'),
|
||||||
|
unit=Mock(),
|
||||||
|
line=None,
|
||||||
|
sale_line=Mock(
|
||||||
|
unit_price=Decimal('100'),
|
||||||
|
estimated_date=[
|
||||||
|
Mock(trigger='bldate', fin_int_delta=-10),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
Valuation._fee_amount_for_lot_or_zero(fee, lot),
|
||||||
|
Decimal('3.33'))
|
||||||
|
|
||||||
def test_create_pnl_price_from_line_keeps_finished_physical_sale_line(self):
|
def test_create_pnl_price_from_line_keeps_finished_physical_sale_line(self):
|
||||||
'purchase valuation keeps finished sale-side pnl on physical lots'
|
'purchase valuation keeps finished sale-side pnl on physical lots'
|
||||||
Valuation = Pool().get('valuation.valuation')
|
Valuation = Pool().get('valuation.valuation')
|
||||||
|
|||||||
@@ -811,15 +811,15 @@ class ValuationBase(ModelSQL):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _fee_amount_or_zero(cls, fee):
|
def _fee_amount_or_zero(cls, fee):
|
||||||
amount = fee.get_amount() if hasattr(fee, 'get_amount') else fee.amount
|
amount = fee.get_amount() if hasattr(fee, 'get_amount') else fee.amount
|
||||||
return Decimal(amount or 0)
|
return abs(Decimal(amount or 0))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _fee_amount_for_lot_or_zero(cls, fee, lot):
|
def _fee_amount_for_lot_or_zero(cls, fee, lot):
|
||||||
if fee.mode == 'ppack' and getattr(lot, 'lot_qt', None):
|
if fee.mode == 'ppack' and getattr(lot, 'lot_qt', None):
|
||||||
return round(
|
return abs(round(
|
||||||
Decimal(fee.price or 0)
|
Decimal(fee.price or 0)
|
||||||
* Decimal(str(lot.lot_qt or 0)),
|
* Decimal(str(lot.lot_qt or 0)),
|
||||||
2)
|
2))
|
||||||
if fee.mode == 'rate':
|
if fee.mode == 'rate':
|
||||||
line = fee.line or getattr(fee, 'sale_line', None)
|
line = fee.line or getattr(fee, 'sale_line', None)
|
||||||
if line and line.estimated_date:
|
if line and line.estimated_date:
|
||||||
@@ -833,9 +833,9 @@ class ValuationBase(ModelSQL):
|
|||||||
* Decimal(est_line.fin_int_delta) / Decimal(360))
|
* Decimal(est_line.fin_int_delta) / Decimal(360))
|
||||||
quantity = Decimal(
|
quantity = Decimal(
|
||||||
lot.get_current_quantity_converted(0, fee.unit) or 0)
|
lot.get_current_quantity_converted(0, fee.unit) or 0)
|
||||||
return round(
|
return abs(round(
|
||||||
factor * Decimal(line.unit_price or 0) * quantity,
|
factor * Decimal(line.unit_price or 0) * quantity,
|
||||||
2)
|
2))
|
||||||
return Decimal(0)
|
return Decimal(0)
|
||||||
return cls._fee_amount_or_zero(fee)
|
return cls._fee_amount_or_zero(fee)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
<field name="to_location"/>
|
<field name="to_location"/>
|
||||||
<field name="payment_term"/>
|
<field name="payment_term"/>
|
||||||
<field name="incoterm"/>
|
<field name="incoterm"/>
|
||||||
<field name="crop"/>
|
|
||||||
<field name="del_period"/>
|
<field name="del_period"/>
|
||||||
<field name="category" tree_invisible="1"/>
|
<field name="category" tree_invisible="1"/>
|
||||||
<field name="company_visible" tree_invisible="1"/>
|
<field name="company_visible" tree_invisible="1"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user