This commit is contained in:
2026-05-24 19:32:03 +02:00
parent 1d398ab4b5
commit 54309a1980
11 changed files with 662 additions and 0 deletions

View File

@@ -82,6 +82,14 @@ def register():
ctrm_reporting.CTRMContractPerformanceContext,
ctrm_reporting.CTRMScheduling,
ctrm_reporting.CTRMSchedulingContext,
ctrm_reporting.CTRMAccruals,
ctrm_reporting.CTRMAccrualsContext,
ctrm_reporting.CTRMSettlements,
ctrm_reporting.CTRMSettlementsContext,
ctrm_reporting.CTRMCashFlow,
ctrm_reporting.CTRMCashFlowContext,
ctrm_reporting.CTRMGLReconciliation,
ctrm_reporting.CTRMGLReconciliationContext,
configuration.Configuration,
pricing.ImportPricesStart,
pricing.ImportPricesResult,

View File

@@ -1545,3 +1545,442 @@ class CTRMScheduling(ModelSQL, ModelView):
Coalesce(lot.lot_qt, 0).as_('quantity'),
lot.lot_unit_line.as_('unit'),
where=where))
class CTRMFinanceDateContextMixin:
from_date = fields.Date("From Date")
to_date = fields.Date("To Date")
party = fields.Many2One('party.party', "Party")
currency = fields.Many2One('currency.currency', "Currency")
@classmethod
def default_to_date(cls):
Date = Pool().get('ir.date')
return Date.today()
class CTRMAccrualsContext(CTRMFinanceDateContextMixin, ModelView):
"CTRM Accruals Context"
__name__ = 'ctrm.reporting.finance.accruals.context'
product = fields.Many2One('product.product', "Product")
purchase = fields.Many2One('purchase.purchase', "Purchase")
sale = fields.Many2One('sale.sale', "Sale")
type = fields.Selection([
(None, ''),
('physical', 'Physical'),
('fee', 'Fee'),
('derivative', 'Derivative'),
], "Accrual Type")
class CTRMAccruals(ModelSQL, ModelView):
"CTRM Accruals"
__name__ = 'ctrm.reporting.finance.accruals'
valuation_date = fields.Date("Valuation Date")
accrual_type = fields.Selection([
('physical', 'Physical'),
('fee', 'Fee'),
('derivative', 'Derivative'),
], "Accrual Type")
lot = fields.Many2One('lot.lot', "Lot")
product = fields.Many2One('product.product', "Product")
party = fields.Many2One('party.party', "Party")
purchase = fields.Many2One('purchase.purchase', "Purchase")
purchase_line = fields.Many2One('purchase.line', "Purchase Line")
sale = fields.Many2One('sale.sale', "Sale")
sale_line = fields.Many2One('sale.line', "Sale Line")
reference = fields.Char("Reference")
quantity = fields.Numeric("Quantity", digits=(16, 5))
unit = fields.Many2One('product.uom', "Unit")
amount = fields.Numeric("Amount", digits=(16, 2))
currency = fields.Many2One('currency.currency', "Currency")
state = fields.Char("State")
@classmethod
def table_query(cls):
pool = Pool()
ValuationLine = pool.get('valuation.valuation.line')
Lot = pool.get('lot.lot')
val = ValuationLine.__table__()
lot = Lot.__table__()
accrual_type = Case(
(val.type == 'derivative', 'derivative'),
(val.type.in_(['line fee', 'pur. fee', 'sale fee',
'shipment fee']), 'fee'),
else_='physical')
context = Transaction().context
where = (
val.type.in_(ALL_VALUATION_TYPES)
& ((val.lot == Null)
| ((lot.invoice_line == Null)
& (lot.sale_invoice_line == Null))))
if context.get('from_date'):
where &= val.date >= context['from_date']
if context.get('to_date'):
where &= val.date <= context['to_date']
if context.get('party'):
where &= val.counterparty == context['party']
if context.get('currency'):
where &= val.currency == context['currency']
if context.get('product'):
where &= val.product == context['product']
if context.get('purchase'):
where &= val.purchase == context['purchase']
if context.get('sale'):
where &= val.sale == context['sale']
if context.get('type'):
where &= accrual_type == context['type']
return (
val
.join(lot, 'LEFT', condition=val.lot == lot.id)
.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.date.as_('valuation_date'),
accrual_type.as_('accrual_type'),
val.lot.as_('lot'),
val.product.as_('product'),
val.counterparty.as_('party'),
val.purchase.as_('purchase'),
val.line.as_('purchase_line'),
val.sale.as_('sale'),
val.sale_line.as_('sale_line'),
val.reference.as_('reference'),
val.quantity.as_('quantity'),
val.unit.as_('unit'),
val.amount.as_('amount'),
val.currency.as_('currency'),
val.state.as_('state'),
where=where))
class CTRMSettlementsContext(CTRMFinanceDateContextMixin, ModelView):
"CTRM Settlements Context"
__name__ = 'ctrm.reporting.finance.settlements.context'
invoice_type = fields.Selection([
(None, ''),
('in', 'Supplier'),
('out', 'Customer'),
], "Invoice Type")
state = fields.Selection([
(None, ''),
('draft', 'Draft'),
('validated', 'Validated'),
('posted', 'Posted'),
('paid', 'Paid'),
('cancelled', 'Cancelled'),
], "State")
payment_status = fields.Selection([
(None, ''),
('not', 'Not Paid'),
('partial', 'Partially Paid'),
('paid', 'Paid'),
], "Payment Status")
class CTRMSettlements(ModelSQL, ModelView):
"CTRM Settlements and Invoicing"
__name__ = 'ctrm.reporting.finance.settlements'
invoice = fields.Many2One('account.invoice', "Invoice")
invoice_type = fields.Selection([
('in', 'Supplier'),
('out', 'Customer'),
], "Invoice Type")
number = fields.Char("Number")
reference = fields.Char("Reference")
invoice_date = fields.Date("Invoice Date")
party = fields.Many2One('party.party', "Party")
state = fields.Char("State")
currency = fields.Many2One('currency.currency', "Currency")
invoice_amount = fields.Numeric("Invoice Amount", digits=(16, 2))
paid_amount = fields.Numeric("Paid Amount", digits=(16, 2))
open_amount = fields.Numeric("Open Amount", digits=(16, 2))
payment_status = fields.Selection([
('not', 'Not Paid'),
('partial', 'Partially Paid'),
('paid', 'Paid'),
], "Payment Status")
move = fields.Many2One('account.move', "Move")
reconciliation = fields.Integer("Reconciliation")
@classmethod
def table_query(cls):
pool = Pool()
Invoice = pool.get('account.invoice')
InvoicePayment = pool.get('account.invoice-account.move.line')
MoveLine = pool.get('account.move.line')
invoice = Invoice.__table__()
invoice_payment = InvoicePayment.__table__()
line = MoveLine.__table__()
amount = Coalesce(invoice.total_amount_cache, 0)
paid = Coalesce(Sum(line.amount_second_currency), 0)
open_amount = amount - paid
payment_status = Case(
(open_amount == 0, 'paid'),
(paid != 0, 'partial'),
else_='not')
context = Transaction().context
where = invoice.id != Null
if context.get('from_date'):
where &= invoice.invoice_date >= context['from_date']
if context.get('to_date'):
where &= invoice.invoice_date <= context['to_date']
if context.get('party'):
where &= invoice.party == context['party']
if context.get('currency'):
where &= invoice.currency == context['currency']
if context.get('invoice_type'):
where &= invoice.type == context['invoice_type']
if context.get('state'):
where &= invoice.state == context['state']
grouped = (
invoice
.join(invoice_payment, 'LEFT',
condition=invoice_payment.invoice == invoice.id)
.join(line, 'LEFT',
condition=line.id == invoice_payment.line)
.select(
invoice.id.as_('id'),
invoice.id.as_('invoice'),
invoice.type.as_('invoice_type'),
invoice.number.as_('number'),
invoice.reference.as_('reference'),
invoice.invoice_date.as_('invoice_date'),
invoice.party.as_('party'),
invoice.state.as_('state'),
invoice.currency.as_('currency'),
amount.as_('invoice_amount'),
paid.as_('paid_amount'),
open_amount.as_('open_amount'),
payment_status.as_('payment_status'),
invoice.move.as_('move'),
Max(line.reconciliation).as_('reconciliation'),
where=where,
group_by=[
invoice.id, invoice.type, invoice.number,
invoice.reference, invoice.invoice_date, invoice.party,
invoice.state, invoice.currency,
invoice.total_amount_cache, invoice.move,
]))
outer_where = grouped.id != Null
if context.get('payment_status'):
outer_where &= grouped.payment_status == context['payment_status']
return grouped.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
grouped.id.as_('id'),
grouped.invoice.as_('invoice'),
grouped.invoice_type.as_('invoice_type'),
grouped.number.as_('number'),
grouped.reference.as_('reference'),
grouped.invoice_date.as_('invoice_date'),
grouped.party.as_('party'),
grouped.state.as_('state'),
grouped.currency.as_('currency'),
grouped.invoice_amount.as_('invoice_amount'),
grouped.paid_amount.as_('paid_amount'),
grouped.open_amount.as_('open_amount'),
grouped.payment_status.as_('payment_status'),
grouped.move.as_('move'),
grouped.reconciliation.as_('reconciliation'),
where=outer_where)
class CTRMCashFlowContext(CTRMFinanceDateContextMixin, ModelView):
"CTRM Cash Flow Context"
__name__ = 'ctrm.reporting.finance.cashflow.context'
overdue_only = fields.Boolean("Overdue Only")
class CTRMCashFlow(ModelSQL, ModelView):
"CTRM Cash Flow Forecast"
__name__ = 'ctrm.reporting.finance.cashflow'
move_line = fields.Many2One('account.move.line', "Move Line")
move = fields.Many2One('account.move', "Move")
due_date = fields.Date("Due Date")
posting_date = fields.Date("Posting Date")
party = fields.Many2One('party.party', "Party")
account = fields.Many2One('account.account', "Account")
currency = fields.Many2One('currency.currency', "Currency")
amount = fields.Numeric("Amount", digits=(16, 2))
debit = fields.Numeric("Debit", digits=(16, 2))
credit = fields.Numeric("Credit", digits=(16, 2))
second_currency = fields.Many2One('currency.currency', "Second Currency")
amount_second_currency = fields.Numeric(
"Amount Second Currency", digits=(16, 2))
description = fields.Char("Description")
reference = fields.Char("Reference")
journal = fields.Many2One('account.journal', "Journal")
@classmethod
def table_query(cls):
pool = Pool()
MoveLine = pool.get('account.move.line')
Move = pool.get('account.move')
Company = pool.get('company.company')
line = MoveLine.__table__()
move = Move.__table__()
company = Company.__table__()
currency = Coalesce(line.second_currency, company.currency)
amount = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
context = Transaction().context
where = (
(line.reconciliation == Null)
& (line.maturity_date != Null))
if context.get('from_date'):
where &= line.maturity_date >= context['from_date']
if context.get('to_date'):
where &= line.maturity_date <= context['to_date']
if context.get('party'):
where &= line.party == context['party']
if context.get('currency'):
where &= currency == context['currency']
if context.get('overdue_only'):
Date = Pool().get('ir.date')
where &= line.maturity_date < Date.today()
return (
line
.join(move, condition=line.move == move.id)
.join(company, condition=move.company == company.id)
.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
line.id.as_('id'),
line.id.as_('move_line'),
line.move.as_('move'),
line.maturity_date.as_('due_date'),
move.date.as_('posting_date'),
line.party.as_('party'),
line.account.as_('account'),
currency.as_('currency'),
amount.as_('amount'),
line.debit.as_('debit'),
line.credit.as_('credit'),
line.second_currency.as_('second_currency'),
line.amount_second_currency.as_('amount_second_currency'),
line.description.as_('description'),
Coalesce(line.ext_ref, move.ext_ref).as_('reference'),
move.journal.as_('journal'),
where=where))
class CTRMGLReconciliationContext(CTRMFinanceDateContextMixin, ModelView):
"CTRM GL Reconciliation Context"
__name__ = 'ctrm.reporting.finance.gl.context'
account = fields.Many2One('account.account', "Account")
lot = fields.Many2One('lot.lot', "Lot")
unreconciled_only = fields.Boolean("Unreconciled Only")
class CTRMGLReconciliation(ModelSQL, ModelView):
"CTRM GL Reconciliation"
__name__ = 'ctrm.reporting.finance.gl'
move_line = fields.Many2One('account.move.line', "Move Line")
move = fields.Many2One('account.move', "Move")
posting_date = fields.Date("Posting Date")
journal = fields.Many2One('account.journal', "Journal")
account = fields.Many2One('account.account', "Account")
party = fields.Many2One('party.party', "Party")
lot = fields.Many2One('lot.lot', "Lot")
fee = fields.Many2One('fee.fee', "Fee")
invoice = fields.Many2One('account.invoice', "Invoice")
currency = fields.Many2One('currency.currency', "Currency")
debit = fields.Numeric("Debit", digits=(16, 2))
credit = fields.Numeric("Credit", digits=(16, 2))
balance = fields.Numeric("Balance", digits=(16, 2))
second_currency = fields.Many2One('currency.currency', "Second Currency")
amount_second_currency = fields.Numeric(
"Amount Second Currency", digits=(16, 2))
reconciliation = fields.Integer("Reconciliation")
move_state = fields.Char("Move State")
description = fields.Char("Description")
reference = fields.Char("Reference")
@classmethod
def table_query(cls):
pool = Pool()
MoveLine = pool.get('account.move.line')
Move = pool.get('account.move')
Invoice = pool.get('account.invoice')
Company = pool.get('company.company')
line = MoveLine.__table__()
move = Move.__table__()
invoice = Invoice.__table__()
company = Company.__table__()
currency = Coalesce(line.second_currency, company.currency)
balance = Coalesce(line.debit, 0) - Coalesce(line.credit, 0)
context = Transaction().context
where = (line.lot != Null) | (line.fee != Null) | (invoice.id != Null)
if context.get('from_date'):
where &= move.date >= context['from_date']
if context.get('to_date'):
where &= move.date <= context['to_date']
if context.get('party'):
where &= line.party == context['party']
if context.get('currency'):
where &= currency == context['currency']
if context.get('account'):
where &= line.account == context['account']
if context.get('lot'):
where &= line.lot == context['lot']
if context.get('unreconciled_only'):
where &= line.reconciliation == Null
return (
line
.join(move, condition=line.move == move.id)
.join(company, condition=move.company == company.id)
.join(invoice, 'LEFT', condition=invoice.move == move.id)
.select(
Literal(0).as_('create_uid'),
CurrentTimestamp().as_('create_date'),
Literal(None).as_('write_uid'),
Literal(None).as_('write_date'),
line.id.as_('id'),
line.id.as_('move_line'),
line.move.as_('move'),
move.date.as_('posting_date'),
move.journal.as_('journal'),
line.account.as_('account'),
line.party.as_('party'),
line.lot.as_('lot'),
line.fee.as_('fee'),
invoice.id.as_('invoice'),
currency.as_('currency'),
line.debit.as_('debit'),
line.credit.as_('credit'),
balance.as_('balance'),
line.second_currency.as_('second_currency'),
line.amount_second_currency.as_('amount_second_currency'),
line.reconciliation.as_('reconciliation'),
move.state.as_('move_state'),
line.description.as_('description'),
Coalesce(line.ext_ref, move.ext_ref).as_('reference'),
where=where))

