Bug fee financing interest
This commit is contained in:
@@ -550,20 +550,17 @@ Owner technique: `a completer`
|
||||
- Priorite:
|
||||
- `importante`
|
||||
|
||||
### BR-PT-016 - Les fees `% rate` utilisent la BL Date estimee
|
||||
### BR-PT-016 - Les fees `% rate` utilisent le delta de financement
|
||||
|
||||
- Intent: aligner le calcul des frais financiers `% rate` entre achat et vente.
|
||||
- Description:
|
||||
- Pour un `fee.fee` en mode `rate`, le calcul ne depend pas du
|
||||
`payment_term`.
|
||||
- La date de fin est toujours issue de l'onglet `Estimated date` de la ligne
|
||||
achat ou vente:
|
||||
- chercher `trigger = bldate`
|
||||
- prendre `estimated_date`
|
||||
- ajouter `fin_int_delta`
|
||||
`payment_term`, ni de la date du jour, ni de `fee_date`.
|
||||
- La periode de calcul est directement le champ `fin_int_delta` de la ligne
|
||||
`Estimated date` avec `trigger = bldate`.
|
||||
- Resultat attendu:
|
||||
- purchase et sale appliquent la meme formule:
|
||||
`fee_date -> BL Date + financing delta`
|
||||
`amount = unit_price * quantity * (price / 100) * fin_int_delta / 360`
|
||||
- si aucune Estimated Date `bldate` n'est renseignee, aucun montant `% rate`
|
||||
n'est calcule
|
||||
- Priorite:
|
||||
|
||||
@@ -19,7 +19,6 @@ import logging
|
||||
from collections import defaultdict
|
||||
from trytond.exceptions import UserWarning, UserError
|
||||
from trytond.modules.account.exceptions import PeriodNotFoundError
|
||||
from trytond.modules.purchase_trade.finance_tools import InterestCalculator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -339,7 +338,6 @@ class Fee(ModelSQL,ModelView):
|
||||
return Decimal(quantity or 0)
|
||||
|
||||
def get_amount(self,name=None):
|
||||
Date = Pool().get('ir.date')
|
||||
sign = Decimal(1)
|
||||
if self.price:
|
||||
# if self.p_r:
|
||||
@@ -350,53 +348,31 @@ class Fee(ModelSQL,ModelView):
|
||||
elif self.mode == 'ppack':
|
||||
return round(self.price * self.quantity,2)
|
||||
elif self.mode == 'rate':
|
||||
#take period with estimated trigger date
|
||||
if self.line:
|
||||
if self.line.estimated_date:
|
||||
beg_date = self.fee_date if self.fee_date else Date.today()
|
||||
est_lines = [dd for dd in self.line.estimated_date if dd.trigger == 'bldate']
|
||||
est_line = est_lines[0] if est_lines else None
|
||||
if est_line and est_line.estimated_date:
|
||||
est_date = est_line.estimated_date + datetime.timedelta(
|
||||
days=est_line.fin_int_delta or 0
|
||||
if est_line and est_line.fin_int_delta:
|
||||
factor = (
|
||||
Decimal(self.price) / Decimal(100)
|
||||
* Decimal(est_line.fin_int_delta) / Decimal(360)
|
||||
)
|
||||
if est_date and beg_date:
|
||||
factor = InterestCalculator.calculate(
|
||||
start_date=beg_date,
|
||||
end_date=est_date,
|
||||
rate=self.price/100,
|
||||
rate_type='annual',
|
||||
convention='ACT/360',
|
||||
compounding='simple'
|
||||
)
|
||||
|
||||
return round(factor * self.line.unit_price * self._get_amount_quantity() * sign,2)
|
||||
return round(factor * self.line.unit_price * self._get_amount_quantity() * sign,2)
|
||||
if self.sale_line:
|
||||
if self.sale_line.estimated_date:
|
||||
beg_date = self.fee_date if self.fee_date else Date.today()
|
||||
est_lines = [
|
||||
dd for dd in self.sale_line.estimated_date
|
||||
if dd.trigger == 'bldate'
|
||||
]
|
||||
est_line = est_lines[0] if est_lines else None
|
||||
if est_line and est_line.estimated_date:
|
||||
est_date = est_line.estimated_date + datetime.timedelta(
|
||||
days=est_line.fin_int_delta or 0
|
||||
if est_line and est_line.fin_int_delta:
|
||||
factor = (
|
||||
Decimal(self.price) / Decimal(100)
|
||||
* Decimal(est_line.fin_int_delta) / Decimal(360)
|
||||
)
|
||||
logger.info("EST_DATE:%s", est_date)
|
||||
if est_date and beg_date:
|
||||
factor = InterestCalculator.calculate(
|
||||
start_date=beg_date,
|
||||
end_date=est_date,
|
||||
rate=self.price/100,
|
||||
rate_type='annual',
|
||||
convention='ACT/360',
|
||||
compounding='simple'
|
||||
)
|
||||
logger.info("FACTOR:%s", factor)
|
||||
return round(
|
||||
factor * self.sale_line.unit_price
|
||||
* self._get_amount_quantity() * sign, 2)
|
||||
return round(
|
||||
factor * self.sale_line.unit_price
|
||||
* self._get_amount_quantity() * sign, 2)
|
||||
|
||||
elif self.mode == 'perqt':
|
||||
if self.shipment_in:
|
||||
|
||||
@@ -243,12 +243,12 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
self.assertEqual(values[0]['amount'], Decimal('0'))
|
||||
|
||||
def test_purchase_rate_fee_amount_uses_virtual_lot_quantity(self):
|
||||
'purchase rate fee amount falls back to the purchase virtual lot quantity'
|
||||
'purchase rate fee amount uses the financing delta as absolute period'
|
||||
Fee = Pool().get('fee.fee')
|
||||
fee = Fee()
|
||||
fee.mode = 'rate'
|
||||
fee.price = Decimal('12')
|
||||
fee.fee_date = datetime.date(2026, 4, 23)
|
||||
fee.fee_date = None
|
||||
fee.quantity = None
|
||||
fee.unit = Mock()
|
||||
fee.shipment_in = None
|
||||
@@ -267,19 +267,15 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
)
|
||||
fee.sale_line = None
|
||||
|
||||
with patch('trytond.modules.purchase_trade.fee.Pool') as PoolMock:
|
||||
PoolMock.return_value.get.return_value = Mock(
|
||||
today=Mock(return_value=datetime.date(2026, 4, 23)))
|
||||
self.assertEqual(fee.get_amount(), Decimal('3.33'))
|
||||
|
||||
self.assertEqual(fee.get_amount(), Decimal('13.33'))
|
||||
|
||||
def test_sale_rate_fee_amount_uses_bl_date_estimate(self):
|
||||
'sale rate fee amount uses BL estimated date plus financing delta'
|
||||
def test_sale_rate_fee_amount_uses_absolute_financing_delta(self):
|
||||
'sale rate fee amount uses the financing delta as absolute period'
|
||||
Fee = Pool().get('fee.fee')
|
||||
fee = Fee()
|
||||
fee.mode = 'rate'
|
||||
fee.price = Decimal('12')
|
||||
fee.fee_date = datetime.date(2026, 4, 23)
|
||||
fee.fee_date = None
|
||||
fee.quantity = Decimal('10')
|
||||
fee.unit = Mock()
|
||||
fee.shipment_in = None
|
||||
@@ -296,11 +292,7 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
],
|
||||
)
|
||||
|
||||
with patch('trytond.modules.purchase_trade.fee.Pool') as PoolMock:
|
||||
PoolMock.return_value.get.return_value = Mock(
|
||||
today=Mock(return_value=datetime.date(2026, 4, 23)))
|
||||
|
||||
self.assertEqual(fee.get_amount(), Decimal('13.33'))
|
||||
self.assertEqual(fee.get_amount(), Decimal('3.33'))
|
||||
|
||||
def test_create_pnl_price_from_line_keeps_finished_physical_sale_line(self):
|
||||
'purchase valuation keeps finished sale-side pnl on physical lots'
|
||||
|
||||
@@ -118,12 +118,12 @@ elle existe, par exemple:
|
||||
- Le calcul du montant d'un fee en mode `rate` est aligne entre purchase et
|
||||
sale.
|
||||
- Il ne depend pas du `payment_term`.
|
||||
- La date de fin vient de l'onglet `Estimated date` de la ligne metier:
|
||||
chercher `trigger = bldate`, prendre `estimated_date`, puis ajouter
|
||||
`fin_int_delta`.
|
||||
- Il ne depend pas de la date du jour ni de `fee_date`.
|
||||
- La periode de financement est directement `fin_int_delta` sur la ligne
|
||||
`Estimated date` avec `trigger = bldate`.
|
||||
- La formule reste en interet simple ACT/360:
|
||||
`amount = rate_factor * unit_price * quantity`, avec
|
||||
`rate_factor = (price / 100) * jours / 360`.
|
||||
`rate_factor = (price / 100) * fin_int_delta / 360`.
|
||||
- Si aucune Estimated Date `bldate` n'est renseignee, le fee `% rate` ne
|
||||
calcule pas de montant.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user