This commit is contained in:
2026-01-04 14:49:22 +01:00
parent 02b99b4622
commit dee3d2ff90
4 changed files with 21 additions and 408 deletions

View File

@@ -94,7 +94,8 @@ def register():
fee.Fee,
fee.FeeLots,
purchase.FeeLots,
valuation.Valuation,
valuation.Valuation,
valuation.ValuationLine,
valuation.ValuationDyn,
derivative.Derivative,
derivative.DerivativeMatch,

View File

@@ -252,21 +252,12 @@ class Purchase(metaclass=PoolMeta):
broker = fields.Many2One('party.party',"Broker",domain=[('categories.parent', 'child_of', [4])])
tol_min = fields.Numeric("Tol - in %")
tol_max = fields.Numeric("Tol + in %")
# certification = fields.Selection([
# (None, ''),
# ('bci', 'BCI'),
# ],"Certification")
certif = fields.Many2One('purchase.certification',"Certification")
wb = fields.Many2One('purchase.weight.basis',"Weight basis")
association = fields.Many2One('purchase.association',"Association")
crop = fields.Many2One('purchase.crop',"Crop")
# weight_basis = fields.Selection([
# (None, ''),
# ('ncsw', 'NCSW'),
# ('nlw', 'NLW'),
# ], 'Weight basis')
pnl = fields.One2Many('valuation.valuation.dyn', 'r_purchase', 'Pnl',states={'invisible': ~Eval('group_pnl'),})
pnl_ = fields.One2Many('valuation.valuation', 'purchase', 'Pnl',states={'invisible': Eval('group_pnl'),})
pnl_ = fields.One2Many('valuation.valuation.line', 'purchase', 'Pnl',states={'invisible': Eval('group_pnl'),})
derivatives = fields.One2Many('derivative.derivative', 'purchase', 'Derivative')
plans = fields.One2Many('workflow.plan','purchase',"Execution plans")
forex = fields.One2Many('forex.cover.physical.contract','contract',"Forex",readonly=True)
@@ -647,358 +638,6 @@ class Line(metaclass=PoolMeta):
f.purchase = line.purchase
Fee.save([f])
def get_pnl_der_lines(self):
der_lines = []
if self.derivatives:
Pnl = Pool().get('valuation.valuation')
Date = Pool().get('ir.date')
for d in self.derivatives:
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.line = self.id
pnl.type = 'derivative'
pnl.date = Date.today()
pnl.reference = d.price_index.price_index
pnl.price = round(Decimal(d.price_index.get_price_per_qt(d.price,self.unit,self.purchase.currency)),4)
pnl.counterparty = d.party
pnl.product = d.product
pnl.state = 'fixed'
pnl.amount = round(pnl.price * (d.quantity) * Decimal(-1),4)
mtm = round(Decimal(d.price_index.get_price(Date.today(),self.unit,self.purchase.currency,True)) * d.quantity * Decimal(-1),4)
pnl.mtm = pnl.amount - mtm
pnl.quantity = round(d.quantity,5)
pnl.unit = self.unit
pnl.currency = self.purchase.currency
der_lines.append(pnl)
return der_lines
def get_pnl_price_lines(self):
price_lines = []
Pnl = Pool().get('valuation.valuation')
LotQt = Pool().get('lot.qt')
Date = Pool().get('ir.date')
for lot in self.lots:
logger.info("FROM_VALUATION_TYPE:%s",self.price_type)
if self.price_type == 'basis' and self.price_summary:
for pc in self.price_summary:
#pnl management
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.line = self.id
pnl.type = 'pur. priced'
pnl.date = Date.today()
pnl.lot = lot.id
if lot.sale_line:
pnl.sale = lot.sale_line.sale.id
pnl.reference = pc.get_name() + ' / ' + str(pc.ratio) + '%'
pnl.price = round(pc.price,4)
pnl.counterparty = self.purchase.party
pnl.product = self.product
if pc.unfixed_qt == 0:
pnl.state = 'fixed'
elif pc.fixed_qt == 0:
pnl.state = 'unfixed'
else:
pnl.state = 'part. fixed' + ' ' + str(round(pc.fixed_qt / Decimal(self.quantity_theorical) * 100,0)) + '%'
if pc.price and pc.ratio:
#pnl.amount = round(pc.price * (pc.unfixed_qt + pc.fixed_qt) * Decimal(-1) * pc.ratio / 100,2)
pnl.amount = round(pc.price * lot.get_current_quantity_converted() * Decimal(-1) * pc.ratio / 100,4)
last_price = pc.get_last_price()
mtm = Decimal(0)
if last_price:
mtm = round(Decimal(last_price) * lot.get_current_quantity_converted() * Decimal(-1),4)
pnl.mtm = round(pnl.amount - (mtm * pc.ratio / 100),4)
pnl.quantity = round(lot.get_current_quantity_converted(),5)
pnl.unit = self.unit
pnl.currency = self.purchase.currency
price_lines.append(pnl)
elif self.price_type == 'priced':
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.line = self.id
pnl.type = 'pur. priced'
pnl.date = Date.today()
pnl.lot = lot.id
if lot.sale_line:
pnl.sale = lot.sale_line.sale.id
if lot.lot_type == 'physic':
pnl.reference = 'Purchase/Physic'
else:
pnl.reference = 'Purchase/Open'
if lot.lot_price:
pnl.price = round(lot.lot_price,4)
pnl.counterparty = self.purchase.party
pnl.product = self.product
pnl.state = 'fixed'
mtm = Decimal(0)
pnl.mtm = mtm
pnl.quantity = round(lot.get_current_quantity_converted(),5)
pnl.amount = round(pnl.price * pnl.quantity * Decimal(-1),4)
pnl.unit = self.unit
pnl.currency = self.purchase.currency
price_lines.append(pnl)
elif self.price_type == 'efp':
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.line = self.id
pnl.type = 'pur. efp'
pnl.date = Date.today()
pnl.lot = lot.id
if lot.sale_line:
pnl.sale = lot.sale_line.sale.id
if lot.lot_type == 'physic':
pnl.reference = 'Purchase/Physic'
else:
pnl.reference = 'Purchase/Open'
if lot.lot_price:
pnl.price = round(lot.lot_price,4)
pnl.counterparty = self.purchase.party
pnl.product = self.product
pnl.state = 'not fixed'
mtm = Decimal(0)
pnl.mtm = mtm
pnl.quantity = round(lot.get_current_quantity_converted(),5)
pnl.amount = round(pnl.price * pnl.quantity * Decimal(-1),4)
pnl.unit = self.unit
pnl.currency = self.purchase.currency
price_lines.append(pnl)
if lot.sale_line:
sl = lot
if sl.sale_line.price_type == 'basis' and sl.sale_line.price_summary:
for pc in sl.sale_line.price_summary:
#pnl management
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.sale = sl.sale_line.sale.id
pnl.line = self.id
pnl.type = 'sale priced'
pnl.date = Date.today()
pnl.lot = lot.id
pnl.reference = pc.get_name() + ' / ' + str(pc.ratio) + '%'
pnl.price = round(pc.price,4)
pnl.counterparty = sl.sale_line.sale.party
pnl.product = sl.sale_line.product
if pc.unfixed_qt == 0:
pnl.state = 'fixed'
elif pc.fixed_qt == 0:
pnl.state = 'unfixed'
else:
pnl.state = 'part. fixed' + ' ' + str(round(pc.fixed_qt / Decimal(sl.sale_line.quantity_theorical) * 100,0)) + '%'
if pc.price and pc.ratio:
#pnl.amount = round(pc.price * (pc.unfixed_qt + pc.fixed_qt) * Decimal(-1) * pc.ratio / 100,2)
pnl.amount = round(pc.price * sl.get_current_quantity_converted() * pc.ratio / 100,4)
last_price = pc.get_last_price()
mtm = Decimal(0)
if last_price:
mtm = round(Decimal(last_price) * sl.get_current_quantity_converted(),4)
pnl.mtm = round(pnl.amount - (mtm * pc.ratio / 100),4)
pnl.quantity = round(sl.get_current_quantity_converted(),5)
pnl.unit = sl.sale_line.unit
pnl.currency = sl.sale_line.sale.currency
price_lines.append(pnl)
elif sl.sale_line.price_type == 'priced':
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.sale = sl.sale_line.sale.id
pnl.line = self.id
pnl.type = 'sale priced'
pnl.date = Date.today()
pnl.lot = lot.id
if lot.lot_type == 'physic':
pnl.reference = 'Sale/Physic'
else:
pnl.reference = 'Sale/Open'
pnl.price = round(lot.lot_price_sale,4)
pnl.counterparty = sl.sale_line.sale.party
pnl.product = self.product
pnl.state = 'fixed'
mtm = Decimal(0)
pnl.mtm = mtm
pnl.quantity = round(lot.get_current_quantity_converted(),5)
pnl.amount = round(pnl.price * pnl.quantity,4)
pnl.unit = self.unit
pnl.currency = self.purchase.currency
price_lines.append(pnl)
elif sl.sale_line.price_type == 'efp':
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.sale = sl.sale_line.sale.id
pnl.line = self.id
pnl.type = 'sale efp'
pnl.date = Date.today()
pnl.lot = lot.id
if lot.lot_type == 'physic':
pnl.reference = 'Sale/Physic'
else:
pnl.reference = 'Sale/Open'
pnl.price = round(lot.lot_price_sale,4)
pnl.counterparty = sl.sale_line.sale.party
pnl.product = self.product
pnl.state = 'not fixed'
mtm = Decimal(0)
pnl.mtm = mtm
pnl.quantity = round(lot.get_current_quantity_converted(),5)
pnl.amount = round(pnl.price * pnl.quantity,4)
pnl.unit = self.unit
pnl.currency = self.purchase.currency
price_lines.append(pnl)
lqts = LotQt.search([('lot_p','=',lot.id),('lot_s','>',0),('lot_quantity','>',0)])
logger.info("FROM_VALUATION:%s",lqts)
logger.info("FROM_VALUATION2:%s",lot.sale_line)
if lqts and not lot.sale_line:
for lqt in lqts:
sl = lqt.lot_s
if sl.sale_line.price_type == 'basis' and sl.sale_line.price_summary:
for pc in sl.sale_line.price_summary:
#pnl management
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.sale = sl.sale_line.sale.id
pnl.line = self.id
pnl.lot = sl.id
pnl.type = 'sale priced'
pnl.date = Date.today()
pnl.reference = pc.get_name() + ' / ' + str(pc.ratio) + '%'
pnl.price = round(pc.price,4)
pnl.counterparty = sl.sale_line.sale.party
pnl.product = sl.sale_line.product
if pc.unfixed_qt == 0:
pnl.state = 'fixed'
elif pc.fixed_qt == 0:
pnl.state = 'unfixed'
else:
pnl.state = 'part. fixed' + ' ' + str(round(pc.fixed_qt / Decimal(sl.sale_line.quantity_theorical) * 100,0)) + '%'
if pc.price and pc.ratio:
#pnl.amount = round(pc.price * (pc.unfixed_qt + pc.fixed_qt) * Decimal(-1) * pc.ratio / 100,2)
pnl.amount = round(pc.price * sl.get_current_quantity_converted() * pc.ratio / 100,4)
last_price = pc.get_last_price()
mtm = Decimal(0)
if last_price:
mtm = round(Decimal(last_price) * sl.get_current_quantity_converted(),4)
pnl.mtm = round(pnl.amount - (mtm * pc.ratio / 100),4)
pnl.quantity = round(sl.get_current_quantity_converted(),5)
pnl.unit = sl.sale_line.unit
pnl.currency = sl.sale_line.sale.currency
price_lines.append(pnl)
elif sl.sale_line.price_type == 'priced':
logger.info("FROM_VALUATION3:%s",sl)
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.sale = sl.sale_line.sale.id
pnl.line = self.id
pnl.type = 'sale priced'
pnl.date = Date.today()
pnl.lot = sl.id
if sl.lot_type == 'physic':
pnl.reference = 'Sale/Physic'
else:
pnl.reference = 'Sale/Open'
pnl.price = round(sl.lot_price_sale,4)
pnl.counterparty = sl.sale_line.sale.party
pnl.product = self.product
pnl.state = 'fixed'
mtm = Decimal(0)
pnl.mtm = mtm
pnl.quantity = round(sl.get_current_quantity_converted(),5)
pnl.amount = round(pnl.price * pnl.quantity,4)
pnl.unit = self.unit
pnl.currency = self.purchase.currency
price_lines.append(pnl)
elif sl.sale_line.price_type == 'efp':
logger.info("FROM_VALUATION3:%s",sl)
pnl = Pnl()
pnl.purchase = self.purchase.id
pnl.sale = sl.sale_line.sale.id
pnl.line = self.id
pnl.type = 'sale efp'
pnl.date = Date.today()
pnl.lot = sl.id
if sl.lot_type == 'physic':
pnl.reference = 'Sale/Physic'
else:
pnl.reference = 'Sale/Open'
pnl.price = round(sl.lot_price_sale,4)
pnl.counterparty = sl.sale_line.sale.party
pnl.product = self.product
pnl.state = 'not fixed'
mtm = Decimal(0)
pnl.mtm = mtm
pnl.quantity = round(sl.get_current_quantity_converted(),5)
pnl.amount = round(pnl.price * pnl.quantity,4)
pnl.unit = self.unit
pnl.currency = self.purchase.currency
price_lines.append(pnl)
return price_lines
def group_fees_by_type_supplier(self,fees):
grouped = defaultdict(list)
# Regrouper par (type, supplier)
for fee in fees:
key = (fee.product, fee.supplier)
grouped[key].append(fee)
result = []
for key, fee_list in grouped.items():
ordered_fees = [f for f in fee_list if f.type == 'ordered']
if ordered_fees:
result.extend(ordered_fees)
else:
budgeted_fees = [f for f in fee_list if f.type == 'budgeted']
result.extend(budgeted_fees)
return result
def get_pnl_fee_lines(self):
fee_lines = []
#pnl management
Pnl = Pool().get('valuation.valuation')
Date = Pool().get('ir.date')
Currency = Pool().get('currency.currency')
FeeLots = Pool().get('fee.lots')
if self.lots:
for lot in self.lots:
fl = FeeLots.search(['lot','=',lot.id])
if fl:
fees = [e.fee for e in fl]
sorted_fees = self.group_fees_by_type_supplier(fees)
if sorted_fees:
for sf in sorted_fees:
pnl = Pnl()
pnl.lot = lot.id
if lot.sale_line:
pnl.sale = lot.sale_line.sale.id
pnl.purchase = self.purchase.id
pnl.line = self.id
if sf.line:
pnl.type = 'pur. fee'
if sf.sale_line:
pnl.type = 'sale fee'
if sf.shipment_in:
pnl.type = 'shipment fee'
pnl.date = Date.today()
pnl.price = Decimal(sf.get_price_per_qt())
if sf.currency != self.purchase.currency:
with Transaction().set_context(date=Date.today()):
pnl.price = Currency.compute(sf.currency,pnl.price, self.purchase.currency)
pnl.counterparty = sf.supplier
str_op = ''
if lot.lot_type == 'physic':
str_op = '/Physic'
else:
str_op = '/Open'
pnl.reference = sf.product.name + str_op
pnl.product = sf.product
pnl.state = sf.type
if sf.p_r == 'pay':
sign = -1
pnl.amount = round(pnl.price * lot.get_current_quantity_converted() * sign,2)
pnl.mtm = 0
pnl.quantity = round(lot.get_current_quantity_converted(),5)
pnl.unit = sf.unit if sf.unit else self.unit
pnl.currency = sf.currency
fee_lines.append(pnl)
return fee_lines
def check_from_to(self,tr):
if tr.pricing_period:
date_from,date_to, d, include, dates = tr.getDateWithEstTrigger(1)

