Add template management

This commit is contained in:
2026-04-06 15:17:17 +02:00
parent 1f62ae91dd
commit 888b880bd6
7 changed files with 166 additions and 29 deletions

View File

@@ -276,5 +276,7 @@ def register():
module='sale', type_='wizard')
Pool.register(
invoice.InvoiceReport,
invoice.SaleReport,
invoice.PurchaseReport,
module='purchase_trade', type_='report')

View File

@@ -6,6 +6,10 @@ class Configuration(ModelSingleton, ModelSQL, ModelView):
__name__ = 'purchase_trade.configuration'
pricing_rule = fields.Text("Pricing Rule")
sale_report_template = fields.Char("Sale Template")
sale_bill_report_template = fields.Char("Sale Bill Template")
sale_final_report_template = fields.Char("Sale Final Template")
invoice_report_template = fields.Char("Invoice Template")
invoice_cndn_report_template = fields.Char("CN/DN Template")
invoice_prepayment_report_template = fields.Char("Prepayment Template")
purchase_report_template = fields.Char("Purchase Template")

View File

@@ -5,6 +5,11 @@
<field name="type">form</field>
<field name="name">configuration_form</field>
</record>
<record model="ir.ui.view" id="purchase_trade_template_configuration_view_form">
<field name="model">purchase_trade.configuration</field>
<field name="type">form</field>
<field name="name">template_configuration_form</field>
</record>
<record model="ir.action.act_window" id="act_purchase_trade_configuration_form">
<field name="name">Pricing Configuration</field>
@@ -15,6 +20,15 @@
<field name="view" ref="purchase_trade_configuration_view_form"/>
<field name="act_window" ref="act_purchase_trade_configuration_form"/>
</record>
<record model="ir.action.act_window" id="act_purchase_trade_template_configuration_form">
<field name="name">Document Templates</field>
<field name="res_model">purchase_trade.configuration</field>
</record>
<record model="ir.action.act_window.view" id="act_purchase_trade_template_configuration_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="purchase_trade_template_configuration_view_form"/>
<field name="act_window" ref="act_purchase_trade_template_configuration_form"/>
</record>
<menuitem
name="Configuration"
@@ -23,5 +37,12 @@
sequence="10"
id="menu_purchase_trade_configuration"
icon="tryton-settings"/>
<menuitem
name="Document Templates"
parent="document_incoming.menu_configuration"
action="act_purchase_trade_template_configuration_form"
sequence="20"
id="menu_purchase_trade_template_configuration"
icon="tryton-settings"/>
</data>
</tryton>

View File

@@ -5,6 +5,9 @@ from trytond.modules.purchase_trade.numbers_to_words import amount_to_currency_w
from trytond.exceptions import UserError
from trytond.modules.account_invoice.invoice import (
InvoiceReport as BaseInvoiceReport)
from trytond.modules.sale.sale import SaleReport as BaseSaleReport
from trytond.modules.purchase.purchase import (
PurchaseReport as BasePurchaseReport)
class Invoice(metaclass=PoolMeta):
@@ -670,9 +673,7 @@ class InvoiceLine(metaclass=PoolMeta):
return round(Decimal(net) * Decimal('2204.62'),2)
class InvoiceReport(BaseInvoiceReport):
__name__ = 'account.invoice'
class ReportTemplateMixin:
@classmethod
def _get_purchase_trade_configuration(cls):
Configuration = Pool().get('purchase_trade.configuration')
@@ -686,31 +687,20 @@ class InvoiceReport(BaseInvoiceReport):
return getattr(action, 'name', '') or ''
@classmethod
def _resolve_configured_report_path(cls, action):
def _get_action_report_path(cls, action):
if isinstance(action, dict):
return action.get('report') or ''
return getattr(action, 'report', '') or ''
@classmethod
def _resolve_template_path(cls, action, field_name, default_prefix):
config = cls._get_purchase_trade_configuration()
report_path = cls._get_action_report_path(action) or ''
action_name = cls._get_action_name(action)
if (report_path.endswith('/prepayment.fodt')
or action_name == 'Prepayment'):
template = (
getattr(config, 'invoice_prepayment_report_template', '')
if config else '')
elif (report_path.endswith('/invoice_ict_final.fodt')
or action_name == 'CN/DN'):
template = (
getattr(config, 'invoice_cndn_report_template', '')
if config else '')
else:
template = (
getattr(config, 'invoice_report_template', '')
if config else '')
template = getattr(config, field_name, '') if config else ''
template = (template or '').strip()
if not template:
raise UserError('No template found')
if '/' not in template:
return f'account_invoice/{template}'
return f'{default_prefix}/{template}'
return template
@classmethod
@@ -727,3 +717,47 @@ class InvoiceReport(BaseInvoiceReport):
def _execute(cls, records, header, data, action):
resolved_action = cls._get_resolved_action(action)
return super()._execute(records, header, data, resolved_action)
class InvoiceReport(ReportTemplateMixin, BaseInvoiceReport):
__name__ = 'account.invoice'
@classmethod
def _resolve_configured_report_path(cls, action):
report_path = cls._get_action_report_path(action) or ''
action_name = cls._get_action_name(action)
if (report_path.endswith('/prepayment.fodt')
or action_name == 'Prepayment'):
field_name = 'invoice_prepayment_report_template'
elif (report_path.endswith('/invoice_ict_final.fodt')
or action_name == 'CN/DN'):
field_name = 'invoice_cndn_report_template'
else:
field_name = 'invoice_report_template'
return cls._resolve_template_path(action, field_name, 'account_invoice')
class SaleReport(ReportTemplateMixin, BaseSaleReport):
__name__ = 'sale.sale'
@classmethod
def _resolve_configured_report_path(cls, action):
report_path = cls._get_action_report_path(action)
action_name = cls._get_action_name(action)
if report_path.endswith('/bill.fodt') or action_name == 'Bill':
field_name = 'sale_bill_report_template'
elif report_path.endswith('/sale_final.fodt') or action_name == 'Sale (final)':
field_name = 'sale_final_report_template'
else:
field_name = 'sale_report_template'
return cls._resolve_template_path(action, field_name, 'sale')
class PurchaseReport(ReportTemplateMixin, BasePurchaseReport):
__name__ = 'purchase.purchase'
@classmethod
def _resolve_configured_report_path(cls, action):
return cls._resolve_template_path(
action, 'purchase_report_template', 'purchase')