View File

@@ -240,6 +240,86 @@
<field name="view" ref="ctrm_scheduling_view_list"/>
<field name="act_window" ref="act_ctrm_scheduling"/>
</record>
<record model="ir.ui.view" id="ctrm_accruals_context_view_form">
<field name="model">ctrm.reporting.finance.accruals.context</field>
<field name="type">form</field>
<field name="name">ctrm_accruals_context_form</field>
</record>
<record model="ir.ui.view" id="ctrm_accruals_view_list">
<field name="model">ctrm.reporting.finance.accruals</field>
<field name="type">tree</field>
<field name="name">ctrm_accruals_list</field>
</record>
<record model="ir.action.act_window" id="act_ctrm_accruals">
<field name="name">Accruals</field>
<field name="res_model">ctrm.reporting.finance.accruals</field>
<field name="context_model">ctrm.reporting.finance.accruals.context</field>
</record>
<record model="ir.action.act_window.view" id="act_ctrm_accruals_view">
<field name="sequence" eval="10"/>
<field name="view" ref="ctrm_accruals_view_list"/>
<field name="act_window" ref="act_ctrm_accruals"/>
</record>
<record model="ir.ui.view" id="ctrm_settlements_context_view_form">
<field name="model">ctrm.reporting.finance.settlements.context</field>
<field name="type">form</field>
<field name="name">ctrm_settlements_context_form</field>
</record>
<record model="ir.ui.view" id="ctrm_settlements_view_list">
<field name="model">ctrm.reporting.finance.settlements</field>
<field name="type">tree</field>
<field name="name">ctrm_settlements_list</field>
</record>
<record model="ir.action.act_window" id="act_ctrm_settlements">
<field name="name">Settlements and Invoicing</field>
<field name="res_model">ctrm.reporting.finance.settlements</field>
<field name="context_model">ctrm.reporting.finance.settlements.context</field>
</record>
<record model="ir.action.act_window.view" id="act_ctrm_settlements_view">
<field name="sequence" eval="10"/>
<field name="view" ref="ctrm_settlements_view_list"/>
<field name="act_window" ref="act_ctrm_settlements"/>
</record>
<record model="ir.ui.view" id="ctrm_cashflow_context_view_form">
<field name="model">ctrm.reporting.finance.cashflow.context</field>
<field name="type">form</field>
<field name="name">ctrm_cashflow_context_form</field>
</record>
<record model="ir.ui.view" id="ctrm_cashflow_view_list">
<field name="model">ctrm.reporting.finance.cashflow</field>
<field name="type">tree</field>
<field name="name">ctrm_cashflow_list</field>
</record>
<record model="ir.action.act_window" id="act_ctrm_cashflow">
<field name="name">Cash Flow Forecast</field>
<field name="res_model">ctrm.reporting.finance.cashflow</field>
<field name="context_model">ctrm.reporting.finance.cashflow.context</field>
</record>
<record model="ir.action.act_window.view" id="act_ctrm_cashflow_view">
<field name="sequence" eval="10"/>
<field name="view" ref="ctrm_cashflow_view_list"/>
<field name="act_window" ref="act_ctrm_cashflow"/>
</record>
<record model="ir.ui.view" id="ctrm_gl_reconciliation_context_view_form">
<field name="model">ctrm.reporting.finance.gl.context</field>
<field name="type">form</field>
<field name="name">ctrm_gl_reconciliation_context_form</field>
</record>
<record model="ir.ui.view" id="ctrm_gl_reconciliation_view_list">
<field name="model">ctrm.reporting.finance.gl</field>
<field name="type">tree</field>
<field name="name">ctrm_gl_reconciliation_list</field>
</record>
<record model="ir.action.act_window" id="act_ctrm_gl_reconciliation">
<field name="name">GL Reconciliation</field>
<field name="res_model">ctrm.reporting.finance.gl</field>
<field name="context_model">ctrm.reporting.finance.gl.context</field>
</record>
<record model="ir.action.act_window.view" id="act_ctrm_gl_reconciliation_view">
<field name="sequence" eval="10"/>
<field name="view" ref="ctrm_gl_reconciliation_view_list"/>
<field name="act_window" ref="act_ctrm_gl_reconciliation"/>
</record>
<menuitem
name="Positions"
@@ -372,6 +452,7 @@
name="Accruals"
parent="menu_ctrm_finance"
sequence="10"
action="act_ctrm_accruals"
id="menu_ctrm_finance_accruals"/>
<menuitem
name="Hedge Accounting"
@@ -382,16 +463,19 @@
name="Settlements and Invoicing"
parent="menu_ctrm_finance"
sequence="30"
action="act_ctrm_settlements"
id="menu_ctrm_finance_settlements"/>
<menuitem
name="Cash Flow Forecast"
parent="menu_ctrm_finance"
sequence="40"
action="act_ctrm_cashflow"
id="menu_ctrm_finance_cash_flow"/>
<menuitem
name="GL Reconciliation"
parent="menu_ctrm_finance"
sequence="50"
action="act_ctrm_gl_reconciliation"
id="menu_ctrm_finance_gl_reconciliation"/>
</data>
</tryton>

