Merge dev from main #1

Merged
admin merged 25 commits from main into dev 2026-01-05 13:03:46 +00:00
15 changed files with 759 additions and 558 deletions

View File

@@ -1685,7 +1685,7 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
@fields.depends(
'type', 'quantity', 'unit_price',
'purchase', '_parent_purchase.currency')
'purchase', '_parent_purchase.currency','premium')
def on_change_with_amount(self):
if (self.type == 'line'
and self.quantity is not None

View File

@@ -3,7 +3,32 @@
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():
Pool.register(
@@ -69,8 +94,11 @@ def register():
fee.Fee,
fee.FeeLots,
purchase.FeeLots,
fee.Valuation,
fee.ValuationDyn,
valuation.Valuation,
valuation.ValuationLine,
valuation.ValuationDyn,
valuation.ValuationReport,
valuation.ValuationReportContext,
derivative.Derivative,
derivative.DerivativeMatch,
derivative.MatchWizardStart,
@@ -151,6 +179,8 @@ def register():
sale.SaleCreatePurchaseInput,
sale.Derivative,
sale.Valuation,
sale.ValuationLine,
sale.ValuationReport,
sale.Fee,
sale.Lot,
sale.FeeLots,

View File

@@ -193,7 +193,7 @@ class Dashboard(ModelSQL, ModelView):
self.chatbot = 'chatbot:' + json.dumps(dial, ensure_ascii=False)
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.
"""
@@ -208,29 +208,42 @@ class Dashboard(ModelSQL, ModelView):
rates = CurrencyRate.search(
[('currency', '=', to_currency.id)],
order=[('date', 'DESC')],
limit=2,
limit=5,
)
if not rates:
return None, None
return None, None, None, None, None
# Calcul du taux EUR/USD
# 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
last_rate = rates[0].rate
prev_rate = rates[1].rate if len(rates) > 1 else None
f1 = rates[0].rate
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:
# last_rate = 1 / last_rate if last_rate else None
# prev_rate = 1 / prev_rate if prev_rate else None
if last_rate and prev_rate:
return round(1/last_rate,6), round(1/prev_rate,6)
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
def get_tremor(self,name):
Pnl = Pool().get('valuation.valuation')
pnls = Pnl.search(['id','>',0])
pnl_amount = "{:,.0f}".format(round(sum([e.amount for e in pnls]),0))
Configuration = Pool().get('gr.configuration')
config = Configuration.search(['id','>',0])[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')
opens = Open.search(['id','>',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')
draft = Shipment.search(['state','=','draft'])
shipment_d = len(draft)
val = Purchase.search(['state','=','started'])
val = Shipment.search(['state','=','started'])
shipment_s = len(val)
conf = Purchase.search(['state','=','received'])
conf = Shipment.search(['state','=','received'])
shipment_r = len(conf)
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)
val = Lot.search(['sale_line','=',None])
val = Lot.search([('sale_line','=',None),('line','!=',None),('lot_type','=','physic')])
lot_a = len(val)
conf = Lot.search(['lot_type','=','physic'])
lot_al = len(conf)
Invoice = Pool().get('account.invoice')
invs = Invoice.search(['type','=','in'])
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'])
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')
accs = AccountMove.search(['id','>',0])
move_cash = len(accs)
accs = AccountMove.search([('journal','=',3),('state','!=','posted')])
pay_val = len(accs)
accs = AccountMove.search([('journal','=',3),('state','=','posted')])
pay_posted = len(accs)
return (
"https://srv413259.hstgr.cloud/dashboard/index.html?pnl_amount="
config.dashboard +
"/dashboard/index.html?pnl_amount="
+ str(pnl_amount)
+ "&pnl_variation="
+ str(pnl_variation)
+ "&exposure="
+ str(exposure)
+ "&topay="
+ str(topay)
+ "&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="
+ str(draft_p)
+ "&val_p="
@@ -312,14 +356,22 @@ class Dashboard(ModelSQL, ModelView):
+ str(lot_m)
+ "&lot_a="
+ str(lot_a)
+ "&lot_al="
+ str(lot_al)
+ "&inv_p="
+ str(inv_p)
+ "&inv_p_p="
+ str(inv_p_p)
+ "&inv_p_np="
+ str(inv_p_np)
+ "&inv_s="
+ str(inv_s)
+ "&move_cash="
+ str(move_cash)
+ "&inv_s_p="
+ 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')
Date = Pool().get('ir.date')
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:
variation = ((last_rate - prev_rate) / prev_rate) * 100 if prev_rate else 0
direction = "📈" if variation > 0 else "📉"

View File

@@ -21,117 +21,6 @@ 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 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(func):
@wraps(func)

View File

@@ -19,26 +19,6 @@ this repository contains the full copyright notices and license terms. -->
<field name="name">fee_tree_sequence</field>
</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">
<field name="model">fee.fee</field>
<field name="type">tree</field>

View File

@@ -12,4 +12,5 @@ class GRConfiguration(ModelSingleton, ModelSQL, ModelView):
__name__ = 'gr.configuration'
bi = fields.Char("BI connexion")
dashboard = fields.Char("Dashboard connexion")
dark = fields.Boolean("Dark mode")

View File

@@ -1168,6 +1168,7 @@ class LotQt(
@classmethod
def validate(cls, lotqts):
super(LotQt, cls).validate(lotqts)
Date = Pool().get('ir.date')
#Update Move
for lqt in lotqts:
cls.updateMove(lqt.lot_move)
@@ -1177,15 +1178,15 @@ class LotQt(
if lqt.lot_p and lqt.lot_quantity > 0:
pl = lqt.lot_p.line
logger.info("VALIDATE_LQT_PL:%s",pl)
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 = Pool().get('valuation.valuation')
# pnl = Pnl.search([('line','=',pl.id),('date','=',Date.today())])
# 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)
#Open position update
if pl.quantity_theorical:

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)
@@ -380,19 +371,11 @@ class Purchase(metaclass=PoolMeta):
Decimal(str(line.quantity))
.quantize(Decimal("0.00001"))
)
Line.save([line])
#compute pnl
Pnl = Pool().get('valuation.valuation')
pnl = Pnl.search([('line','=',line.id)])
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)
Pnl.generate(line)
if line.quantity_theorical:
OpenPosition = Pool().get('open.position')
@@ -655,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)
@@ -1380,11 +1011,11 @@ class PnlBI(ModelSingleton,ModelSQL, ModelView):
config = Configuration.search(['id','>',0])[0]
payload = {
"resource": {"dashboard": 6},
"resource": {"dashboard": 2},
"params": {},
"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)
if config.dark:
url = f"metabase:{config.bi}/embed/dashboard/{token}#theme=night&bordered=true&titled=true"

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"
@@ -831,6 +817,20 @@ class Valuation(metaclass=PoolMeta):
sale = fields.Many2One('sale.sale',"Sale")
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):
"Valuation"
__name__ = 'valuation.valuation.dyn'

View File

@@ -28,4 +28,5 @@ xml:
party.xml
forex.xml
global_reporting.xml
derivative.xml
derivative.xml
valuation.xml

View 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'

View 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>

View File

@@ -1,6 +1,8 @@
<form>
<label name="bi"/>
<field name="bi"/>
<label name="dashboard"/>
<field name="dashboard"/>
<label name="dark"/>
<field name="dark"/>
</form>

View 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>

View 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>