Merge dev from main #1
@@ -54,22 +54,23 @@ class ValuationBase(ModelSQL):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _base_pnl(cls, *, line, lot, pnl_type, sale=None):
|
def _base_pnl(cls, *, line, lot, pnl_type, sale=None):
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
pnl = Pool().get('valuation.valuation')()
|
|
||||||
|
|
||||||
pnl.purchase = line.purchase.id
|
values = {
|
||||||
pnl.line = line.id
|
'purchase': line.purchase.id,
|
||||||
pnl.type = pnl_type
|
'line': line.id,
|
||||||
pnl.date = Date.today()
|
'type': pnl_type,
|
||||||
pnl.lot = lot.id
|
'date': Date.today(),
|
||||||
|
'lot': lot.id,
|
||||||
|
}
|
||||||
|
|
||||||
if sale:
|
if sale:
|
||||||
pnl.sale = sale.id
|
values['sale'] = sale.id
|
||||||
|
|
||||||
return pnl
|
return values
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _build_basis_pnl(cls, *, line, lot, sale_line, pc, sign):
|
def _build_basis_pnl(cls, *, line, lot, sale_line, pc, sign):
|
||||||
pnl = cls._base_pnl(
|
values = cls._base_pnl(
|
||||||
line=line,
|
line=line,
|
||||||
lot=lot,
|
lot=lot,
|
||||||
sale=sale_line.sale if sale_line else None,
|
sale=sale_line.sale if sale_line else None,
|
||||||
@@ -78,38 +79,41 @@ class ValuationBase(ModelSQL):
|
|||||||
|
|
||||||
qty = lot.get_current_quantity_converted()
|
qty = lot.get_current_quantity_converted()
|
||||||
|
|
||||||
pnl.reference = f"{pc.get_name()} / {pc.ratio}%"
|
values.update({
|
||||||
pnl.price = round(pc.price, 4)
|
'reference': f"{pc.get_name()} / {pc.ratio}%",
|
||||||
pnl.counterparty = sale_line.sale.party if sale_line else line.purchase.party
|
'price': round(pc.price, 4),
|
||||||
pnl.product = sale_line.product if sale_line else line.product
|
'counterparty': sale_line.sale.party.id if sale_line else line.purchase.party.id,
|
||||||
|
'product': sale_line.product.id if sale_line else line.product.id,
|
||||||
|
})
|
||||||
|
|
||||||
# State
|
# State
|
||||||
if pc.unfixed_qt == 0:
|
if pc.unfixed_qt == 0:
|
||||||
pnl.state = 'fixed'
|
values['state'] = 'fixed'
|
||||||
elif pc.fixed_qt == 0:
|
elif pc.fixed_qt == 0:
|
||||||
pnl.state = 'unfixed'
|
values['state'] = 'unfixed'
|
||||||
else:
|
else:
|
||||||
base = sale_line.quantity_theorical if sale_line else line.quantity_theorical
|
base = sale_line.quantity_theorical if sale_line else line.quantity_theorical
|
||||||
pnl.state = f"part. fixed {round(pc.fixed_qt / Decimal(base) * 100, 0)}%"
|
values['state'] = f"part. fixed {round(pc.fixed_qt / Decimal(base) * 100, 0)}%"
|
||||||
|
|
||||||
if pc.price and pc.ratio:
|
if pc.price and pc.ratio:
|
||||||
pnl.quantity = round(qty, 5)
|
amount = round(pc.price * qty * Decimal(sign) * pc.ratio / 100, 4)
|
||||||
pnl.amount = round(pc.price * qty * Decimal(sign) * pc.ratio / 100, 4)
|
|
||||||
|
|
||||||
mtm = Decimal(0)
|
|
||||||
last_price = pc.get_last_price()
|
last_price = pc.get_last_price()
|
||||||
if last_price:
|
mtm = round(Decimal(last_price) * qty * Decimal(sign), 4) if last_price else Decimal(0)
|
||||||
mtm = round(Decimal(last_price) * qty * Decimal(sign), 4)
|
|
||||||
|
|
||||||
pnl.mtm = round(pnl.amount - (mtm * pc.ratio / 100), 4)
|
values.update({
|
||||||
pnl.unit = sale_line.unit if sale_line else line.unit
|
'quantity': round(qty, 5),
|
||||||
pnl.currency = sale_line.sale.currency if sale_line else line.purchase.currency
|
'amount': amount,
|
||||||
|
'mtm': round(amount - (mtm * pc.ratio / 100), 4),
|
||||||
|
'unit': sale_line.unit.id if sale_line else line.unit.id,
|
||||||
|
'currency': sale_line.sale.currency.id if sale_line else line.purchase.currency.id,
|
||||||
|
})
|
||||||
|
|
||||||
return pnl
|
return values
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _build_simple_pnl(cls, *, line, lot, sale_line, price, state, sign, pnl_type):
|
def _build_simple_pnl(cls, *, line, lot, sale_line, price, state, sign, pnl_type):
|
||||||
pnl = cls._base_pnl(
|
values = cls._base_pnl(
|
||||||
line=line,
|
line=line,
|
||||||
lot=lot,
|
lot=lot,
|
||||||
sale=sale_line.sale if sale_line else None,
|
sale=sale_line.sale if sale_line else None,
|
||||||
@@ -117,19 +121,25 @@ class ValuationBase(ModelSQL):
|
|||||||
)
|
)
|
||||||
|
|
||||||
qty = lot.get_current_quantity_converted()
|
qty = lot.get_current_quantity_converted()
|
||||||
pnl.price = round(price, 4)
|
|
||||||
pnl.quantity = round(qty, 5)
|
|
||||||
pnl.amount = round(pnl.price * qty * Decimal(sign), 4)
|
|
||||||
pnl.mtm = Decimal(0)
|
|
||||||
pnl.state = state
|
|
||||||
pnl.unit = sale_line.unit if sale_line else line.unit
|
|
||||||
pnl.currency = sale_line.sale.currency if sale_line else line.purchase.currency
|
|
||||||
pnl.counterparty = sale_line.sale.party if sale_line else line.purchase.party
|
|
||||||
pnl.product = sale_line.product if sale_line else line.product
|
|
||||||
|
|
||||||
pnl.reference = 'Sale/Physic' if lot.lot_type == 'physic' else 'Sale/Open' if sale_line else 'Purchase/Physic'
|
values.update({
|
||||||
|
'price': round(price, 4),
|
||||||
|
'quantity': round(qty, 5),
|
||||||
|
'amount': round(price * qty * Decimal(sign), 4),
|
||||||
|
'mtm': Decimal(0),
|
||||||
|
'state': state,
|
||||||
|
'unit': sale_line.unit.id if sale_line else line.unit.id,
|
||||||
|
'currency': sale_line.sale.currency.id if sale_line else line.purchase.currency.id,
|
||||||
|
'counterparty': sale_line.sale.party.id if sale_line else line.purchase.party.id,
|
||||||
|
'product': sale_line.product.id if sale_line else line.product.id,
|
||||||
|
'reference': (
|
||||||
|
'Sale/Physic' if lot.lot_type == 'physic'
|
||||||
|
else 'Sale/Open' if sale_line
|
||||||
|
else 'Purchase/Physic'
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
return pnl
|
return values
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_pnl_price_from_line(cls, line):
|
def create_pnl_price_from_line(cls, line):
|
||||||
@@ -138,30 +148,31 @@ class ValuationBase(ModelSQL):
|
|||||||
|
|
||||||
for lot in line.lots:
|
for lot in line.lots:
|
||||||
|
|
||||||
# --- PURCHASE SIDE ---
|
|
||||||
if line.price_type == 'basis':
|
if line.price_type == 'basis':
|
||||||
for pc in line.price_summary or []:
|
for pc in line.price_summary or []:
|
||||||
pnl = cls._build_basis_pnl(line=line, lot=lot, sale_line=None, pc=pc, sign=-1)
|
values = cls._build_basis_pnl(line=line, lot=lot, sale_line=None, pc=pc, sign=-1)
|
||||||
if pnl:
|
if values:
|
||||||
price_lines.append(pnl)
|
price_lines.append(values)
|
||||||
|
|
||||||
elif line.price_type in ('priced', 'efp') and lot.lot_price:
|
elif line.price_type in ('priced', 'efp') and lot.lot_price:
|
||||||
state = 'fixed' if line.price_type == 'priced' else 'not fixed'
|
|
||||||
price_lines.append(
|
price_lines.append(
|
||||||
cls._build_simple_pnl(
|
cls._build_simple_pnl(
|
||||||
line=line,
|
line=line,
|
||||||
lot=lot,
|
lot=lot,
|
||||||
sale_line=None,
|
sale_line=None,
|
||||||
price=lot.lot_price,
|
price=lot.lot_price,
|
||||||
state=state,
|
state='fixed' if line.price_type == 'priced' else 'not fixed',
|
||||||
sign=-1,
|
sign=-1,
|
||||||
pnl_type=f'pur. {line.price_type}'
|
pnl_type=f'pur. {line.price_type}'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- SALE SIDE ---
|
|
||||||
sale_lots = [lot] if lot.sale_line else [
|
sale_lots = [lot] if lot.sale_line else [
|
||||||
lqt.lot_s for lqt in LotQt.search([('lot_p','=',lot.id),('lot_s','>',0),('lot_quantity','>',0)])
|
lqt.lot_s for lqt in LotQt.search([
|
||||||
|
('lot_p', '=', lot.id),
|
||||||
|
('lot_s', '>', 0),
|
||||||
|
('lot_quantity', '>', 0),
|
||||||
|
])
|
||||||
]
|
]
|
||||||
|
|
||||||
for sl in sale_lots:
|
for sl in sale_lots:
|
||||||
@@ -171,20 +182,18 @@ class ValuationBase(ModelSQL):
|
|||||||
|
|
||||||
if sl_line.price_type == 'basis':
|
if sl_line.price_type == 'basis':
|
||||||
for pc in sl_line.price_summary or []:
|
for pc in sl_line.price_summary or []:
|
||||||
pnl = cls._build_basis_pnl(line=line, lot=sl, sale_line=sl_line, pc=pc, sign=+1)
|
values = cls._build_basis_pnl(line=line, lot=sl, sale_line=sl_line, pc=pc, sign=+1)
|
||||||
if pnl:
|
if values:
|
||||||
price_lines.append(pnl)
|
price_lines.append(values)
|
||||||
|
|
||||||
elif sl_line.price_type in ('priced', 'efp'):
|
elif sl_line.price_type in ('priced', 'efp'):
|
||||||
state = 'fixed' if sl_line.price_type == 'priced' else 'not fixed'
|
|
||||||
price = sl.lot_price_sale
|
|
||||||
price_lines.append(
|
price_lines.append(
|
||||||
cls._build_simple_pnl(
|
cls._build_simple_pnl(
|
||||||
line=line,
|
line=line,
|
||||||
lot=sl,
|
lot=sl,
|
||||||
sale_line=sl_line,
|
sale_line=sl_line,
|
||||||
price=price,
|
price=sl.lot_price_sale,
|
||||||
state=state,
|
state='fixed' if sl_line.price_type == 'priced' else 'not fixed',
|
||||||
sign=+1,
|
sign=+1,
|
||||||
pnl_type=f'sale {sl_line.price_type}'
|
pnl_type=f'sale {sl_line.price_type}'
|
||||||
)
|
)
|
||||||
@@ -192,6 +201,7 @@ class ValuationBase(ModelSQL):
|
|||||||
|
|
||||||
return price_lines
|
return price_lines
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def group_fees_by_type_supplier(cls,line,fees):
|
def group_fees_by_type_supplier(cls,line,fees):
|
||||||
grouped = defaultdict(list)
|
grouped = defaultdict(list)
|
||||||
@@ -211,101 +221,107 @@ class ValuationBase(ModelSQL):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_pnl_fee_from_line(cls,line):
|
def create_pnl_fee_from_line(cls, line):
|
||||||
fee_lines = []
|
fee_lines = []
|
||||||
#pnl management
|
|
||||||
Pnl = Pool().get('valuation.valuation')
|
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
Currency = Pool().get('currency.currency')
|
Currency = Pool().get('currency.currency')
|
||||||
FeeLots = Pool().get('fee.lots')
|
FeeLots = Pool().get('fee.lots')
|
||||||
if line.lots:
|
|
||||||
for lot in line.lots:
|
for lot in line.lots or []:
|
||||||
fl = FeeLots.search(['lot','=',lot.id])
|
fl = FeeLots.search([('lot', '=', lot.id)])
|
||||||
if fl:
|
if not fl:
|
||||||
|
continue
|
||||||
|
|
||||||
fees = [e.fee for e in fl]
|
fees = [e.fee for e in fl]
|
||||||
sorted_fees = cls.group_fees_by_type_supplier(line,fees)
|
for sf in cls.group_fees_by_type_supplier(line, fees):
|
||||||
if sorted_fees:
|
|
||||||
for sf in sorted_fees:
|
price = Decimal(sf.get_price_per_qt())
|
||||||
pnl = Pnl()
|
|
||||||
pnl.lot = lot.id
|
|
||||||
if lot.sale_line:
|
|
||||||
pnl.sale = lot.sale_line.sale.id
|
|
||||||
pnl.purchase = line.purchase.id
|
|
||||||
pnl.line = line.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 != line.purchase.currency:
|
if sf.currency != line.purchase.currency:
|
||||||
with Transaction().set_context(date=Date.today()):
|
with Transaction().set_context(date=Date.today()):
|
||||||
pnl.price = Currency.compute(sf.currency,pnl.price, line.purchase.currency)
|
price = Currency.compute(sf.currency, price, line.purchase.currency)
|
||||||
pnl.counterparty = sf.supplier
|
|
||||||
str_op = ''
|
sign = -1 if sf.p_r == 'pay' else 1
|
||||||
if lot.lot_type == 'physic':
|
|
||||||
str_op = '/Physic'
|
fee_lines.append({
|
||||||
else:
|
'lot': lot.id,
|
||||||
str_op = '/Open'
|
'sale': lot.sale_line.sale.id if lot.sale_line else None,
|
||||||
pnl.reference = sf.product.name + str_op
|
'purchase': line.purchase.id,
|
||||||
pnl.product = sf.product
|
'line': line.id,
|
||||||
pnl.state = sf.type
|
'type': (
|
||||||
if sf.p_r == 'pay':
|
'shipment fee' if sf.shipment_in
|
||||||
sign = -1
|
else 'sale fee' if sf.sale_line
|
||||||
pnl.amount = round(pnl.price * lot.get_current_quantity_converted() * sign,2)
|
else 'pur. fee'
|
||||||
pnl.mtm = 0
|
),
|
||||||
pnl.quantity = round(lot.get_current_quantity_converted(),5)
|
'date': Date.today(),
|
||||||
pnl.unit = sf.unit if sf.unit else line.unit
|
'price': price,
|
||||||
pnl.currency = sf.currency
|
'counterparty': sf.supplier.id,
|
||||||
fee_lines.append(pnl)
|
'reference': f"{sf.product.name}/{'Physic' if lot.lot_type == 'physic' else 'Open'}",
|
||||||
|
'product': sf.product.id,
|
||||||
|
'state': sf.type,
|
||||||
|
'quantity': round(lot.get_current_quantity_converted(), 5),
|
||||||
|
'amount': round(price * lot.get_current_quantity_converted() * sign, 2),
|
||||||
|
'mtm': Decimal(0),
|
||||||
|
'unit': sf.unit.id if sf.unit else line.unit.id,
|
||||||
|
'currency': sf.currency.id,
|
||||||
|
})
|
||||||
|
|
||||||
return fee_lines
|
return fee_lines
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_pnl_der_from_line(cls,line):
|
def create_pnl_der_from_line(cls, line):
|
||||||
der_lines = []
|
|
||||||
if line.derivatives:
|
|
||||||
Pnl = Pool().get('valuation.valuation')
|
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
for d in line.derivatives:
|
der_lines = []
|
||||||
pnl = Pnl()
|
|
||||||
pnl.purchase = line.purchase.id
|
for d in line.derivatives or []:
|
||||||
pnl.line = line.id
|
price = Decimal(d.price_index.get_price_per_qt(
|
||||||
pnl.type = 'derivative'
|
d.price, line.unit, line.purchase.currency
|
||||||
pnl.date = Date.today()
|
))
|
||||||
pnl.reference = d.price_index.price_index
|
|
||||||
pnl.price = round(Decimal(d.price_index.get_price_per_qt(d.price,line.unit,line.purchase.currency)),4)
|
mtm = Decimal(d.price_index.get_price(
|
||||||
pnl.counterparty = d.party
|
Date.today(), line.unit, line.purchase.currency, True
|
||||||
pnl.product = d.product
|
))
|
||||||
pnl.state = 'fixed'
|
|
||||||
pnl.amount = round(pnl.price * (d.quantity) * Decimal(-1),4)
|
der_lines.append({
|
||||||
mtm = round(Decimal(d.price_index.get_price(Date.today(),line.unit,line.purchase.currency,True)) * d.quantity * Decimal(-1),4)
|
'purchase': line.purchase.id,
|
||||||
pnl.mtm = pnl.amount - mtm
|
'line': line.id,
|
||||||
pnl.quantity = round(d.quantity,5)
|
'type': 'derivative',
|
||||||
pnl.unit = line.unit
|
'date': Date.today(),
|
||||||
pnl.currency = line.purchase.currency
|
'reference': d.price_index.price_index,
|
||||||
der_lines.append(pnl)
|
'price': round(price, 4),
|
||||||
|
'counterparty': d.party.id,
|
||||||
|
'product': d.product.id,
|
||||||
|
'state': 'fixed',
|
||||||
|
'quantity': round(d.quantity, 5),
|
||||||
|
'amount': round(price * d.quantity * Decimal(-1), 4),
|
||||||
|
'mtm': round((price * d.quantity * Decimal(-1)) - (mtm * d.quantity * Decimal(-1)), 4),
|
||||||
|
'unit': line.unit.id,
|
||||||
|
'currency': line.purchase.currency.id,
|
||||||
|
})
|
||||||
|
|
||||||
return der_lines
|
return der_lines
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate(cls, line):
|
def generate(cls, line):
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
Pnl = Pool().get('valuation.valuation')
|
Valuation = Pool().get('valuation.valuation')
|
||||||
pnl = Pnl.search([('line','=',line.id),('date','=',Date.today())])
|
ValuationLine = Pool().get('valuation.valuation.line')
|
||||||
if pnl:
|
|
||||||
Pnl.delete(pnl)
|
Valuation.delete(Valuation.search([
|
||||||
PnlLine = Pool().get('valuation.valuation.line')
|
('line', '=', line.id),
|
||||||
pnlline = PnlLine.search(['line','=',line.id])
|
('date', '=', Date.today()),
|
||||||
if pnlline:
|
]))
|
||||||
PnlLine.delete(pnlline)
|
|
||||||
pnl_lines = []
|
ValuationLine.delete(ValuationLine.search([
|
||||||
pnl_lines.extend(cls.create_pnl_fee_from_line(line))
|
('line', '=', line.id),
|
||||||
pnl_lines.extend(cls.create_pnl_price_from_line(line))
|
]))
|
||||||
pnl_lines.extend(cls.create_pnl_der_from_line(line))
|
|
||||||
Pnl.save(pnl_lines)
|
values = []
|
||||||
PnlLine.save(pnl_lines)
|
values.extend(cls.create_pnl_fee_from_line(line))
|
||||||
|
values.extend(cls.create_pnl_price_from_line(line))
|
||||||
|
values.extend(cls.create_pnl_der_from_line(line))
|
||||||
|
|
||||||
|
Valuation.create(values)
|
||||||
|
ValuationLine.create(values)
|
||||||
|
|
||||||
class Valuation(ValuationBase, ModelView):
|
class Valuation(ValuationBase, ModelView):
|
||||||
"Valuation"
|
"Valuation"
|
||||||
|
|||||||
Reference in New Issue
Block a user