Skip to content

[1.8] Validate required fields per e-invoice standard before XML generation #21

Description

@nielsdrost7

Development target: ivpl-exprmt · branch v180

Context

Each e-invoice standard requires a different combination of client and invoice fields. Without upfront validation, a missing field currently causes XML generation to fail silently, produce malformed output, or throw a PHP error. The user has no way to know which field caused the problem.

A dedicated validation layer should check all required fields before generation begins and return a structured list of what is missing — one entry per missing field, in plain language.


Validation matrix

Field Peppol XRechnung ISDOC FatturaPA Facturae
client_name Required Required Required Required Required
client_address_1 Required Required Required Required Required
client_city Required Required Required Required Required
client_zip Required Required Required Required Required
client_country Required Required Required Required Required
client_vat_id Required Required Required Required (B2B/B2G) Required
client_company_number Recommended Required (B2C only, as fiscal code)
client_peppol_id Required
client_einvoice_sdi_code or client_einvoice_pec_email Required
invoice_routing_code_1 Required (blocks generation) Recommended (B2G) Recommended (B2G)
invoice_routing_code_2 Recommended (B2G) Recommended (B2G)
invoice_routing_code_3 Recommended (B2G)

Required = generation blocked, error returned.
Recommended = warning returned, generation still proceeds.

client_peppol_id is a column on ip_clients. The FatturaPA routing fields (client_einvoice_sdi_code, client_einvoice_pec_email) are stored in ip_client_meta — see #13 and #41.


Deliverable

A validation method — either in Mdl_einvoice or a dedicated EInvoice_validator class:

public function validate(string $standard, array $client, array $invoice, array $client_meta = [], array $invoice_meta = []): array

Returns [] on success. On failure, returns an associative array of field_key => human_readable_message:

[
    'client_peppol_id'        => 'Electronic invoice address is required for Peppol.',
    'invoice_routing_code_1'  => 'Routing code 1 (Leitweg-ID) is required for XRechnung.',
]

The XML generator calls this before producing any output. If the returned array contains any required failures, generation is aborted and the errors are displayed in the UI.


Acceptance criteria

  • validate() implemented and called by every XML generator before output
  • Each missing required field returns a separate, human-readable error message in plain English — no raw field names in the error text
  • Missing recommended fields return warnings that are shown in the UI but do not block generation
  • Validation passes and returns [] when all required fields are present
  • UI shows the full list of errors/warnings on the invoice page
  • No XML file is written when validation returns required errors
  • $client_meta and $invoice_meta passed in so FatturaPA SdI/PEC fields can be validated

AAA tests

Test 1 — missing Peppol endpoint blocks generation

Arrange: Peppol client with all required address fields set, client_vat_id = 'BE0123456789', but client_peppol_id = NULL
Act: Call validate('peppol', $client, $invoice)
Assert: Returns array containing entry for client_peppol_id; generation does not proceed


Test 2 — missing Leitweg-ID blocks XRechnung generation

Arrange: German government client; invoice with invoice_routing_code_1 = NULL
Act: Call validate('xrechnung', $client, $invoice)
Assert: Returns array containing entry for invoice_routing_code_1; generation does not proceed


Test 3 — all required fields present

Arrange: Peppol client with all required fields populated
Act: Call validate('peppol', $client, $invoice)
Assert: Returns []; XML generation proceeds normally


Test 4 — FatturaPA B2C: VAT not required for individuals

Arrange: Italian individual client with client_vat_id = NULL and client_company_number = 'RSSMRA80A01H501Z'
Act: Call validate('fatturaPA', $client, $invoice, $client_meta)
Assert: No error for missing VAT number; validation passes


Test 5 — FatturaPA B2G: missing routing codes return warnings, not errors

Arrange: Italian government client; invoice with all three routing codes NULL
Act: Call validate('fatturaPA', $client, $invoice, $client_meta, $invoice_meta)
Assert: Returns warnings for missing routing codes; generation is not blocked; warning shown in UI


Test 6 — multiple missing fields returns all errors at once

Arrange: Client with client_city = NULL, client_zip = NULL, client_peppol_id = NULL for Peppol
Act: Call validate('peppol', $client, $invoice)
Assert: Returns three separate entries — all three missing fields reported in a single call, not just the first one found

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