View File

@@ -324,14 +324,7 @@ class Sale(metaclass=PoolMeta):
if line_p:
#compute pnl
Pnl = Pool().get('valuation.valuation')
pnl = Pnl.search([('line','=',line_p.id)])
if pnl:
Pnl.delete(pnl)
pnl_lines = []
pnl_lines.extend(line_p.get_pnl_fee_lines())
pnl_lines.extend(line_p.get_pnl_price_lines())
pnl_lines.extend(line_p.get_pnl_der_lines())
Pnl.save(pnl_lines)
Pnl.generate(line_p)
if line.quantity_theorical:
OpenPosition = Pool().get('open.position')
@@ -733,14 +726,7 @@ class SaleLine(metaclass=PoolMeta):
if purchase_lines:
for pl in purchase_lines:
Pnl = Pool().get('valuation.valuation')
pnl = Pnl.search([('line','=',pl.id)])
if pnl:
Pnl.delete(pnl)
pnl_lines = []
pnl_lines.extend(pl.get_pnl_fee_lines())
pnl_lines.extend(pl.get_pnl_price_lines())
pnl_lines.extend(pl.get_pnl_der_lines())
Pnl.save(pnl_lines)
Pnl.generate(pl)
class SaleCreatePurchase(Wizard):
"Create mirror purchase"

