Issue 1 — An order-level discount (coupon) does not reduce the taxable base (VAT computed on the pre-discount amount)
Steps to reproduce
1. One product at 29.00 ex-tax, VAT 20%, quantity 2 (subtotal 58.00 HT).
2. Apply a 50% cart coupon (tested with both percentage_cart and percentage_product).
Expected — VAT is computed on the discounted base: taxable base 29.00 → VAT 5.80 → total 34.80. (In France this is required: a commercial discount reduces the taxable base, art. 267 CGI.)
Actual — VAT stays computed on 58.00 (order_tax = 11.60), the coupon is applied after tax (order_discount = 29.00, order_discount_tax = 0), total 40.60.
Analysis — CartOrder::recalculateTaxAfterDiscounts() does compute the correct ratio and scales taxRates, but CartOrder::quantizeTotals() then rewrites order_tax = Σ orderitem_tax (the full per-line tax), so the reduction is discarded whenever items carry per-line tax. The coupon type does not matter — the discount always lands as an after-tax reduction.
What we did on our side (not proposed as the correct fix — it only covers our case) — we kept the per-line product tax full and made the discount carry its own VAT: store order_discount_tax = taxReduction in recalculateTaxAfterDiscounts(), subtract it from the total in quantizeTotals() (exclusive-tax branch), persist it in saveOrder(), and adjust get_formatted_order_totals() to display the discount tax-inclusive and the VAT line net. This is enough for our single-rate, no-shipping shop; we did not validate it for tax-inclusive pricing, multiple tax rates, mixed coupon/voucher, or shipping tax, so it isn't a general solution. We're mainly flagging the underlying behavior as a bug so a proper fix can be
designed.
---
Issue 2 — Terms text (termstext) is not passed through Text::_() on the checkout confirm step
Steps to reproduce
1. Set a language key as the terms text (System → J2Commerce config, termstext), e.g. a custom override key such as MY_AGREE_TO_KEY, with terms_display_type = checkbox.
2. Go to the checkout confirm step.
Expected — the key is translated (its language-override value is shown).
Actual — the raw key string is displayed.
Analysis — CheckoutController passes 'termsText' => (string) J2CommerceHelper::config()->get('termstext', '') (raw) to the confirm template (both emission points in renderStep('confirm', …)), and the core tmpl/checkout/{uikit,bootstrap5}/default_confirm.php echoes it without Text::_(). Wrapping the value in Text::_() at the controller (or in the template) resolves it for any template/framework.
---
Issue 3 — registerFrameworkTemplatePaths() reads the param framework while the config field is named subtemplate → uikit checkout overrides are ignored
Steps to reproduce
1. Set the frontend to subtemplate = uikit (config field name).
2. Add a template override under templates/<yourtemplate>/html/com_j2commerce/checkout/uikit/… (e.g. default_confirm.php) and load the checkout (steps are rendered via AJAX).
Expected — the uikit override is used.
Actual — the override is ignored; the core bootstrap5 template renders instead.
Analysis — the config field is subtemplate (default bootstrap5), but registerFrameworkTemplatePaths() (Checkout / Confirmation / Myprofile / Carts / Paymentupdate views) reads $this->params->get('framework', 'bootstrap5'). Since no framework param exists, it always falls back to bootstrap5, so all AJAX-rendered checkout views ignore the subtemplate = uikit setting and bypass uikit overrides. As a workaround we set an extra framework = uikit param, but the code should read subtemplate.
---
Happy to provide diffs, the exact line references, or a minimal reproduction if useful.
Regards,
Fred