Pricing manual

This commit is contained in:
2026-06-11 10:49:05 +02:00
parent 9d13cfd587
commit 52459a267b
6 changed files with 84 additions and 23 deletions

View File

@@ -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:

View File

@@ -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&#x27;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>

View File

@@ -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. |

View File

@@ -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(

View File

@@ -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')

View File

@@ -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.