View File

@@ -34,10 +34,7 @@ VALTYPE = [
('derivative', 'Derivative'),
]
class Valuation(ModelSQL,ModelView):
"Valuation"
__name__ = 'valuation.valuation'
class ValuationBase(ModelSQL):
purchase = fields.Many2One('purchase.purchase',"Purchase")
line = fields.Many2One('purchase.line',"Purch. Line")
date = fields.Date("Date")
@@ -299,11 +296,24 @@ class Valuation(ModelSQL,ModelView):
pnl = Pnl.search([('line','=',line.id),('date','=',Date.today())])
if pnl:
Pnl.delete(pnl)
PnlLine = Pool().get('valuation.valuation.line')
pnlline = PnlLine.search(['line','=',line.id])
if pnlline:
PnlLine.delete(pnlline)
pnl_lines = []
pnl_lines.extend(cls.create_pnl_fee_from_line(line))
pnl_lines.extend(cls.create_pnl_price_from_line(line))
pnl_lines.extend(cls.create_pnl_der_from_line(line))
Pnl.save(pnl_lines)
PnlLine.save(pnl_lines)
class Valuation(ValuationBase, ModelView):
"Valuation"
__name__ = 'valuation.valuation'
class ValuationLine(ValuationBase, ModelView):
"Last Valuation"
__name__ = 'valuation.valuation.line'
class ValuationDyn(ModelSQL,ModelView):
"Valuation"
@@ -327,35 +337,12 @@ class ValuationDyn(ModelSQL,ModelView):
@classmethod
def table_query(cls):
Valuation = Pool().get('valuation.valuation')
Valuation = Pool().get('valuation.valuation.line')
val = Valuation.__table__()
context = Transaction().context
group_pnl = context.get('group_pnl')
wh = (val.id > 0)
# query = val.select(
# Literal(0).as_('create_uid'),
# CurrentTimestamp().as_('create_date'),
# Literal(None).as_('write_uid'),
# Literal(None).as_('write_date'),
# val.id.as_('id'),
# val.purchase.as_('r_purchase'),
# val.line.as_('r_line'),
# val.date.as_('r_date'),
# val.type.as_('r_type'),
# val.reference.as_('r_reference'),
# val.counterparty.as_('r_counterparty'),
# val.product.as_('r_product'),
# val.state.as_('r_state'),
# val.price.as_('r_price'),
# val.currency.as_('r_currency'),
# val.quantity.as_('r_quantity'),
# val.unit.as_('r_unit'),
# val.amount.as_('r_amount'),
# val.mtm.as_('r_mtm'),
# val.lot.as_('r_lot'),
# where=wh)
#if group_pnl==True:
query = val.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),