View File

@@ -0,0 +1,18 @@
<form>
<label name="from_date"/>
<field name="from_date"/>
<label name="to_date"/>
<field name="to_date"/>
<label name="party"/>
<field name="party"/>
<label name="currency"/>
<field name="currency"/>
<label name="product"/>
<field name="product"/>
<label name="purchase"/>
<field name="purchase"/>
<label name="sale"/>
<field name="sale"/>
<label name="type"/>
<field name="type"/>
</form>

View File

@@ -0,0 +1,15 @@
<tree>
<field name="valuation_date"/>
<field name="accrual_type"/>
<field name="lot"/>
<field name="product" width="160"/>
<field name="party" width="160"/>
<field name="reference"/>
<field name="quantity" sum="1"/>
<field name="unit"/>
<field name="amount" sum="1"/>
<field name="currency"/>
<field name="state"/>
<field name="purchase"/>
<field name="sale"/>
</tree>

View File

@@ -0,0 +1,12 @@
<form>
<label name="from_date"/>
<field name="from_date"/>
<label name="to_date"/>
<field name="to_date"/>
<label name="party"/>
<field name="party"/>
<label name="currency"/>
<field name="currency"/>
<label name="overdue_only"/>
<field name="overdue_only"/>
</form>

View File

@@ -0,0 +1,17 @@
<tree>
<field name="due_date"/>
<field name="posting_date"/>
<field name="party" width="160"/>
<field name="account"/>
<field name="amount" sum="1"/>
<field name="currency"/>
<field name="debit" sum="1"/>
<field name="credit" sum="1"/>
<field name="second_currency"/>
<field name="amount_second_currency" sum="1"/>
<field name="journal"/>
<field name="move"/>
<field name="move_line"/>
<field name="reference"/>
<field name="description"/>
</tree>

