Add multi client template management
This commit is contained in:
@@ -274,4 +274,7 @@ def register():
|
||||
sale.SaleCreatePurchase,
|
||||
sale.SaleAllocationsWizard,
|
||||
module='sale', type_='wizard')
|
||||
Pool.register(
|
||||
invoice.InvoiceReport,
|
||||
module='purchase_trade', type_='report')
|
||||
|
||||
|
||||
@@ -6,3 +6,6 @@ class Configuration(ModelSingleton, ModelSQL, ModelView):
|
||||
__name__ = 'purchase_trade.configuration'
|
||||
|
||||
pricing_rule = fields.Text("Pricing Rule")
|
||||
invoice_report_template = fields.Char("Invoice Template")
|
||||
invoice_cndn_report_template = fields.Char("CN/DN Template")
|
||||
invoice_prepayment_report_template = fields.Char("Prepayment Template")
|
||||
|
||||
@@ -98,6 +98,9 @@ Derniere mise a jour: `2026-04-02`
|
||||
- verifier si le probleme vient du cache avant de modifier le `.fodt`
|
||||
- pour un report alternatif, ne pas reutiliser le cache du report standard `account_invoice/invoice.fodt`
|
||||
- si besoin, bypasser la lecture/ecriture du cache pour les templates alternatifs
|
||||
- pour les clients multi-templates, preferer une configuration metier qui
|
||||
stocke le nom du template par action (`Invoice`, `CN/DN`, `Prepayment`)
|
||||
plutot qu'une modification manuelle de `ir_action_report.report`
|
||||
|
||||
### TR-007 - Pour une facture trade, privilegier le lot physique comme chemin de navigation
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ from decimal import Decimal, ROUND_HALF_UP
|
||||
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.modules.purchase_trade.numbers_to_words import amount_to_currency_words
|
||||
from trytond.exceptions import UserError
|
||||
from trytond.modules.account_invoice.invoice import (
|
||||
InvoiceReport as BaseInvoiceReport)
|
||||
|
||||
|
||||
class Invoice(metaclass=PoolMeta):
|
||||
@@ -665,3 +668,62 @@ class InvoiceLine(metaclass=PoolMeta):
|
||||
if net == '':
|
||||
return ''
|
||||
return round(Decimal(net) * Decimal('2204.62'),2)
|
||||
|
||||
|
||||
class InvoiceReport(BaseInvoiceReport):
|
||||
__name__ = 'account.invoice'
|
||||
|
||||
@classmethod
|
||||
def _get_purchase_trade_configuration(cls):
|
||||
Configuration = Pool().get('purchase_trade.configuration')
|
||||
configurations = Configuration.search([], limit=1)
|
||||
return configurations[0] if configurations else None
|
||||
|
||||
@classmethod
|
||||
def _get_action_name(cls, action):
|
||||
if isinstance(action, dict):
|
||||
return action.get('name') or ''
|
||||
return getattr(action, 'name', '') or ''
|
||||
|
||||
@classmethod
|
||||
def _resolve_configured_report_path(cls, action):
|
||||
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 = (template or '').strip()
|
||||
if not template:
|
||||
raise UserError('No template found')
|
||||
if '/' not in template:
|
||||
return f'account_invoice/{template}'
|
||||
return template
|
||||
|
||||
@classmethod
|
||||
def _get_resolved_action(cls, action):
|
||||
report_path = cls._resolve_configured_report_path(action)
|
||||
if isinstance(action, dict):
|
||||
resolved = dict(action)
|
||||
resolved['report'] = report_path
|
||||
return resolved
|
||||
setattr(action, 'report', report_path)
|
||||
return action
|
||||
|
||||
@classmethod
|
||||
def _execute(cls, records, header, data, action):
|
||||
resolved_action = cls._get_resolved_action(action)
|
||||
return super()._execute(records, header, data, resolved_action)
|
||||
|
||||
@@ -301,6 +301,64 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
with self.assertRaises(UserError):
|
||||
report.validate_remote_weight_report_context(shipment)
|
||||
|
||||
def test_invoice_report_uses_invoice_template_from_configuration(self):
|
||||
'invoice report path is resolved from purchase_trade configuration'
|
||||
report_class = Pool().get('account.invoice', type='report')
|
||||
config_model = Mock()
|
||||
config_model.search.return_value = [
|
||||
Mock(
|
||||
invoice_report_template='invoice_melya.fodt',
|
||||
invoice_cndn_report_template='invoice_ict_final.fodt',
|
||||
invoice_prepayment_report_template='prepayment.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': 'Invoice',
|
||||
'report': 'account_invoice/invoice.fodt',
|
||||
}),
|
||||
'account_invoice/invoice_melya.fodt')
|
||||
self.assertEqual(
|
||||
report_class._resolve_configured_report_path({
|
||||
'name': 'Prepayment',
|
||||
'report': 'account_invoice/prepayment.fodt',
|
||||
}),
|
||||
'account_invoice/prepayment.fodt')
|
||||
self.assertEqual(
|
||||
report_class._resolve_configured_report_path({
|
||||
'name': 'CN/DN',
|
||||
'report': 'account_invoice/invoice_ict_final.fodt',
|
||||
}),
|
||||
'account_invoice/invoice_ict_final.fodt')
|
||||
|
||||
def test_invoice_report_raises_when_template_is_missing(self):
|
||||
'invoice report must fail clearly when no template is configured'
|
||||
report_class = Pool().get('account.invoice', type='report')
|
||||
config_model = Mock()
|
||||
config_model.search.return_value = [
|
||||
Mock(
|
||||
invoice_report_template='',
|
||||
invoice_cndn_report_template='',
|
||||
invoice_prepayment_report_template='',
|
||||
)
|
||||
]
|
||||
|
||||
with patch(
|
||||
'trytond.modules.purchase_trade.invoice.Pool'
|
||||
) as PoolMock:
|
||||
PoolMock.return_value.get.return_value = config_model
|
||||
with self.assertRaises(UserError):
|
||||
report_class._resolve_configured_report_path({
|
||||
'name': 'Invoice',
|
||||
'report': 'account_invoice/invoice.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')
|
||||
|
||||
@@ -2,4 +2,10 @@
|
||||
<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>
|
||||
|
||||
@@ -1791,7 +1791,7 @@
|
||||
<text:p text:style-name="P9"/>
|
||||
<text:p text:style-name="P9"/>
|
||||
<text:p text:style-name="P9"/>
|
||||
<text:p text:style-name="P9"><text:span text:style-name="T51">SHIPMENT SCHEDULE</text:span>:<text:tab/><text:span text:style-name="T45"><text:placeholder text:placeholder-type="text"><sale.lines[0].del_period.month_name if sale.lines and sale.lines[0].del_period else ''></text:placeholder></text:span></text:p>
|
||||
<text:p text:style-name="P9"><text:span text:style-name="T51">SHIPMENT SCHEDULE</text:span>:<text:tab/><text:span text:style-name="T45"><text:placeholder text:placeholder-type="text"><sale.report_delivery_period_description or ''></text:placeholder></text:span></text:p>
|
||||
<text:p text:style-name="P9"/>
|
||||
<text:p text:style-name="P6">TOLERANCE:<text:tab/><text:tab/>+/- <text:placeholder text:placeholder-type="text"><sale.tol_min></text:placeholder><text:s/>%</text:p>
|
||||
<text:p text:style-name="P2"/>
|
||||
|
||||
Reference in New Issue
Block a user