View File

@@ -307,9 +307,13 @@ class PurchaseTradeTestCase(ModuleTestCase):
config_model = Mock()
config_model.search.return_value = [
Mock(
sale_report_template='sale_melya.fodt',
sale_bill_report_template='bill_melya.fodt',
sale_final_report_template='sale_final_melya.fodt',
invoice_report_template='invoice_melya.fodt',
invoice_cndn_report_template='invoice_ict_final.fodt',
invoice_prepayment_report_template='prepayment.fodt',
purchase_report_template='purchase_melya.fodt',
)
]
@@ -359,6 +363,62 @@ class PurchaseTradeTestCase(ModuleTestCase):
'report': 'account_invoice/invoice.fodt',
})
def test_sale_report_uses_templates_from_configuration(self):
'sale report paths are resolved from purchase_trade configuration'
report_class = Pool().get('sale.sale', type='report')
config_model = Mock()
config_model.search.return_value = [
Mock(
sale_report_template='sale_melya.fodt',
sale_bill_report_template='bill_melya.fodt',
sale_final_report_template='sale_final_melya.fodt',
)
]
with patch(
'trytond.modules.purchase_trade.invoice.Pool'
) as PoolMock:
PoolMock.return_value.get.return_value = config_model
self.assertEqual(
report_class._resolve_configured_report_path({
'name': 'Sale',
'report': 'sale/sale.fodt',
}),
'sale/sale_melya.fodt')
self.assertEqual(
report_class._resolve_configured_report_path({
'name': 'Bill',
'report': 'sale/bill.fodt',
}),
'sale/bill_melya.fodt')
self.assertEqual(
report_class._resolve_configured_report_path({
'name': 'Sale (final)',
'report': 'sale/sale_final.fodt',
}),
'sale/sale_final_melya.fodt')
def test_purchase_report_uses_template_from_configuration(self):
'purchase report path is resolved from purchase_trade configuration'
report_class = Pool().get('purchase.purchase', type='report')
config_model = Mock()
config_model.search.return_value = [
Mock(purchase_report_template='purchase_melya.fodt')
]
with patch(
'trytond.modules.purchase_trade.invoice.Pool'
) as PoolMock:
PoolMock.return_value.get.return_value = config_model
self.assertEqual(
report_class._resolve_configured_report_path({
'name': 'Purchase',
'report': 'purchase/purchase.fodt',
}),
'purchase/purchase_melya.fodt')
def test_sale_report_multi_line_helpers_aggregate_all_lines(self):
'sale report helpers aggregate quantity, price lines and shipment periods'
Sale = Pool().get('sale.sale')

View File

@@ -2,10 +2,4 @@
<form col="4">
<label name="pricing_rule"/>
<field name="pricing_rule" colspan="3"/>
<label name="invoice_report_template"/>
<field name="invoice_report_template" colspan="3"/>
<label name="invoice_cndn_report_template"/>
<field name="invoice_cndn_report_template" colspan="3"/>
<label name="invoice_prepayment_report_template"/>
<field name="invoice_prepayment_report_template" colspan="3"/>
</form>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0"?>
<form col="4">
<separator id="sale_templates" string="Sale" colspan="4"/>
<label name="sale_report_template"/>
<field name="sale_report_template" colspan="3"/>
<label name="sale_bill_report_template"/>
<field name="sale_bill_report_template" colspan="3"/>
<label name="sale_final_report_template"/>
<field name="sale_final_report_template" colspan="3"/>
<separator id="invoice_templates" string="Invoice" colspan="4"/>
<label name="invoice_report_template"/>
<field name="invoice_report_template" colspan="3"/>
<label name="invoice_cndn_report_template"/>
<field name="invoice_cndn_report_template" colspan="3"/>
<label name="invoice_prepayment_report_template"/>
<field name="invoice_prepayment_report_template" colspan="3"/>
<separator id="purchase_templates" string="Purchase" colspan="4"/>
<label name="purchase_report_template"/>
<field name="purchase_report_template" colspan="3"/>
</form>