Merge dev from main #1
@@ -1685,7 +1685,7 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
|||||||
|
|
||||||
@fields.depends(
|
@fields.depends(
|
||||||
'type', 'quantity', 'unit_price',
|
'type', 'quantity', 'unit_price',
|
||||||
'purchase', '_parent_purchase.currency')
|
'purchase', '_parent_purchase.currency','premium')
|
||||||
def on_change_with_amount(self):
|
def on_change_with_amount(self):
|
||||||
if (self.type == 'line'
|
if (self.type == 'line'
|
||||||
and self.quantity is not None
|
and self.quantity is not None
|
||||||
|
|||||||
@@ -3,7 +3,32 @@
|
|||||||
|
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
|
|
||||||
from . import purchase,sale,global_reporting,stock,derivative,lot,pricing,workflow,lc,dashboard,fee,payment_term,purchase_prepayment,cron,party,forex,outgoing,incoming,optional,association_tables, document_tracking, open_position, credit_risk
|
from . import (
|
||||||
|
purchase,
|
||||||
|
sale,
|
||||||
|
global_reporting,
|
||||||
|
stock,
|
||||||
|
derivative,
|
||||||
|
lot,
|
||||||
|
pricing,
|
||||||
|
workflow,
|
||||||
|
lc,
|
||||||
|
dashboard,
|
||||||
|
fee,
|
||||||
|
payment_term,
|
||||||
|
purchase_prepayment,
|
||||||
|
cron,
|
||||||
|
party,
|
||||||
|
forex,
|
||||||
|
outgoing,
|
||||||
|
incoming,
|
||||||
|
optional,
|
||||||
|
association_tables,
|
||||||
|
document_tracking,
|
||||||
|
open_position,
|
||||||
|
credit_risk,
|
||||||
|
valuation,
|
||||||
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
Pool.register(
|
Pool.register(
|
||||||
@@ -69,8 +94,11 @@ def register():
|
|||||||
fee.Fee,
|
fee.Fee,
|
||||||
fee.FeeLots,
|
fee.FeeLots,
|
||||||
purchase.FeeLots,
|
purchase.FeeLots,
|
||||||
fee.Valuation,
|
valuation.Valuation,
|
||||||
fee.ValuationDyn,
|
valuation.ValuationLine,
|
||||||
|
valuation.ValuationDyn,
|
||||||
|
valuation.ValuationReport,
|
||||||
|
valuation.ValuationReportContext,
|
||||||
derivative.Derivative,
|
derivative.Derivative,
|
||||||
derivative.DerivativeMatch,
|
derivative.DerivativeMatch,
|
||||||
derivative.MatchWizardStart,
|
derivative.MatchWizardStart,
|
||||||
@@ -151,6 +179,8 @@ def register():
|
|||||||
sale.SaleCreatePurchaseInput,
|
sale.SaleCreatePurchaseInput,
|
||||||
sale.Derivative,
|
sale.Derivative,
|
||||||
sale.Valuation,
|
sale.Valuation,
|
||||||
|
sale.ValuationLine,
|
||||||
|
sale.ValuationReport,
|
||||||
sale.Fee,
|
sale.Fee,
|
||||||
sale.Lot,
|
sale.Lot,
|
||||||
sale.FeeLots,
|
sale.FeeLots,
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ class Dashboard(ModelSQL, ModelView):
|
|||||||
self.chatbot = 'chatbot:' + json.dumps(dial, ensure_ascii=False)
|
self.chatbot = 'chatbot:' + json.dumps(dial, ensure_ascii=False)
|
||||||
logger.info("EXITONCHANGE",self.chatbot)
|
logger.info("EXITONCHANGE",self.chatbot)
|
||||||
|
|
||||||
def get_last_two_fx_rates(self, from_code='USD', to_code='EUR'):
|
def get_last_five_fx_rates(self, from_code='USD', to_code='EUR'):
|
||||||
"""
|
"""
|
||||||
Retourne (dernier_taux, avant_dernier_taux) pour le couple de devises.
|
Retourne (dernier_taux, avant_dernier_taux) pour le couple de devises.
|
||||||
"""
|
"""
|
||||||
@@ -208,29 +208,42 @@ class Dashboard(ModelSQL, ModelView):
|
|||||||
rates = CurrencyRate.search(
|
rates = CurrencyRate.search(
|
||||||
[('currency', '=', to_currency.id)],
|
[('currency', '=', to_currency.id)],
|
||||||
order=[('date', 'DESC')],
|
order=[('date', 'DESC')],
|
||||||
limit=2,
|
limit=5,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not rates:
|
if not rates:
|
||||||
return None, None
|
return None, None, None, None, None
|
||||||
|
|
||||||
# Calcul du taux EUR/USD
|
# Calcul du taux EUR/USD
|
||||||
# Si la devise principale de la société est EUR, et que le taux stocké est
|
# Si la devise principale de la société est EUR, et que le taux stocké est
|
||||||
# "1 USD = X EUR", on veut l'inverse pour avoir EUR/USD
|
# "1 USD = X EUR", on veut l'inverse pour avoir EUR/USD
|
||||||
last_rate = rates[0].rate
|
f1 = rates[0].rate
|
||||||
prev_rate = rates[1].rate if len(rates) > 1 else None
|
f2 = rates[1].rate if len(rates) > 1 else None
|
||||||
|
f3 = rates[2].rate if len(rates) > 2 else None
|
||||||
|
f4 = rates[3].rate if len(rates) > 3 else None
|
||||||
|
f5 = rates[4].rate if len(rates) > 4 else None
|
||||||
|
d1 = rates[0].date
|
||||||
|
d2 = rates[1].date if len(rates) > 1 else None
|
||||||
|
d3 = rates[2].date if len(rates) > 2 else None
|
||||||
|
d4 = rates[3].date if len(rates) > 3 else None
|
||||||
|
d5 = rates[4].date if len(rates) > 4 else None
|
||||||
|
|
||||||
# if from_currency != to_currency:
|
# if from_currency != to_currency:
|
||||||
# last_rate = 1 / last_rate if last_rate else None
|
# last_rate = 1 / last_rate if last_rate else None
|
||||||
# prev_rate = 1 / prev_rate if prev_rate else None
|
# prev_rate = 1 / prev_rate if prev_rate else None
|
||||||
|
|
||||||
if last_rate and prev_rate:
|
return round(1/f1,6), round(1/f2,6) if f2 else None, round(1/f3,6) if f3 else None, round(1/f4,6) if f4 else None, round(1/f5,6) if f5 else None, d1, d2, d3, d4, d5
|
||||||
return round(1/last_rate,6), round(1/prev_rate,6)
|
|
||||||
|
|
||||||
def get_tremor(self,name):
|
def get_tremor(self,name):
|
||||||
Pnl = Pool().get('valuation.valuation')
|
Configuration = Pool().get('gr.configuration')
|
||||||
pnls = Pnl.search(['id','>',0])
|
config = Configuration.search(['id','>',0])[0]
|
||||||
pnl_amount = "{:,.0f}".format(round(sum([e.amount for e in pnls]),0))
|
f1,f2,f3,f4,f5,d1,d2,d3,d4,d5 = self.get_last_five_fx_rates()
|
||||||
|
Valuation = Pool().get('valuation.valuation')
|
||||||
|
total_t, total_t1, variation = Valuation.get_totals()
|
||||||
|
pnl_amount = "{:,.0f}".format(round(total_t,0))
|
||||||
|
pnl_variation = 0
|
||||||
|
if total_t1:
|
||||||
|
pnl_variation = "{:,.2f}".format(round((total_t/total_t1 - 1)*100,0))
|
||||||
Open = Pool().get('open.position')
|
Open = Pool().get('open.position')
|
||||||
opens = Open.search(['id','>',0])
|
opens = Open.search(['id','>',0])
|
||||||
exposure = "{:,.0f}".format(round(sum([e.net_exposure for e in opens]),0))
|
exposure = "{:,.0f}".format(round(sum([e.net_exposure for e in opens]),0))
|
||||||
@@ -261,35 +274,66 @@ class Dashboard(ModelSQL, ModelView):
|
|||||||
Shipment = Pool().get('stock.shipment.in')
|
Shipment = Pool().get('stock.shipment.in')
|
||||||
draft = Shipment.search(['state','=','draft'])
|
draft = Shipment.search(['state','=','draft'])
|
||||||
shipment_d = len(draft)
|
shipment_d = len(draft)
|
||||||
val = Purchase.search(['state','=','started'])
|
val = Shipment.search(['state','=','started'])
|
||||||
shipment_s = len(val)
|
shipment_s = len(val)
|
||||||
conf = Purchase.search(['state','=','received'])
|
conf = Shipment.search(['state','=','received'])
|
||||||
shipment_r = len(conf)
|
shipment_r = len(conf)
|
||||||
Lot = Pool().get('lot.lot')
|
Lot = Pool().get('lot.lot')
|
||||||
lots = Lot.search(['sale_line','!=',None])
|
lots = Lot.search([('sale_line','!=',None),('line','!=',None),('lot_type','=','physic')])
|
||||||
lot_m = len(lots)
|
lot_m = len(lots)
|
||||||
val = Lot.search(['sale_line','=',None])
|
val = Lot.search([('sale_line','=',None),('line','!=',None),('lot_type','=','physic')])
|
||||||
lot_a = len(val)
|
lot_a = len(val)
|
||||||
conf = Lot.search(['lot_type','=','physic'])
|
|
||||||
lot_al = len(conf)
|
|
||||||
Invoice = Pool().get('account.invoice')
|
Invoice = Pool().get('account.invoice')
|
||||||
invs = Invoice.search(['type','=','in'])
|
invs = Invoice.search(['type','=','in'])
|
||||||
inv_p = len(invs)
|
inv_p = len(invs)
|
||||||
|
invs = Invoice.search([('type','=','in'),('state','=','paid')])
|
||||||
|
inv_p_p = len(invs)
|
||||||
|
invs = Invoice.search([('type','=','in'),('state','!=','paid')])
|
||||||
|
inv_p_np = len(invs)
|
||||||
invs = Invoice.search(['type','=','out'])
|
invs = Invoice.search(['type','=','out'])
|
||||||
inv_s = len(invs)
|
inv_s = len(invs)
|
||||||
|
invs = Invoice.search([('type','=','out'),('state','=','paid')])
|
||||||
|
inv_s_p = len(invs)
|
||||||
|
invs = Invoice.search([('type','=','out'),('state','!=','paid')])
|
||||||
|
inv_s_np = len(invs)
|
||||||
AccountMove = Pool().get('account.move')
|
AccountMove = Pool().get('account.move')
|
||||||
accs = AccountMove.search(['id','>',0])
|
accs = AccountMove.search([('journal','=',3),('state','!=','posted')])
|
||||||
move_cash = len(accs)
|
pay_val = len(accs)
|
||||||
|
accs = AccountMove.search([('journal','=',3),('state','=','posted')])
|
||||||
|
pay_posted = len(accs)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
"https://srv413259.hstgr.cloud/dashboard/index.html?pnl_amount="
|
config.dashboard +
|
||||||
|
"/dashboard/index.html?pnl_amount="
|
||||||
+ str(pnl_amount)
|
+ str(pnl_amount)
|
||||||
|
+ "&pnl_variation="
|
||||||
|
+ str(pnl_variation)
|
||||||
+ "&exposure="
|
+ "&exposure="
|
||||||
+ str(exposure)
|
+ str(exposure)
|
||||||
+ "&topay="
|
+ "&topay="
|
||||||
+ str(topay)
|
+ str(topay)
|
||||||
+ "&toreceive="
|
+ "&toreceive="
|
||||||
+ str(toreceive)
|
+ str(toreceive)
|
||||||
|
+ "&eurusd="
|
||||||
|
+ str(f1)
|
||||||
|
+ "&eurusd="
|
||||||
|
+ str(f2)
|
||||||
|
+ "&eurusd="
|
||||||
|
+ str(f3)
|
||||||
|
+ "&eurusd="
|
||||||
|
+ str(f4)
|
||||||
|
+ "&eurusd="
|
||||||
|
+ str(f5)
|
||||||
|
+ "&eurusd_date="
|
||||||
|
+ str(d1)
|
||||||
|
+ "&eurusd_date="
|
||||||
|
+ str(d2)
|
||||||
|
+ "&eurusd_date="
|
||||||
|
+ str(d3)
|
||||||
|
+ "&eurusd_date="
|
||||||
|
+ str(d4)
|
||||||
|
+ "&eurusd_date="
|
||||||
|
+ str(d5)
|
||||||
+ "&draft_p="
|
+ "&draft_p="
|
||||||
+ str(draft_p)
|
+ str(draft_p)
|
||||||
+ "&val_p="
|
+ "&val_p="
|
||||||
@@ -312,14 +356,22 @@ class Dashboard(ModelSQL, ModelView):
|
|||||||
+ str(lot_m)
|
+ str(lot_m)
|
||||||
+ "&lot_a="
|
+ "&lot_a="
|
||||||
+ str(lot_a)
|
+ str(lot_a)
|
||||||
+ "&lot_al="
|
|
||||||
+ str(lot_al)
|
|
||||||
+ "&inv_p="
|
+ "&inv_p="
|
||||||
+ str(inv_p)
|
+ str(inv_p)
|
||||||
|
+ "&inv_p_p="
|
||||||
|
+ str(inv_p_p)
|
||||||
|
+ "&inv_p_np="
|
||||||
|
+ str(inv_p_np)
|
||||||
+ "&inv_s="
|
+ "&inv_s="
|
||||||
+ str(inv_s)
|
+ str(inv_s)
|
||||||
+ "&move_cash="
|
+ "&inv_s_p="
|
||||||
+ str(move_cash)
|
+ str(inv_s_p)
|
||||||
|
+ "&inv_s_np="
|
||||||
|
+ str(inv_s_np)
|
||||||
|
+ "&pay_val="
|
||||||
|
+ str(pay_val)
|
||||||
|
+ "&pay_posted="
|
||||||
|
+ str(pay_posted)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -327,7 +379,7 @@ class Dashboard(ModelSQL, ModelView):
|
|||||||
News = Pool().get('news.news')
|
News = Pool().get('news.news')
|
||||||
Date = Pool().get('ir.date')
|
Date = Pool().get('ir.date')
|
||||||
news_list = News.search([('active', '=', True)], limit=5, order=[('publish_date', 'DESC')])
|
news_list = News.search([('active', '=', True)], limit=5, order=[('publish_date', 'DESC')])
|
||||||
last_rate,prev_rate = self.get_last_two_fx_rates()
|
last_rate,prev_rate, = self.get_last_five_fx_rates()
|
||||||
if last_rate and prev_rate:
|
if last_rate and prev_rate:
|
||||||
variation = ((last_rate - prev_rate) / prev_rate) * 100 if prev_rate else 0
|
variation = ((last_rate - prev_rate) / prev_rate) * 100 if prev_rate else 0
|
||||||
direction = "📈" if variation > 0 else "📉"
|
direction = "📈" if variation > 0 else "📉"
|
||||||
|
|||||||
@@ -21,117 +21,6 @@ from trytond.exceptions import UserWarning, UserError
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
VALTYPE = [
|
|
||||||
('priced', 'Price'),
|
|
||||||
('pur. priced', 'Pur. price'),
|
|
||||||
('pur. efp', 'Pur. efp'),
|
|
||||||
('sale priced', 'Sale price'),
|
|
||||||
('sale efp', 'Sale efp'),
|
|
||||||
('line fee', 'Line fee'),
|
|
||||||
('pur. fee', 'Pur. fee'),
|
|
||||||
('sale fee', 'Sale fee'),
|
|
||||||
('shipment fee', 'Shipment fee'),
|
|
||||||
('market', 'Market'),
|
|
||||||
('derivative', 'Derivative'),
|
|
||||||
]
|
|
||||||
|
|
||||||
class Valuation(ModelSQL,ModelView):
|
|
||||||
"Valuation"
|
|
||||||
__name__ = 'valuation.valuation'
|
|
||||||
|
|
||||||
purchase = fields.Many2One('purchase.purchase',"Purchase")
|
|
||||||
line = fields.Many2One('purchase.line',"Purch. Line")
|
|
||||||
date = fields.Date("Date")
|
|
||||||
type = fields.Selection(VALTYPE, "Type")
|
|
||||||
reference = fields.Char("Reference")
|
|
||||||
counterparty = fields.Many2One('party.party',"Counterparty")
|
|
||||||
product = fields.Many2One('product.product',"Product")
|
|
||||||
state = fields.Char("State")
|
|
||||||
price = fields.Numeric("Price",digits='unit')
|
|
||||||
currency = fields.Many2One('currency.currency',"Cur")
|
|
||||||
quantity = fields.Numeric("Quantity",digits='unit')
|
|
||||||
unit = fields.Many2One('product.uom',"Unit")
|
|
||||||
amount = fields.Numeric("Amount",digits='unit')
|
|
||||||
mtm = fields.Numeric("Mtm",digits='unit')
|
|
||||||
lot = fields.Many2One('lot.lot',"Lot")
|
|
||||||
|
|
||||||
class ValuationDyn(ModelSQL,ModelView):
|
|
||||||
"Valuation"
|
|
||||||
__name__ = 'valuation.valuation.dyn'
|
|
||||||
|
|
||||||
r_purchase = fields.Many2One('purchase.purchase',"Purchase")
|
|
||||||
r_line = fields.Many2One('purchase.line',"Line")
|
|
||||||
r_date = fields.Date("Date")
|
|
||||||
r_type = fields.Selection(VALTYPE, "Type")
|
|
||||||
r_reference = fields.Char("Reference")
|
|
||||||
r_counterparty = fields.Many2One('party.party',"Counterparty")
|
|
||||||
r_product = fields.Many2One('product.product',"Product")
|
|
||||||
r_state = fields.Char("State")
|
|
||||||
r_price = fields.Numeric("Price",digits='r_unit')
|
|
||||||
r_currency = fields.Many2One('currency.currency',"Cur")
|
|
||||||
r_quantity = fields.Numeric("Quantity",digits='r_unit')
|
|
||||||
r_unit = fields.Many2One('product.uom',"Unit")
|
|
||||||
r_amount = fields.Numeric("Amount",digits='r_unit')
|
|
||||||
r_mtm = fields.Numeric("Mtm",digits='r_unit')
|
|
||||||
r_lot = fields.Many2One('lot.lot',"Lot")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def table_query(cls):
|
|
||||||
Valuation = Pool().get('valuation.valuation')
|
|
||||||
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'),
|
|
||||||
Literal(None).as_('write_uid'),
|
|
||||||
Literal(None).as_('write_date'),
|
|
||||||
Max(val.id).as_('id'),
|
|
||||||
Max(val.purchase).as_('r_purchase'),
|
|
||||||
Max(val.line).as_('r_line'),
|
|
||||||
Max(val.date).as_('r_date'),
|
|
||||||
val.type.as_('r_type'),
|
|
||||||
Max(val.reference).as_('r_reference'),
|
|
||||||
val.counterparty.as_('r_counterparty'),
|
|
||||||
Max(val.product).as_('r_product'),
|
|
||||||
val.state.as_('r_state'),
|
|
||||||
Avg(val.price).as_('r_price'),
|
|
||||||
Max(val.currency).as_('r_currency'),
|
|
||||||
Sum(val.quantity).as_('r_quantity'),
|
|
||||||
Max(val.unit).as_('r_unit'),
|
|
||||||
Sum(val.amount).as_('r_amount'),
|
|
||||||
Sum(val.mtm).as_('r_mtm'),
|
|
||||||
Max(val.lot).as_('r_lot'),
|
|
||||||
where=wh,
|
|
||||||
group_by=[val.type,val.counterparty,val.state])
|
|
||||||
|
|
||||||
return query
|
|
||||||
|
|
||||||
def filter_state(state):
|
def filter_state(state):
|
||||||
def filter(func):
|
def filter(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
|
|||||||
@@ -19,26 +19,6 @@ this repository contains the full copyright notices and license terms. -->
|
|||||||
<field name="name">fee_tree_sequence</field>
|
<field name="name">fee_tree_sequence</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="valuation_view_tree_sequence3">
|
|
||||||
<field name="model">valuation.valuation</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="name">valuation_tree_sequence3</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="valuation_view_graph">
|
|
||||||
<field name="model">valuation.valuation</field>
|
|
||||||
<field name="type">graph</field>
|
|
||||||
<field name="name">valuation_graph</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="valuation_view_graph2">
|
|
||||||
<field name="model">valuation.valuation</field>
|
|
||||||
<field name="type">graph</field>
|
|
||||||
<field name="name">valuation_graph2</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="valuation_view_tree_sequence4">
|
|
||||||
<field name="model">valuation.valuation.dyn</field>
|
|
||||||
<field name="type">tree</field>
|
|
||||||
<field name="name">valuation_tree_sequence4</field>
|
|
||||||
</record>
|
|
||||||
<record model="ir.ui.view" id="fee_view_tree_sequence2">
|
<record model="ir.ui.view" id="fee_view_tree_sequence2">
|
||||||
<field name="model">fee.fee</field>
|
<field name="model">fee.fee</field>
|
||||||
<field name="type">tree</field>
|
<field name="type">tree</field>
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ class GRConfiguration(ModelSingleton, ModelSQL, ModelView):
|
|||||||
__name__ = 'gr.configuration'
|
__name__ = 'gr.configuration'
|
||||||
|
|
||||||
bi = fields.Char("BI connexion")
|
bi = fields.Char("BI connexion")
|
||||||
|
dashboard = fields.Char("Dashboard connexion")
|
||||||
dark = fields.Boolean("Dark mode")
|
dark = fields.Boolean("Dark mode")
|
||||||
@@ -1168,6 +1168,7 @@ class LotQt(
|
|||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls, lotqts):
|
def validate(cls, lotqts):
|
||||||
super(LotQt, cls).validate(lotqts)
|
super(LotQt, cls).validate(lotqts)
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
#Update Move
|
#Update Move
|
||||||
for lqt in lotqts:
|
for lqt in lotqts:
|
||||||
cls.updateMove(lqt.lot_move)
|
cls.updateMove(lqt.lot_move)
|
||||||
@@ -1177,15 +1178,15 @@ class LotQt(
|
|||||||
if lqt.lot_p and lqt.lot_quantity > 0:
|
if lqt.lot_p and lqt.lot_quantity > 0:
|
||||||
pl = lqt.lot_p.line
|
pl = lqt.lot_p.line
|
||||||
logger.info("VALIDATE_LQT_PL:%s",pl)
|
logger.info("VALIDATE_LQT_PL:%s",pl)
|
||||||
Pnl = Pool().get('valuation.valuation')
|
# Pnl = Pool().get('valuation.valuation')
|
||||||
pnl = Pnl.search([('line','=',pl.id)])
|
# pnl = Pnl.search([('line','=',pl.id),('date','=',Date.today())])
|
||||||
if pnl:
|
# if pnl:
|
||||||
Pnl.delete(pnl)
|
# Pnl.delete(pnl)
|
||||||
pnl_lines = []
|
# pnl_lines = []
|
||||||
pnl_lines.extend(pl.get_pnl_fee_lines())
|
# pnl_lines.extend(pl.get_pnl_fee_lines())
|
||||||
pnl_lines.extend(pl.get_pnl_price_lines())
|
# pnl_lines.extend(pl.get_pnl_price_lines())
|
||||||
pnl_lines.extend(pl.get_pnl_der_lines())
|
# pnl_lines.extend(pl.get_pnl_der_lines())
|
||||||
Pnl.save(pnl_lines)
|
# Pnl.save(pnl_lines)
|
||||||
|
|
||||||
#Open position update
|
#Open position update
|
||||||
if pl.quantity_theorical:
|
if pl.quantity_theorical:
|
||||||
|
|||||||
@@ -252,21 +252,12 @@ class Purchase(metaclass=PoolMeta):
|
|||||||
broker = fields.Many2One('party.party',"Broker",domain=[('categories.parent', 'child_of', [4])])
|
broker = fields.Many2One('party.party',"Broker",domain=[('categories.parent', 'child_of', [4])])
|
||||||
tol_min = fields.Numeric("Tol - in %")
|
tol_min = fields.Numeric("Tol - in %")
|
||||||
tol_max = fields.Numeric("Tol + in %")
|
tol_max = fields.Numeric("Tol + in %")
|
||||||
# certification = fields.Selection([
|
|
||||||
# (None, ''),
|
|
||||||
# ('bci', 'BCI'),
|
|
||||||
# ],"Certification")
|
|
||||||
certif = fields.Many2One('purchase.certification',"Certification")
|
certif = fields.Many2One('purchase.certification',"Certification")
|
||||||
wb = fields.Many2One('purchase.weight.basis',"Weight basis")
|
wb = fields.Many2One('purchase.weight.basis',"Weight basis")
|
||||||
association = fields.Many2One('purchase.association',"Association")
|
association = fields.Many2One('purchase.association',"Association")
|
||||||
crop = fields.Many2One('purchase.crop',"Crop")
|
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.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')
|
derivatives = fields.One2Many('derivative.derivative', 'purchase', 'Derivative')
|
||||||
plans = fields.One2Many('workflow.plan','purchase',"Execution plans")
|
plans = fields.One2Many('workflow.plan','purchase',"Execution plans")
|
||||||
forex = fields.One2Many('forex.cover.physical.contract','contract',"Forex",readonly=True)
|
forex = fields.One2Many('forex.cover.physical.contract','contract',"Forex",readonly=True)
|
||||||
@@ -380,19 +371,11 @@ class Purchase(metaclass=PoolMeta):
|
|||||||
Decimal(str(line.quantity))
|
Decimal(str(line.quantity))
|
||||||
.quantize(Decimal("0.00001"))
|
.quantize(Decimal("0.00001"))
|
||||||
)
|
)
|
||||||
|
|
||||||
Line.save([line])
|
Line.save([line])
|
||||||
|
|
||||||
#compute pnl
|
#compute pnl
|
||||||
Pnl = Pool().get('valuation.valuation')
|
Pnl = Pool().get('valuation.valuation')
|
||||||
pnl = Pnl.search([('line','=',line.id)])
|
Pnl.generate(line)
|
||||||
if pnl:
|
|
||||||
Pnl.delete(pnl)
|
|
||||||
pnl_lines = []
|
|
||||||
pnl_lines.extend(line.get_pnl_fee_lines())
|
|
||||||
pnl_lines.extend(line.get_pnl_price_lines())
|
|
||||||
pnl_lines.extend(line.get_pnl_der_lines())
|
|
||||||
Pnl.save(pnl_lines)
|
|
||||||
|
|
||||||
if line.quantity_theorical:
|
if line.quantity_theorical:
|
||||||
OpenPosition = Pool().get('open.position')
|
OpenPosition = Pool().get('open.position')
|
||||||
@@ -655,358 +638,6 @@ class Line(metaclass=PoolMeta):
|
|||||||
f.purchase = line.purchase
|
f.purchase = line.purchase
|
||||||
Fee.save([f])
|
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):
|
def check_from_to(self,tr):
|
||||||
if tr.pricing_period:
|
if tr.pricing_period:
|
||||||
date_from,date_to, d, include, dates = tr.getDateWithEstTrigger(1)
|
date_from,date_to, d, include, dates = tr.getDateWithEstTrigger(1)
|
||||||
@@ -1380,11 +1011,11 @@ class PnlBI(ModelSingleton,ModelSQL, ModelView):
|
|||||||
config = Configuration.search(['id','>',0])[0]
|
config = Configuration.search(['id','>',0])[0]
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"resource": {"dashboard": 6},
|
"resource": {"dashboard": 2},
|
||||||
"params": {},
|
"params": {},
|
||||||
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30),
|
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30),
|
||||||
}
|
}
|
||||||
token = jwt.encode(payload, "798f256d3119a3292bf121196c2a38dddf2cad155c0b6b0b444efc34c6db197c", algorithm="HS256")
|
token = jwt.encode(payload, "5d95b70853af02897d1240e2ee4834e2bf065a5132b5d09840fbef6cf683ae45", algorithm="HS256")
|
||||||
logger.info("TOKEN:%s",token)
|
logger.info("TOKEN:%s",token)
|
||||||
if config.dark:
|
if config.dark:
|
||||||
url = f"metabase:{config.bi}/embed/dashboard/{token}#theme=night&bordered=true&titled=true"
|
url = f"metabase:{config.bi}/embed/dashboard/{token}#theme=night&bordered=true&titled=true"
|
||||||
|
|||||||
@@ -324,14 +324,7 @@ class Sale(metaclass=PoolMeta):
|
|||||||
if line_p:
|
if line_p:
|
||||||
#compute pnl
|
#compute pnl
|
||||||
Pnl = Pool().get('valuation.valuation')
|
Pnl = Pool().get('valuation.valuation')
|
||||||
pnl = Pnl.search([('line','=',line_p.id)])
|
Pnl.generate(line_p)
|
||||||
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)
|
|
||||||
|
|
||||||
if line.quantity_theorical:
|
if line.quantity_theorical:
|
||||||
OpenPosition = Pool().get('open.position')
|
OpenPosition = Pool().get('open.position')
|
||||||
@@ -733,14 +726,7 @@ class SaleLine(metaclass=PoolMeta):
|
|||||||
if purchase_lines:
|
if purchase_lines:
|
||||||
for pl in purchase_lines:
|
for pl in purchase_lines:
|
||||||
Pnl = Pool().get('valuation.valuation')
|
Pnl = Pool().get('valuation.valuation')
|
||||||
pnl = Pnl.search([('line','=',pl.id)])
|
Pnl.generate(pl)
|
||||||
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)
|
|
||||||
|
|
||||||
class SaleCreatePurchase(Wizard):
|
class SaleCreatePurchase(Wizard):
|
||||||
"Create mirror purchase"
|
"Create mirror purchase"
|
||||||
@@ -831,6 +817,20 @@ class Valuation(metaclass=PoolMeta):
|
|||||||
sale = fields.Many2One('sale.sale',"Sale")
|
sale = fields.Many2One('sale.sale',"Sale")
|
||||||
sale_line = fields.Many2One('sale.line',"Line")
|
sale_line = fields.Many2One('sale.line',"Line")
|
||||||
|
|
||||||
|
class ValuationLine(metaclass=PoolMeta):
|
||||||
|
"Last Valuation"
|
||||||
|
__name__ = 'valuation.valuation.line'
|
||||||
|
|
||||||
|
sale = fields.Many2One('sale.sale',"Sale")
|
||||||
|
sale_line = fields.Many2One('sale.line',"Line")
|
||||||
|
|
||||||
|
class ValuationReport(metaclass=PoolMeta):
|
||||||
|
"Valuation Report"
|
||||||
|
__name__ = 'valuation.report'
|
||||||
|
|
||||||
|
sale = fields.Many2One('sale.sale',"Sale")
|
||||||
|
sale_line = fields.Many2One('sale.line',"Line")
|
||||||
|
|
||||||
class ValuationDyn(metaclass=PoolMeta):
|
class ValuationDyn(metaclass=PoolMeta):
|
||||||
"Valuation"
|
"Valuation"
|
||||||
__name__ = 'valuation.valuation.dyn'
|
__name__ = 'valuation.valuation.dyn'
|
||||||
|
|||||||
@@ -29,3 +29,4 @@ xml:
|
|||||||
forex.xml
|
forex.xml
|
||||||
global_reporting.xml
|
global_reporting.xml
|
||||||
derivative.xml
|
derivative.xml
|
||||||
|
valuation.xml
|
||||||
532
modules/purchase_trade/valuation.py
Normal file
532
modules/purchase_trade/valuation.py
Normal file
@@ -0,0 +1,532 @@
|
|||||||
|
from trytond.model import fields
|
||||||
|
from trytond.report import Report
|
||||||
|
from trytond.pool import Pool, PoolMeta
|
||||||
|
from trytond.pyson import Bool, Eval, Id, If
|
||||||
|
from trytond.model import (ModelSQL, ModelView)
|
||||||
|
from trytond.tools import is_full_text, lstrip_wildcard
|
||||||
|
from trytond.transaction import Transaction, inactive_records
|
||||||
|
from decimal import getcontext, Decimal, ROUND_HALF_UP
|
||||||
|
from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr
|
||||||
|
from sql.conditionals import Case
|
||||||
|
from sql import Column, Literal
|
||||||
|
from sql.functions import CurrentTimestamp, DateTrunc
|
||||||
|
from trytond.wizard import Button, StateTransition, StateView, Wizard
|
||||||
|
from itertools import chain, groupby
|
||||||
|
from operator import itemgetter
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
|
from trytond.exceptions import UserWarning, UserError
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
VALTYPE = [
|
||||||
|
('priced', 'Price'),
|
||||||
|
('pur. priced', 'Pur. price'),
|
||||||
|
('pur. efp', 'Pur. efp'),
|
||||||
|
('sale priced', 'Sale price'),
|
||||||
|
('sale efp', 'Sale efp'),
|
||||||
|
('line fee', 'Line fee'),
|
||||||
|
('pur. fee', 'Pur. fee'),
|
||||||
|
('sale fee', 'Sale fee'),
|
||||||
|
('shipment fee', 'Shipment fee'),
|
||||||
|
('market', 'Market'),
|
||||||
|
('derivative', 'Derivative'),
|
||||||
|
]
|
||||||
|
|
||||||
|
class ValuationBase(ModelSQL):
|
||||||
|
purchase = fields.Many2One('purchase.purchase',"Purchase")
|
||||||
|
line = fields.Many2One('purchase.line',"Purch. Line")
|
||||||
|
date = fields.Date("Date")
|
||||||
|
type = fields.Selection(VALTYPE, "Type")
|
||||||
|
reference = fields.Char("Reference")
|
||||||
|
counterparty = fields.Many2One('party.party',"Counterparty")
|
||||||
|
product = fields.Many2One('product.product',"Product")
|
||||||
|
state = fields.Char("State")
|
||||||
|
price = fields.Numeric("Price",digits='unit')
|
||||||
|
currency = fields.Many2One('currency.currency',"Cur")
|
||||||
|
quantity = fields.Numeric("Quantity",digits='unit')
|
||||||
|
unit = fields.Many2One('product.uom',"Unit")
|
||||||
|
amount = fields.Numeric("Amount",digits='unit')
|
||||||
|
mtm = fields.Numeric("Mtm",digits='unit')
|
||||||
|
lot = fields.Many2One('lot.lot',"Lot")
|
||||||
|
base_amount = fields.Numeric("Base Amount",digits='unit')
|
||||||
|
rate = fields.Numeric("Rate", digits=(16,6))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _base_pnl(cls, *, line, lot, pnl_type, sale=None):
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
|
||||||
|
values = {
|
||||||
|
'purchase': line.purchase.id,
|
||||||
|
'line': line.id,
|
||||||
|
'type': pnl_type,
|
||||||
|
'date': Date.today(),
|
||||||
|
'lot': lot.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
if sale:
|
||||||
|
values['sale'] = sale.id
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _build_basis_pnl(cls, *, line, lot, sale_line, pc, sign):
|
||||||
|
Currency = Pool().get('currency.currency')
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
values = cls._base_pnl(
|
||||||
|
line=line,
|
||||||
|
lot=lot,
|
||||||
|
sale=sale_line.sale if sale_line else None,
|
||||||
|
pnl_type='sale priced' if sale_line else 'pur. priced'
|
||||||
|
)
|
||||||
|
|
||||||
|
qty = lot.get_current_quantity_converted()
|
||||||
|
|
||||||
|
values.update({
|
||||||
|
'reference': f"{pc.get_name()} / {pc.ratio}%",
|
||||||
|
'price': round(pc.price, 4),
|
||||||
|
'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
|
||||||
|
if pc.unfixed_qt == 0:
|
||||||
|
values['state'] = 'fixed'
|
||||||
|
elif pc.fixed_qt == 0:
|
||||||
|
values['state'] = 'unfixed'
|
||||||
|
else:
|
||||||
|
base = sale_line.quantity_theorical if sale_line else line.quantity_theorical
|
||||||
|
values['state'] = f"part. fixed {round(pc.fixed_qt / Decimal(base) * 100, 0)}%"
|
||||||
|
|
||||||
|
if pc.price and pc.ratio:
|
||||||
|
amount = round(pc.price * qty * Decimal(sign) * pc.ratio / 100, 4)
|
||||||
|
base_amount = amount
|
||||||
|
currency = sale_line.sale.currency.id if sale_line else line.purchase.currency.id
|
||||||
|
rate = Decimal(1)
|
||||||
|
if line.purchase.company.currency != currency:
|
||||||
|
with Transaction().set_context(date=Date.today()):
|
||||||
|
base_amount = Currency.compute(currency,amount, line.purchase.company.currency)
|
||||||
|
rate = round(amount / base_amount,6)
|
||||||
|
last_price = pc.get_last_price()
|
||||||
|
mtm = round(Decimal(last_price) * qty * Decimal(sign), 4) if last_price else Decimal(0)
|
||||||
|
|
||||||
|
values.update({
|
||||||
|
'quantity': round(qty, 5),
|
||||||
|
'amount': amount,
|
||||||
|
'base_amount': base_amount,
|
||||||
|
'rate': rate,
|
||||||
|
'mtm': round(amount - (mtm * pc.ratio / 100), 4),
|
||||||
|
'unit': sale_line.unit.id if sale_line else line.unit.id,
|
||||||
|
'currency': currency,
|
||||||
|
})
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _build_simple_pnl(cls, *, line, lot, sale_line, price, state, sign, pnl_type):
|
||||||
|
Currency = Pool().get('currency.currency')
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
values = cls._base_pnl(
|
||||||
|
line=line,
|
||||||
|
lot=lot,
|
||||||
|
sale=sale_line.sale if sale_line else None,
|
||||||
|
pnl_type=pnl_type
|
||||||
|
)
|
||||||
|
|
||||||
|
qty = lot.get_current_quantity_converted()
|
||||||
|
amount = round(price * qty * Decimal(sign), 4)
|
||||||
|
base_amount = amount
|
||||||
|
currency = sale_line.sale.currency.id if sale_line else line.purchase.currency.id
|
||||||
|
company_currency = sale_line.sale.company.currency if sale_line else line.purchase.company.currency
|
||||||
|
rate = Decimal(1)
|
||||||
|
if line.purchase.company.currency != currency:
|
||||||
|
with Transaction().set_context(date=Date.today()):
|
||||||
|
base_amount = Currency.compute(currency,amount, company_currency)
|
||||||
|
if base_amount and amount:
|
||||||
|
rate = round(amount / base_amount,6)
|
||||||
|
|
||||||
|
values.update({
|
||||||
|
'price': round(price, 4),
|
||||||
|
'quantity': round(qty, 5),
|
||||||
|
'amount': amount,
|
||||||
|
'base_amount': base_amount,
|
||||||
|
'rate': rate,
|
||||||
|
'mtm': Decimal(0),
|
||||||
|
'state': state,
|
||||||
|
'unit': sale_line.unit.id if sale_line else line.unit.id,
|
||||||
|
'currency': currency,
|
||||||
|
'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 values
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_pnl_price_from_line(cls, line):
|
||||||
|
price_lines = []
|
||||||
|
LotQt = Pool().get('lot.qt')
|
||||||
|
|
||||||
|
for lot in line.lots:
|
||||||
|
|
||||||
|
if line.price_type == 'basis':
|
||||||
|
for pc in line.price_summary or []:
|
||||||
|
values = cls._build_basis_pnl(line=line, lot=lot, sale_line=None, pc=pc, sign=-1)
|
||||||
|
if values:
|
||||||
|
price_lines.append(values)
|
||||||
|
|
||||||
|
elif line.price_type in ('priced', 'efp') and lot.lot_price:
|
||||||
|
price_lines.append(
|
||||||
|
cls._build_simple_pnl(
|
||||||
|
line=line,
|
||||||
|
lot=lot,
|
||||||
|
sale_line=None,
|
||||||
|
price=lot.lot_price,
|
||||||
|
state='fixed' if line.price_type == 'priced' else 'not fixed',
|
||||||
|
sign=-1,
|
||||||
|
pnl_type=f'pur. {line.price_type}'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
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),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
for sl in sale_lots:
|
||||||
|
sl_line = sl.sale_line
|
||||||
|
if not sl_line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if sl_line.price_type == 'basis':
|
||||||
|
for pc in sl_line.price_summary or []:
|
||||||
|
values = cls._build_basis_pnl(line=line, lot=sl, sale_line=sl_line, pc=pc, sign=+1)
|
||||||
|
if values:
|
||||||
|
price_lines.append(values)
|
||||||
|
|
||||||
|
elif sl_line.price_type in ('priced', 'efp'):
|
||||||
|
price_lines.append(
|
||||||
|
cls._build_simple_pnl(
|
||||||
|
line=line,
|
||||||
|
lot=sl,
|
||||||
|
sale_line=sl_line,
|
||||||
|
price=sl.lot_price_sale,
|
||||||
|
state='fixed' if sl_line.price_type == 'priced' else 'not fixed',
|
||||||
|
sign=+1,
|
||||||
|
pnl_type=f'sale {sl_line.price_type}'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return price_lines
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def group_fees_by_type_supplier(cls,line,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
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_pnl_fee_from_line(cls, line):
|
||||||
|
fee_lines = []
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
Currency = Pool().get('currency.currency')
|
||||||
|
FeeLots = Pool().get('fee.lots')
|
||||||
|
|
||||||
|
for lot in line.lots or []:
|
||||||
|
fl = FeeLots.search([('lot', '=', lot.id)])
|
||||||
|
if not fl:
|
||||||
|
continue
|
||||||
|
|
||||||
|
fees = [e.fee for e in fl]
|
||||||
|
for sf in cls.group_fees_by_type_supplier(line, fees):
|
||||||
|
|
||||||
|
price = Decimal(sf.get_price_per_qt())
|
||||||
|
if sf.currency != line.purchase.currency:
|
||||||
|
with Transaction().set_context(date=Date.today()):
|
||||||
|
price = Currency.compute(sf.currency, price, line.purchase.currency)
|
||||||
|
|
||||||
|
sign = -1 if sf.p_r == 'pay' else 1
|
||||||
|
|
||||||
|
fee_lines.append({
|
||||||
|
'lot': lot.id,
|
||||||
|
'sale': lot.sale_line.sale.id if lot.sale_line else None,
|
||||||
|
'purchase': line.purchase.id,
|
||||||
|
'line': line.id,
|
||||||
|
'type': (
|
||||||
|
'shipment fee' if sf.shipment_in
|
||||||
|
else 'sale fee' if sf.sale_line
|
||||||
|
else 'pur. fee'
|
||||||
|
),
|
||||||
|
'date': Date.today(),
|
||||||
|
'price': price,
|
||||||
|
'counterparty': sf.supplier.id,
|
||||||
|
'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
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_pnl_der_from_line(cls, line):
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
der_lines = []
|
||||||
|
|
||||||
|
for d in line.derivatives or []:
|
||||||
|
price = Decimal(d.price_index.get_price_per_qt(
|
||||||
|
d.price, line.unit, line.purchase.currency
|
||||||
|
))
|
||||||
|
|
||||||
|
mtm = Decimal(d.price_index.get_price(
|
||||||
|
Date.today(), line.unit, line.purchase.currency, True
|
||||||
|
))
|
||||||
|
|
||||||
|
der_lines.append({
|
||||||
|
'purchase': line.purchase.id,
|
||||||
|
'line': line.id,
|
||||||
|
'type': 'derivative',
|
||||||
|
'date': Date.today(),
|
||||||
|
'reference': d.price_index.price_index,
|
||||||
|
'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
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate(cls, line):
|
||||||
|
Date = Pool().get('ir.date')
|
||||||
|
Valuation = Pool().get('valuation.valuation')
|
||||||
|
ValuationLine = Pool().get('valuation.valuation.line')
|
||||||
|
|
||||||
|
Valuation.delete(Valuation.search([
|
||||||
|
('line', '=', line.id),
|
||||||
|
('date', '=', Date.today()),
|
||||||
|
]))
|
||||||
|
|
||||||
|
ValuationLine.delete(ValuationLine.search([
|
||||||
|
('line', '=', line.id),
|
||||||
|
]))
|
||||||
|
|
||||||
|
values = []
|
||||||
|
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):
|
||||||
|
"Valuation"
|
||||||
|
__name__ = 'valuation.valuation'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_totals(cls):
|
||||||
|
cursor = Transaction().connection.cursor()
|
||||||
|
table = cls.__table__()
|
||||||
|
|
||||||
|
sql = f"""
|
||||||
|
WITH ranked AS (
|
||||||
|
SELECT
|
||||||
|
CASE
|
||||||
|
WHEN line IS NOT NULL THEN 'P:' || line::text
|
||||||
|
WHEN sale_line IS NOT NULL THEN 'S:' || sale_line::text
|
||||||
|
END AS block_key,
|
||||||
|
date,
|
||||||
|
amount,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY
|
||||||
|
CASE
|
||||||
|
WHEN line IS NOT NULL THEN 'P:' || line::text
|
||||||
|
WHEN sale_line IS NOT NULL THEN 'S:' || sale_line::text
|
||||||
|
END
|
||||||
|
ORDER BY date DESC
|
||||||
|
) AS rn
|
||||||
|
FROM {table}
|
||||||
|
WHERE line IS NOT NULL
|
||||||
|
OR sale_line IS NOT NULL
|
||||||
|
),
|
||||||
|
current_prev AS (
|
||||||
|
SELECT
|
||||||
|
block_key,
|
||||||
|
MAX(CASE WHEN rn = 1 THEN amount END) AS amount_t,
|
||||||
|
MAX(CASE WHEN rn = 2 THEN amount END) AS amount_t1
|
||||||
|
FROM ranked
|
||||||
|
WHERE rn <= 2
|
||||||
|
GROUP BY block_key
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
COALESCE(SUM(amount_t), 0) AS total_t,
|
||||||
|
COALESCE(SUM(amount_t1), 0) AS total_t1,
|
||||||
|
COALESCE(SUM(amount_t), 0)
|
||||||
|
- COALESCE(SUM(amount_t1), 0) AS variation
|
||||||
|
FROM current_prev
|
||||||
|
"""
|
||||||
|
|
||||||
|
cursor.execute(sql)
|
||||||
|
total_t, total_t1, variation = cursor.fetchone()
|
||||||
|
|
||||||
|
return total_t, total_t1, variation
|
||||||
|
|
||||||
|
class ValuationLine(ValuationBase, ModelView):
|
||||||
|
"Last Valuation"
|
||||||
|
__name__ = 'valuation.valuation.line'
|
||||||
|
|
||||||
|
class ValuationDyn(ModelSQL,ModelView):
|
||||||
|
"Valuation"
|
||||||
|
__name__ = 'valuation.valuation.dyn'
|
||||||
|
|
||||||
|
r_purchase = fields.Many2One('purchase.purchase',"Purchase")
|
||||||
|
r_line = fields.Many2One('purchase.line',"Line")
|
||||||
|
r_date = fields.Date("Date")
|
||||||
|
r_type = fields.Selection(VALTYPE, "Type")
|
||||||
|
r_reference = fields.Char("Reference")
|
||||||
|
r_counterparty = fields.Many2One('party.party',"Counterparty")
|
||||||
|
r_product = fields.Many2One('product.product',"Product")
|
||||||
|
r_state = fields.Char("State")
|
||||||
|
r_price = fields.Numeric("Price",digits='r_unit')
|
||||||
|
r_currency = fields.Many2One('currency.currency',"Cur")
|
||||||
|
r_quantity = fields.Numeric("Quantity",digits='r_unit')
|
||||||
|
r_unit = fields.Many2One('product.uom',"Unit")
|
||||||
|
r_amount = fields.Numeric("Amount",digits='r_unit')
|
||||||
|
r_base_amount = fields.Numeric("Base Amount",digits='r_unit')
|
||||||
|
r_rate = fields.Numeric("Rate",digits=(16,6))
|
||||||
|
r_mtm = fields.Numeric("Mtm",digits='r_unit')
|
||||||
|
r_lot = fields.Many2One('lot.lot',"Lot")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def table_query(cls):
|
||||||
|
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'),
|
||||||
|
Max(val.id).as_('id'),
|
||||||
|
Max(val.purchase).as_('r_purchase'),
|
||||||
|
Max(val.line).as_('r_line'),
|
||||||
|
Max(val.date).as_('r_date'),
|
||||||
|
val.type.as_('r_type'),
|
||||||
|
Max(val.reference).as_('r_reference'),
|
||||||
|
val.counterparty.as_('r_counterparty'),
|
||||||
|
Max(val.product).as_('r_product'),
|
||||||
|
val.state.as_('r_state'),
|
||||||
|
Avg(val.price).as_('r_price'),
|
||||||
|
Max(val.currency).as_('r_currency'),
|
||||||
|
Sum(val.quantity).as_('r_quantity'),
|
||||||
|
Max(val.unit).as_('r_unit'),
|
||||||
|
Sum(val.amount).as_('r_amount'),
|
||||||
|
Sum(val.base_amount).as_('r_base_amount'),
|
||||||
|
Sum(val.rate).as_('r_rate'),
|
||||||
|
Sum(val.mtm).as_('r_mtm'),
|
||||||
|
Max(val.lot).as_('r_lot'),
|
||||||
|
where=wh,
|
||||||
|
group_by=[val.type,val.counterparty,val.state])
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
|
class ValuationReport(ValuationBase, ModelView):
|
||||||
|
"Valuation Report"
|
||||||
|
__name__ = 'valuation.report'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def table_query(cls):
|
||||||
|
Valuation = Pool().get('valuation.valuation')
|
||||||
|
val = Valuation.__table__()
|
||||||
|
context = Transaction().context
|
||||||
|
valuation_date = context.get('valuation_date')
|
||||||
|
wh = (val.date == valuation_date)
|
||||||
|
|
||||||
|
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_('purchase'),
|
||||||
|
val.sale.as_('sale'),
|
||||||
|
val.sale_line.as_('sale_line'),
|
||||||
|
val.line.as_('line'),
|
||||||
|
val.date.as_('date'),
|
||||||
|
val.type.as_('type'),
|
||||||
|
val.reference.as_('reference'),
|
||||||
|
val.counterparty.as_('counterparty'),
|
||||||
|
val.product.as_('product'),
|
||||||
|
val.state.as_('state'),
|
||||||
|
val.price.as_('price'),
|
||||||
|
val.currency.as_('currency'),
|
||||||
|
val.quantity.as_('quantity'),
|
||||||
|
val.unit.as_('unit'),
|
||||||
|
val.amount.as_('amount'),
|
||||||
|
val.base_amount.as_('base_amount'),
|
||||||
|
val.rate.as_('rate'),
|
||||||
|
val.mtm.as_('mtm'),
|
||||||
|
val.lot.as_('lot'),
|
||||||
|
where=wh)
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
|
class ValuationReportContext(ModelView):
|
||||||
|
"Valuation Report Context"
|
||||||
|
__name__ = 'valuation.report.context'
|
||||||
|
|
||||||
|
valuation_date = fields.Date("Valuation date")
|
||||||
|
supplier = fields.Many2One('party.party',"Supplier")
|
||||||
|
client = fields.Many2One('party.party',"Client")
|
||||||
|
product = fields.Many2One('product.product',"Product")
|
||||||
|
purchase = fields.Many2One('purchase.purchase', "Purchase")
|
||||||
|
sale = fields.Many2One('sale.sale',"Sale")
|
||||||
|
state = fields.Selection([
|
||||||
|
('all', 'All'),
|
||||||
|
('open', 'Open'),
|
||||||
|
('fixed', 'Fixed'),
|
||||||
|
('hedged', 'Hedged')
|
||||||
|
], 'State')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_valuation_date(cls):
|
||||||
|
pool = Pool()
|
||||||
|
Date = pool.get('ir.date')
|
||||||
|
return Date.today()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_state(cls):
|
||||||
|
return 'all'
|
||||||
51
modules/purchase_trade/valuation.xml
Normal file
51
modules/purchase_trade/valuation.xml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<tryton>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_tree_sequence3">
|
||||||
|
<field name="model">valuation.valuation</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="name">valuation_tree_sequence3</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_graph">
|
||||||
|
<field name="model">valuation.valuation</field>
|
||||||
|
<field name="type">graph</field>
|
||||||
|
<field name="name">valuation_graph</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_graph2">
|
||||||
|
<field name="model">valuation.valuation</field>
|
||||||
|
<field name="type">graph</field>
|
||||||
|
<field name="name">valuation_graph2</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_tree_sequence4">
|
||||||
|
<field name="model">valuation.valuation.dyn</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="name">valuation_tree_sequence4</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="valuation_context_view_form">
|
||||||
|
<field name="model">valuation.report.context</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="name">valuation_context_form</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.ui.view" id="valuation_view_list">
|
||||||
|
<field name="model">valuation.report</field>
|
||||||
|
<field name="type">tree</field>
|
||||||
|
<field name="name">valuation_list</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.action.act_window" id="act_valuation_form">
|
||||||
|
<field name="name">Valuation</field>
|
||||||
|
<field name="res_model">valuation.report</field>
|
||||||
|
<field name="context_model">valuation.report.context</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.action.act_window.view" id="act_valuation_form_view">
|
||||||
|
<field name="sequence" eval="70"/>
|
||||||
|
<field name="view" ref="valuation_view_list"/>
|
||||||
|
<field name="act_window" ref="act_valuation_form"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
parent="purchase_trade.menu_global_reporting"
|
||||||
|
sequence="120"
|
||||||
|
action="act_valuation_form"
|
||||||
|
id="menu_valuation_form"/>
|
||||||
|
</data>
|
||||||
|
</tryton>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
<form>
|
<form>
|
||||||
<label name="bi"/>
|
<label name="bi"/>
|
||||||
<field name="bi"/>
|
<field name="bi"/>
|
||||||
|
<label name="dashboard"/>
|
||||||
|
<field name="dashboard"/>
|
||||||
<label name="dark"/>
|
<label name="dark"/>
|
||||||
<field name="dark"/>
|
<field name="dark"/>
|
||||||
</form>
|
</form>
|
||||||
16
modules/purchase_trade/view/valuation_context_form.xml
Normal file
16
modules/purchase_trade/view/valuation_context_form.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<form>
|
||||||
|
<label name="valuation_date"/>
|
||||||
|
<field name="valuation_date"/>
|
||||||
|
<label name="supplier"/>
|
||||||
|
<field name="supplier"/>
|
||||||
|
<label name="client"/>
|
||||||
|
<field name="client"/>
|
||||||
|
<label name="purchase"/>
|
||||||
|
<field name="purchase"/>
|
||||||
|
<label name="sale"/>
|
||||||
|
<field name="sale"/>
|
||||||
|
<label name="product"/>
|
||||||
|
<field name="product"/>
|
||||||
|
<label name="state"/>
|
||||||
|
<field name="state"/>
|
||||||
|
</form>
|
||||||
15
modules/purchase_trade/view/valuation_list.xml
Normal file
15
modules/purchase_trade/view/valuation_list.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<tree>
|
||||||
|
<field name="lot"/>
|
||||||
|
<field name="purchase"/>
|
||||||
|
<field name="sale"/>
|
||||||
|
<field name="type"/>
|
||||||
|
<field name="reference"/>
|
||||||
|
<field name="counterparty"/>
|
||||||
|
<field name="state"/>
|
||||||
|
<field name="price"/>
|
||||||
|
<field name="quantity" symbol="unit"/>
|
||||||
|
<field name="amount"/>
|
||||||
|
<field name="base_amount" sum="1"/>
|
||||||
|
<field name="rate"/>
|
||||||
|
<field name="mtm" optional="1" sum="1"/>
|
||||||
|
</tree>
|
||||||
Reference in New Issue
Block a user