This page documents the gobl.ubl codebase structure, development workflow, testing infrastructure, and contribution process for developers working on the project. For CLI tool documentation, see CLI Tool. For dependency information, see Dependencies and Build System.
The repository follows Go module structure with separation between public API, CLI, internal utilities, and tests.
Repository Layout: Code Entity Map
Directory Structure
| Directory/File | Key Entities | Description |
|---|---|---|
*.go | Convert(), Parse(), ConvertInvoice() | Public conversion API |
invoice.go | type Invoice struct, func (*Invoice) Convert() | UBL Invoice document type |
credit_note.go | type CreditNote struct | UBL CreditNote document type |
context.go | type Context, ContextEN16931, ContextPeppol, FindContext() | Profile configuration system |
party.go | newParty(), goblParty(), parseAddress() | Party/address conversion |
lines.go | addLines(), goblAddLines(), goblConvertLine() | Line item conversion |
payment.go | addPayment(), goblAddPayment() | Payment instruction conversion |
tax.go | addTotals(), buildTaxCategoryMap() | Tax and total calculation |
charges.go | addCharges(), goblAddCharges() | Document-level charge/discount conversion |
attachments.go | addAttachments(), ExtractBinaryAttachments() | Attachment handling |
delivery.go | addDelivery(), goblAddDelivery() | Delivery information conversion |
ordering.go | addOrdering(), goblAddOrdering() | Order reference conversion |
cmd/gobl.ubl/ | main(), rootCmd, convertCmd | CLI implementation using cobra |
internal/utog/ | UBL parsing helpers | Internal parsing utilities |
test/data/convert/ | *.json, out/*.xml | GOBL input + expected UBL output |
test/data/parse/ | *.xml, out/*.json | UBL input + expected GOBL output |
Sources: go.mod1-3
Required:
Optional dependencies for validation:
github.com/invopop/phive v0.6.0 (go.mod14) - Profile-specific UBL validationgithub.com/lestrrat-go/libxml2 v0.0.0-20240905100032-c934e3fcb9d3 (go.mod17) - XSD schema validationThe CLI reads environment variables from .env file using github.com/joho/godotenv v1.5.1 (go.mod7):
This configures the Phive validation service endpoint for test validation.
Sources: go.mod3 go.mod7 go.mod14 go.mod17
The codebase separates concerns into: entry point functions, context system, component converters (GOBL→UBL), and component parsers (UBL→GOBL).
Function Call Hierarchy
Architectural Patterns
| Pattern | Implementation | Files |
|---|---|---|
| Context-Driven Generation | type Context struct with CustomizationID, ProfileID, RequiredAddons, VESIDMapping fields. Predefined contexts: ContextEN16931, ContextPeppol, ContextPeppolSelfBilled, ContextXRechnung, ContextPeppolFranceCIUS, ContextPeppolFranceExtended | context.go |
| Component Separation | Each invoice component (party, lines, payment, tax, charges, delivery, ordering, attachments) has dedicated conversion functions in separate files | party.go, lines.go, payment.go, tax.go, charges.go, delivery.go, ordering.go, attachments.go |
| Bidirectional Naming | GOBL→UBL: newParty(), addLines(). UBL→GOBL: goblParty(), goblAddLines(). Different names reflect asymmetric processing | All component files |
| Extension Preservation | UBL codes stored in GOBL tax.Extensions: ExtKeyTaxCategory (UNTDID 5305), ExtKeyVATEX (UNTDID 7161), ExtKeyPaymentMeans (UNTDID 4461) | tax.go, payment.go |
| Document Interface | type Document interface implemented by Invoice and CreditNote with Convert() and Bytes() methods | document.go, invoice.go, credit_note.go |
| Tax Category Map | buildTaxCategoryMap() creates map[string]*TaxCategory from invoice-level tax totals, used by line and charge parsers to lookup exemption codes | tax.go |
Component Files and Functions
| File | GOBL→UBL Functions | UBL→GOBL Functions |
|---|---|---|
party.go | newParty(), newDeliveryParty(), newPayeeParty() | goblParty(), parseAddress() |
lines.go | addLines(), makeLineCharges() | goblAddLines(), goblConvertLine() |
payment.go | addPayment(), goblInvoiceInstructions() | goblAddPayment(), goblCreditTransfer(), goblDirectDebit(), goblPaymentCard() |
tax.go | addTotals(), buildTaxCategoryMap() | Tax extraction in goblAddCharges() |
charges.go | addCharges() | goblAddCharges() |
attachments.go | addAttachments() | goblAddAttachments(), ExtractBinaryAttachments() |
delivery.go | addDelivery() | goblAddDelivery() |
ordering.go | addOrdering() | goblAddOrdering() |
Sources: context.go, party.go, lines.go, payment.go, tax.go, charges.go, attachments.go, delivery.go, ordering.go, document.go, invoice.go, credit_note.go
Development Commands
| Task | Command | Description |
|---|---|---|
| Run all tests | go test ./... | Execute complete test suite |
| Enable validation | go test ./... -validate | Run Phive gRPC + XSD validation |
| Update golden files | go test ./... -update | Regenerate expected outputs in test/data/*/out/ |
| Run specific test | go test -run TestConvertToInvoice/en16931 | Execute single test scenario |
| Run context-specific | go test -run TestConvertToInvoice/peppol | Test single context profile |
| Build CLI | go build -o gobl.ubl ./cmd/gobl.ubl | Build CLI binary |
| Test CLI | ./gobl.ubl convert input.json output.xml | Validate CLI functionality |
Modification Workflow
| Change Type | Steps |
|---|---|
| Add context | 1. Define Context in context.go with CustomizationID, ProfileID, RequiredAddons, VESIDMapping2. Add context to FindContext() lookup3. Create test data directory: test/data/convert/{context}/4. Add test files and run with -update |
| Modify conversion | 1. Edit component file: party.go, lines.go, payment.go, etc.2. Update corresponding addX() or newX() function3. Run tests: go test ./... -update4. Verify golden files: test/data/convert/*/out/*.xml |
| Modify parsing | 1. Edit component file with goblX() function2. Run tests: go test ./... -update3. Verify golden files: test/data/parse/*/out/*.json |
| Add test case | 1. Add input file: test/data/convert/{context}/example.json or test/data/parse/{context}/example.xml2. Run with -update: go test ./... -update3. Review generated output in out/ subdirectory |
Sources: context.go, party.go, lines.go, payment.go
Tests are organized by operation (convert vs parse) and context (EN16931, Peppol, XRechnung, etc.) with golden file validation.
Test Data Structure
Test Functions and Validation Pipeline
| Test Function | File | Input | Output | Validation |
|---|---|---|---|---|
TestConvertToInvoice(t *testing.T) | convert_test.go | test/data/convert/{context}/*.json | test/data/convert/{context}/out/*.xml | Golden file comparison + optional Phive/XSD |
TestParseInvoice(t *testing.T) | parse_test.go | test/data/parse/{context}/*.xml | test/data/parse/{context}/out/*.json | Golden file comparison |
| Component tests | party_test.go, lines_test.go, etc. | Inline test data | Assertions | Direct function testing |
Conversion Test Flow: GOBL → UBL
Parsing Test Flow: UBL → GOBL
Test Flags
| Flag | Effect |
|---|---|
-validate | Enable Phive gRPC (github.com/invopop/phive v0.6.0) and XSD (github.com/lestrrat-go/libxml2) validation |
-update | Regenerate all golden files in test/data/*/out/ |
Assertion Libraries
github.com/stretchr/testify v1.10.0 (go.mod9) - assert.Equal(), assert.JSONEq()gitlab.com/flimzy/testy v0.14.0 (go.mod10) - Additional test helpersGolden File Update Process
Sources: go.mod9-10 convert_test.go, parse_test.go
test/data/ subdirectorytest/data/*/out/-validate flag before committing changes-update flag-validate flag (if Phive service available)The project uses semantic versioning and maintains compatibility with GOBL v0.305.0 (go.mod6). When updating GOBL dependency:
Sources: go.mod6 cmd/gobl.ubl/convert.go1-92