View File

@@ -0,0 +1,16 @@
<form>
<label name="from_date"/>
<field name="from_date"/>
<label name="to_date"/>
<field name="to_date"/>
<label name="party"/>
<field name="party"/>
<label name="currency"/>
<field name="currency"/>
<label name="account"/>
<field name="account"/>
<label name="lot"/>
<field name="lot"/>
<label name="unreconciled_only"/>
<field name="unreconciled_only"/>
</form>

View File

@@ -0,0 +1,21 @@
<tree>
<field name="posting_date"/>
<field name="journal"/>
<field name="account"/>
<field name="party" width="160"/>
<field name="lot"/>
<field name="fee"/>
<field name="invoice"/>
<field name="debit" sum="1"/>
<field name="credit" sum="1"/>
<field name="balance" sum="1"/>
<field name="currency"/>
<field name="second_currency"/>
<field name="amount_second_currency" sum="1"/>
<field name="reconciliation"/>
<field name="move_state"/>
<field name="move"/>
<field name="move_line"/>
<field name="reference"/>
<field name="description"/>
</tree>

View File

@@ -0,0 +1,16 @@
<form>
<label name="from_date"/>
<field name="from_date"/>
<label name="to_date"/>
<field name="to_date"/>
<label name="party"/>
<field name="party"/>
<label name="currency"/>
<field name="currency"/>
<label name="invoice_type"/>
<field name="invoice_type"/>
<label name="state"/>
<field name="state"/>
<label name="payment_status"/>
<field name="payment_status"/>
</form>

View File

@@ -0,0 +1,16 @@
<tree>
<field name="invoice"/>
<field name="invoice_type"/>
<field name="number"/>
<field name="reference"/>
<field name="invoice_date"/>
<field name="party" width="160"/>
<field name="state"/>
<field name="invoice_amount" sum="1"/>
<field name="paid_amount" sum="1"/>
<field name="open_amount" sum="1"/>
<field name="currency"/>
<field name="payment_status"/>
<field name="move"/>
<field name="reconciliation"/>
</tree>