Pricing manual
This commit is contained in:
@@ -418,7 +418,9 @@ Owner technique: `a completer`
|
||||
- `fixed_qt` = cumul des `quantity`
|
||||
- `fixed_qt_price` = moyenne ponderee cumulee des `settl_price`
|
||||
- `unfixed_qt` = quantite de base de la ligne - `fixed_qt`
|
||||
- `unfixed_qt_price` = `settl_price` de la ligne
|
||||
- `unfixed_qt_price` = dernier prix disponible de la courbe du composant
|
||||
quand le composant est lie a une courbe; sinon fallback sur `settl_price`
|
||||
de la ligne
|
||||
- `eod_price` = moyenne ponderee entre jambe fixee et non fixee
|
||||
- `last=True` reste unique par groupe et suit la plus grande `pricing_date`
|
||||
- Hors scope:
|
||||
|
||||
@@ -197,7 +197,7 @@ Source: `BR-PT-016` et doublon historique `BR-PT-015`
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>unfixed_qt_price</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Prix de fallback du solde non fixe.</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Dernier prix disponible de la courbe du composant pour le solde non fixe, avec fallback sur <code>settl_price</code> si aucune courbe exploitable n'est disponible.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>eod_price</code></td>
|
||||
|
||||
@@ -91,7 +91,7 @@ Source: `BR-PT-016` et doublon historique `BR-PT-015`
|
||||
| `fixed_qt` | Cumul des quantites fixees. |
|
||||
| `fixed_qt_price` | Prix moyen pondere du cumul fixe. |
|
||||
| `unfixed_qt` | Solde restant a fixer. |
|
||||
| `unfixed_qt_price` | Prix de fallback du solde non fixe. |
|
||||
| `unfixed_qt_price` | Dernier prix disponible de la courbe du composant pour le solde non fixe, avec fallback sur `settl_price` si aucune courbe exploitable n'est disponible. |
|
||||
| `eod_price` | Prix economique courant de la ligne de fixing. |
|
||||
| `last` | Derniere ligne du groupe, utilisee par le summary. |
|
||||
|
||||
|
||||
@@ -1191,6 +1191,24 @@ class Pricing(ModelSQL,ModelView):
|
||||
quantity = getattr(owner, 'quantity', None)
|
||||
return Decimal(str(quantity or 0))
|
||||
|
||||
@classmethod
|
||||
def _get_manual_unfixed_price(cls, pricing, settl_price):
|
||||
component = getattr(pricing, 'price_component', None)
|
||||
if (not component
|
||||
or getattr(component, 'price_source_type', None) != 'curve'
|
||||
or not getattr(component, 'price_index', None)):
|
||||
return settl_price
|
||||
owner = getattr(pricing, 'sale_line', None) or getattr(pricing, 'line', None)
|
||||
if not owner:
|
||||
return settl_price
|
||||
document = getattr(owner, 'sale', None) or getattr(owner, 'purchase', None)
|
||||
currency = getattr(document, 'currency', None)
|
||||
unit = getattr(owner, 'unit', None)
|
||||
if not currency or not unit:
|
||||
return settl_price
|
||||
return round(Decimal(str(component.get_price(
|
||||
pricing.pricing_date, unit, currency, True) or 0)), 4)
|
||||
|
||||
@classmethod
|
||||
def _sync_manual_values(cls, records):
|
||||
if (not records
|
||||
@@ -1243,7 +1261,8 @@ class Pricing(ModelSQL,ModelView):
|
||||
'fixed_qt': fixed_qt,
|
||||
'fixed_qt_price': fixed_qt_price,
|
||||
'unfixed_qt': unfixed_qt,
|
||||
'unfixed_qt_price': settl_price,
|
||||
'unfixed_qt_price': cls._get_manual_unfixed_price(
|
||||
pricing, settl_price),
|
||||
'last': index == (total - 1),
|
||||
}
|
||||
eod_price = cls._weighted_average_price(
|
||||
|
||||
@@ -2820,6 +2820,45 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
self.assertEqual(second_values['eod_price'], Decimal('106.0000'))
|
||||
self.assertTrue(second_values['last'])
|
||||
|
||||
def test_pricing_sync_manual_values_uses_curve_last_price_for_unfixed(self):
|
||||
'manual pricing rows use the component curve latest price for the unfixed leg'
|
||||
Pricing = Pool().get('pricing.pricing')
|
||||
|
||||
currency = Mock()
|
||||
unit = Mock()
|
||||
sale_line = Mock(
|
||||
id=10,
|
||||
sale=Mock(currency=currency),
|
||||
unit=unit,
|
||||
)
|
||||
sale_line._get_pricing_base_quantity = Mock(return_value=Decimal('10'))
|
||||
component = Mock(
|
||||
id=33,
|
||||
auto=False,
|
||||
price_source_type='curve',
|
||||
price_index=Mock(),
|
||||
)
|
||||
component.get_price.return_value = Decimal('120')
|
||||
pricing = Mock(
|
||||
id=1,
|
||||
price_component=component,
|
||||
sale_line=sale_line,
|
||||
line=None,
|
||||
pricing_date=datetime.date(2026, 4, 10),
|
||||
quantity=Decimal('4'),
|
||||
settl_price=Decimal('100'),
|
||||
)
|
||||
|
||||
with patch.object(Pricing, 'search', return_value=[pricing]), patch(
|
||||
'trytond.modules.purchase_trade.pricing.super') as super_mock:
|
||||
Pricing._sync_manual_values([pricing])
|
||||
|
||||
values = super_mock.return_value.write.call_args.args[1]
|
||||
self.assertEqual(values['unfixed_qt_price'], Decimal('120.0000'))
|
||||
self.assertEqual(values['eod_price'], Decimal('112.0000'))
|
||||
component.get_price.assert_called_once_with(
|
||||
datetime.date(2026, 4, 10), unit, currency, True)
|
||||
|
||||
def test_pricing_trigger_manual_from_to_generates_dates_without_calendar(self):
|
||||
'manual pricing From/To uses calendar days when no calendar is selected'
|
||||
Trigger = Pool().get('pricing.trigger')
|
||||
|
||||
@@ -90,7 +90,8 @@ Scope: templates Relatorio + ponts `report_*` Python.
|
||||
- `fixed_qt` = cumul des `quantity` du groupe.
|
||||
- `fixed_qt_price` = moyenne ponderee cumulee des `settl_price`.
|
||||
- `unfixed_qt` = quantite de base restante a fixer.
|
||||
- `unfixed_qt_price` = `settl_price` de la ligne.
|
||||
- `unfixed_qt_price` = dernier prix disponible de la courbe du composant,
|
||||
fallback `settl_price` si aucune courbe exploitable n'est disponible.
|
||||
- `eod_price` reste non editable et suit le prix moyen pondere.
|
||||
- le mode auto suit la meme formule.
|
||||
- `last` est gere par groupe metier (`line + component`), avec un seul `last=True` par groupe.
|
||||
|
||||
Reference in New Issue
Block a user