feat(configurator): totals = one tax on (subtotal + charge)

This commit is contained in:
gsinghpal
2026-05-29 21:37:00 -04:00
parent a07a5f931a
commit f8929eb686
2 changed files with 37 additions and 45 deletions

View File

@@ -28,3 +28,19 @@ class TestChargeTaxLot(TransactionCase):
self.assertEqual(ct.default_amount, 75.0)
cid, cname = self.env['fp.additional.charge.type'].name_create('Setup')
self.assertTrue(cid)
# ----- Task 3: totals -----
def _make_wizard(self, **kw):
vals = {'partner_id': self.partner.id}
vals.update(kw)
return self.env['fp.direct.order.wizard'].create(vals)
def test_tax_applies_on_subtotal_plus_charge(self):
wiz = self._make_wizard(charge_amount=100.0, tax_id=self.tax13.id)
self.env['fp.direct.order.line'].create({
'wizard_id': wiz.id, 'quantity': 1, 'unit_price': 50.0,
})
wiz.invalidate_recordset()
self.assertEqual(wiz.total_subtotal, 50.0)
self.assertAlmostEqual(wiz.total_tax, 19.5, places=2)
self.assertAlmostEqual(wiz.total_amount, 169.5, places=2)

View File

@@ -401,65 +401,41 @@ class FpDirectOrderWizard(models.Model):
# ---- Computes ----
@api.depends(
'line_ids.line_subtotal',
'line_ids.quantity',
'line_ids.unit_price',
'line_ids.tax_ids',
'charge_amount',
'tooling_charge',
'tax_id',
'partner_id',
'currency_id',
)
def _compute_totals(self):
"""Roll up subtotal / tax / grand total across lines + tooling.
"""Roll up subtotal / tax / grand total across lines + charge.
Each line's taxes are computed via account.tax.compute_all so the
Express form's totals card mirrors what the eventual SO will show
once the operator hits Confirm. The tooling charge is added at the
wizard level here AND pushed as an actual sale.order.line at
action_create_order time (so it carries into the invoice).
The order-level ``tax_id`` is applied once to (subtotal + charge),
where charge = ``charge_amount`` (legacy ``tooling_charge`` as
fallback). The charge is also pushed as an actual sale.order.line
at action_create_order time (so it carries into the invoice).
"""
for rec in self:
subtotal = 0.0
subtotal = sum(
(l.quantity or 0) * (l.unit_price or 0.0)
for l in rec.line_ids
)
charge = rec.charge_amount or rec.tooling_charge or 0.0
tax_total = 0.0
for line in rec.line_ids:
line_pre_tax = (line.quantity or 0) * (line.unit_price or 0.0)
subtotal += line_pre_tax
if line.tax_ids and line_pre_tax:
taxes_res = line.tax_ids.compute_all(
line.unit_price or 0.0,
currency=rec.currency_id,
quantity=line.quantity or 0,
product=None,
partner=rec.partner_id or None,
)
tax_total += (
taxes_res['total_included']
- taxes_res['total_excluded']
)
# Tooling charge: pick the tax set from the FIRST line that
# has one (best proxy for the customer's standard rate). If
# no line has taxes set yet, tooling is shown untaxed in the
# preview; the eventual SO line will apply product defaults.
tooling = rec.tooling_charge or 0.0
if tooling:
first_taxed_line = next(
(l for l in rec.line_ids if l.tax_ids), False,
if rec.tax_id and (subtotal + charge):
res = rec.tax_id.compute_all(
subtotal + charge,
currency=rec.currency_id,
quantity=1,
product=None,
partner=rec.partner_id or None,
)
if first_taxed_line:
tooling_res = first_taxed_line.tax_ids.compute_all(
tooling,
currency=rec.currency_id,
quantity=1,
product=None,
partner=rec.partner_id or None,
)
tax_total += (
tooling_res['total_included']
- tooling_res['total_excluded']
)
tax_total = res['total_included'] - res['total_excluded']
rec.total_subtotal = subtotal
rec.total_tax = tax_total
rec.total_amount = subtotal + tax_total + tooling
rec.total_amount = subtotal + charge + tax_total
rec.total_qty = sum(rec.line_ids.mapped('quantity'))
rec.total_line_count = len(rec.line_ids)