Skip to content

Item base quantity & line unit of measure (EN 16931 BT-149/150/130) - #881

Draft
alvarolivie wants to merge 1 commit into
mainfrom
items-price-base-qty
Draft

Item base quantity & line unit of measure (EN 16931 BT-149/150/130)#881
alvarolivie wants to merge 1 commit into
mainfrom
items-price-base-qty

Conversation

@alvarolivie

Copy link
Copy Markdown
Contributor

Adds the item price base quantity and a line-level unit of measure for EN 16931.

Part of splitting the original combined item-details PR. Sibling PRs: item attributes (#859), and item identities & buyer item reference.

Changes

  • org: base quantity (BT-149)Item.BaseQuantity, the number of units the item's price refers to (e.g. a price per 100 units). Line sums are calculated as price × quantity ÷ base_quantity. Validated as positive.
  • bill: invoiced quantity unit (BT-130)Line.Unit / SubLine.Unit, distinct from the item's price base quantity unit (Item.Unit, BT-150). Core EN 16931 permits the two to differ (e.g. priced per bushel, invoiced in tonnes); only the PEPPOL layer requires them equal.
  • Normalization is lazy by design — a line-only unit seeds Item.Unit (BT-150), but an item-only unit does not populate the line. Existing documents are byte-for-byte unchanged (no digest churn); divergent units are preserved when set explicitly.
  • Adds a GB example (examples/gb/invoice-base-quantity.yaml): 250 kg invoiced, priced per 100 kg → sum 300.00.

Converter follow-up (gobl.ubl / gobl.cii)

The @unitCode on the invoiced quantity (BT-130) is mandatory in both UBL and CII, so the converter mapping is:

  • BT-130InvoicedQuantity/@unitCode (UBL) / BilledQuantity/@unitCode (CII) = Line.Unit if set, else fall back to Item.Unit.
  • BT-150Price/BaseQuantity/@unitCode (UBL) / BasisQuantity/@unitCode (CII) = Item.Unit, emitted only when a base quantity is present.

A future addons/peppol should enforce PEPPOL-EN16931-R130 (BT-150 == BT-130); core EN 16931 has no such rule.

Pre-Review Checklist

  • Opened this PR as a draft
  • Read the CONTRIBUTING.md guide.
  • Performed a self-review of my code.
  • Added thorough tests with at least 90% code coverage.
  • Modified or created example GOBL documents to show my changes in use, if appropriate.
  • Added links to the source of the changes in tax regimes or addons, either structured or in the comments.
  • Run go generate . to ensure that the Schemas and Regime data are up to date.
  • Reviewed and fixed all linter warnings.
  • Been obsessive with pointer nil checks to avoid panics.
  • Updated the CHANGELOG.md with an overview of my changes.
  • Marked this PR as ready for review.

And if you are part of the org:

  • Requested a review from Copilot and fixed or dismissed (with a reason) all the feedback raised.
  • Requested a review from @samlown.

@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.76%. Comparing base (595b77a) to head (8b377c2).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #881   +/-   ##
=======================================
  Coverage   94.75%   94.76%           
=======================================
  Files         343      343           
  Lines       18815    18835   +20     
=======================================
+ Hits        17829    17849   +20     
  Misses        600      600           
  Partials      386      386           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…130)

- org.Item.BaseQuantity (BT-149): the number of units the price refers to;
  line sums are price × quantity ÷ base_quantity. Validated as positive.
- bill.Line.Unit / SubLine.Unit (BT-130): the invoiced quantity unit of
  measure, distinct from the item's price base quantity unit (Item.Unit,
  BT-150). Core EN 16931 permits them to differ; only PEPPOL requires equal.
- Normalization seeds Item.Unit (BT-150) from a line-only unit but never the
  reverse, so existing documents (and their digests) are unchanged. Converters
  read BT-130 from Line.Unit, falling back to Item.Unit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alvarolivie
alvarolivie force-pushed the items-price-base-qty branch from 874b926 to 8b377c2 Compare June 30, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant