Skip to content

Allow custom TransactionCalculator injection to properly support BR-FXEXT-CO-15 tolerance use cases #1041

@meparis

Description

@meparis

Context

In Factur-X EXTENDED, rule BR-FXEXT-CO-15 introduces a tolerance of ±0.01 EUR per invoice line between:

BT-112 (GrandTotalAmount)
and
BT-109 + BT-110

This tolerance exists to absorb rounding strategy differences (line-level vs document-level rounding).

However, in Mustang, TransactionCalculator is directly instantiated inside ZUGFeRD2PullProvider:

TransactionCalculator calc = new TransactionCalculator(trans);

This makes it impossible to provide an alternative calculation strategy without modifying core code.

Why this is a problem

BR-FXEXT-CO-15 explicitly acknowledges that small discrepancies may exist due to rounding strategies.

In real ERP systems:

Line amounts may be rounded early

VAT may be computed globally

Totals may be derived from upstream certified engines

Payment totals may legally differ by 0.01 or 0.02

Even when remaining within the tolerance defined by BR-FXEXT-CO-15.

Currently, Mustang:

Applies a fixed “round-early, aggregate-later” strategy

Does not allow substituting the calculation model

Prevents leveraging the tolerance provided by BR-FXEXT-CO-15
unless the core library is forked

This limits advanced but legitimate use cases in Factur-X EXTENDED.

Important Clarification

This proposal does not aim to:

Break EN 16931 compliance

Bypass BR-CO-15

Modify default behavior

It simply allows advanced users to:

Implement alternative compliant calculation strategies

Explicitly rely on BR-FXEXT-CO-15 tolerance where appropriate

Preserve ERP-certified totals when legally acceptable

without modifying Mustang core.

Proposed Solution (Minimal & Backward Compatible)

Introduce a protected factory method in ZUGFeRD2PullProvider:

protected TransactionCalculator createCalculator(Invoice trans) {
return new TransactionCalculator(trans);
}

Replace:

TransactionCalculator calc = new TransactionCalculator(trans);

with:

TransactionCalculator calc = createCalculator(trans);

Benefits

100% backward compatible

No behavioral change for existing users

Enables legitimate BR-FXEXT-CO-15 tolerance scenarios

Avoids forcing users to fork Mustang

Improves extensibility and enterprise adoption

Keeps normative compliance fully under user responsibility

Example Usage
public class CustomPullProvider extends ZUGFeRD2PullProvider {

@Override
protected TransactionCalculator createCalculator(Invoice trans) {
    return new CustomTransactionCalculator(trans);
}

}

Why This Matters

BR-FXEXT-CO-15 exists specifically to tolerate controlled rounding discrepancies.

Without an extension mechanism in Mustang, it is not possible to:

Align calculation strategy with external ERP engines

Fully leverage the tolerance defined in the Factur-X EXTENDED profile

Implement controlled, documented rounding policies

Adding this factory method would make Mustang more extensible,
while keeping default behavior fully unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions