OfficeIMO.Invoicing
3.4.5
Prefix Reserved
dotnet add package OfficeIMO.Invoicing --version 3.4.5
NuGet\Install-Package OfficeIMO.Invoicing -Version 3.4.5
<PackageReference Include="OfficeIMO.Invoicing" Version="3.4.5" />
<PackageVersion Include="OfficeIMO.Invoicing" Version="3.4.5" />
<PackageReference Include="OfficeIMO.Invoicing" />
paket add OfficeIMO.Invoicing --version 3.4.5
#r "nuget: OfficeIMO.Invoicing, 3.4.5"
#:package OfficeIMO.Invoicing@3.4.5
#addin nuget:?package=OfficeIMO.Invoicing&version=3.4.5
#tool nuget:?package=OfficeIMO.Invoicing&version=3.4.5
OfficeIMO.Invoicing
Create, read, edit and convert electronic invoices with one typed .NET model. The core has no external runtime dependencies and supports .NET Standard 2.0, .NET 8, .NET 10 and .NET Framework 4.7.2.
Install
dotnet add package OfficeIMO.Invoicing --version 3.4.4
This package does not depend on a PDF engine or an external validation runtime.
Build from source
From the repository root, pack the project into a local feed, then add that package to your application:
dotnet pack OfficeIMO.Invoicing/OfficeIMO.Invoicing.csproj -c Release -o artifacts/invoice-feed
dotnet add path/to/Application.csproj package OfficeIMO.Invoicing --source artifacts/invoice-feed
Create invoice XML
InvoiceXmlOptions.GetSupportedTargets() returns the valid combinations of
specification release, syntax and profile from the same contract used by the
options constructor. Use this catalog for format selection; projection rejects
data loss by default.
using OfficeIMO.Invoicing;
var invoice = new Invoice {
Number = "INV-001",
IssueDate = new DateTime(2026, 9, 10),
DueDate = new DateTime(2026, 10, 10),
Currency = "EUR",
Seller = new InvoiceParty {
Name = "Example Seller",
Address = new InvoiceAddress { CountryCode = "DE" }
},
Buyer = new InvoiceParty {
Name = "Example Buyer",
Address = new InvoiceAddress { CountryCode = "DE" }
}
};
invoice.Seller.TaxRegistrations.Add(
new InvoiceTaxRegistration("DE123456789", InvoiceTaxRegistration.VatScheme));
invoice.Payments.Add(new InvoicePayment {
MeansCode = "58",
Reference = invoice.Number,
Account = new InvoiceBankAccount { Identifier = "DE79000000001234567890" }
});
invoice.Lines.Add(new InvoiceLine {
Id = "1", Name = "Consulting", Quantity = 1m, UnitPrice = 100m,
Tax = new InvoiceTaxCategory { Code = "S", Rate = 19m }
});
byte[] cii = InvoiceSerializer.Write(invoice, new InvoiceXmlOptions(
InvoiceSpecificationRelease.FacturX_1_09_2_Zugferd_2_5_2,
InvoiceSyntax.Cii,
InvoiceProfile.En16931));
byte[] ubl = InvoiceSerializer.Write(invoice,
new InvoiceXmlOptions(
InvoiceSpecificationRelease.En16931_1_3_16,
InvoiceSyntax.Ubl,
InvoiceProfile.En16931));
Serialization checks model arithmetic and the supported target mapping. Identical models and options produce identical UTF-8 bytes. Use OfficeIMO.Invoicing.Validation to check the resulting bytes against an explicit XSD and Schematron release. Successful serialization alone does not establish standards compliance.
Supported authoring contract
| Area | Supported data |
|---|---|
| Syntax and profiles | Factur-X 1.09.2 / ZUGFeRD 2.5.2 CII D22B with MINIMUM, BASIC WL, BASIC, EN 16931 and EXTENDED; EN 16931 1.3.16 and XRechnung 3.0.2 in CII D16B or UBL 2.1; Peppol BIS Billing 3.0.21 in UBL 2.1 |
| Documents | Invoice (380), credit note (381), partial invoice (326), corrected invoice (384), prepayment invoice (386) and self-billed invoice (389); document currency and accounting-currency VAT. UBL credit notes cannot carry a due date or project reference; writing or converting those fields reports unsupported target data. Peppol BIS Billing rejects self-billed invoices because they require a separate self-billing contract. |
| Parties | Seller, buyer, payee, tax representative, addresses, identifiers and contacts within each semantic role. Tax registrations retain their identifier and arbitrary source scheme. Target inspection reports the exact indexed registration when a target permits only VAT, one seller fiscal registration, or a canonical CII VA/FC scheme. |
| Lines | Quantities, price base quantities, net/gross prices, discounts, allowances, charges, item identifiers, classifications and attributes |
| VAT and totals | Category/rate breakdowns, exemptions, document adjustments, prepayments and payable rounding |
| Payments | Ordered payment-means occurrences with their own description, account, reference, direct-debit and masked-card data. Independent PaymentReference, CreditorIdentifier and DirectDebitMandateReference retain invoice-level data when no payment means is declared. UBL requires a payment instruction for a remittance reference or mandate; conversion reports those fields rather than inventing a means code. UBL preserves card network identifiers; CII reports them as unsupported. Conflicting invoice-level and occurrence values are reported by exact payment index. |
| References | Orders, preceding invoices, contracts, projects, delivery, periods, accounting and supporting documents. CII preserves a sales-order-only reference; UBL reports that it requires the associated purchase-order reference. External locations preserve well-formed absolute URIs, including FTP and URN schemes, without fetching them. |
IBAN classification checks the registered country prefix, national length and character structure, and MOD-97 checksum against the SWIFT IBAN Registry, release 102. Unknown prefixes and country-invalid identifiers remain generic UBL creditor accounts; they cannot be declared as CII IBANs. These checks do not establish that an account exists.
Lower Factur-X profiles intentionally carry less business data. The default
RejectDataLoss projection policy reports each populated source field that the
selected profile omits. AllowProfileDefinedDataLoss permits only that declared
profile reduction and returns the same findings as warnings from InspectTarget.
MINIMUM and BASIC WL carry calculated aggregates without XML line occurrences.
Use the constants in InvoiceDocumentTypes to select a document type. The qualified
type mapping covers the following pinned contracts; required business data still
depends on the selected type and profile:
| Contract | Syntax | Qualified types |
|---|---|---|
| EN 16931 1.3.16 | CII D16B, UBL 2.1 | 326, 380, 381, 384, 386, 389 |
| XRechnung 3.0.2, 2026-08-31 rules | CII D16B, UBL 2.1 | 326, 380, 381, 384, 386, 389 |
| Peppol BIS Billing 3.0.21 | UBL 2.1 | 326, 380, 381, 384, 386 |
| Factur-X 1.09.2 / ZUGFeRD 2.5.2, all five profiles | CII D22B | 326, 380, 381, 384, 386, 389 |
A corrected invoice can identify the original through PrecedingInvoices.
Selecting prepayment type 386 does not infer a paid amount; set PrepaidAmount
when the business record actually includes a payment. Selecting self-billed type
389 does not change seller/buyer roles or establish permission to self-bill.
XRechnung is the only national CIUS with an authoring contract because this release includes its CII/UBL syntax mappings, pinned rules and corpus evidence. EXTENDED-CTC-FR remains recognizable for inspection but cannot be selected for authoring or validation. Peppol BIS is a cross-border network usage specification, not a substitute for a national CIUS contract.
Create Polish FA(3)
Fa3InvoiceWriter uses the shared Invoice model with explicit national options.
FA(3) is a separate format contract rather than another EN profile.
var polish = new Invoice {
Number = "FA-2026-001", IssueDate = new DateTime(2026, 9, 30), Currency = "PLN",
Seller = new InvoiceParty {
Name = "Example Seller",
Address = new InvoiceAddress {
CountryCode = "PL", Line1 = "Example Street 1", Line2 = "00-001 Warszawa"
}
},
Buyer = new InvoiceParty {
Name = "Example Buyer",
Address = new InvoiceAddress {
CountryCode = "PL", Line1 = "Example Street 2", Line2 = "00-001 Warszawa"
}
}
};
// Fictional example identities; supply the issuer's actual identifiers.
polish.Seller.TaxRegistrations.Add(
new InvoiceTaxRegistration("9999999999", "NIP", InvoiceTaxRegistrationKind.Fiscal));
polish.Buyer.TaxRegistrations.Add(
new InvoiceTaxRegistration("1111111111", "NIP", InvoiceTaxRegistrationKind.Fiscal));
polish.Lines.Add(new InvoiceLine {
Id = "1", Name = "Consulting", Quantity = 1, UnitPrice = 100, UnitCode = "HUR",
Tax = new InvoiceTaxCategory { Code = "S", Rate = 23 }
});
var national = new Fa3InvoiceWriteOptions(
Fa3InvoiceKind.TaxInvoice,
new DateTimeOffset(2026, 9, 30, 12, 0, 0, TimeSpan.Zero),
new Fa3InvoiceAnnotations());
var findings = Fa3InvoiceWriter.Inspect(polish, national);
byte[] fa3Xml = Fa3InvoiceWriter.Write(polish, national);
Creation time and annotations are issuer declarations. The annotations cover cash accounting, self-billing, reverse charge, split payment, triangular transactions, an explicit exemption legal basis and margin procedures. This authoring contract declares ordinary buyer JST/GV roles and no new means of transport. It does not infer those choices from names or item descriptions.
| National kind | Authoring contract |
|---|---|
| VAT | Ordinary PLN amounts use the shared calculator. Foreign currency or margin procedures require explicit national amounts. |
| KOR | Common type 384, preceding invoice number and date, optional actual KSeF identifier, correction reason/timing and explicit signed national amounts. Rows express differences; previous-state row authoring is outside this contract. |
| ZAL | Explicit national advance amounts and full order rows whose gross calculation agrees with the order total. |
| KOR_ZAL | Explicit signed national amounts, preceding invoice reference, and Fa3Order.CorrectionDifferences(originalTotal, revisedTotal, differences, differenceTaxAmounts). Signed row amounts and explicit P_11VatZ declarations in row order reconcile the order totals. P_15ZK is optional. An unchanged order requires before/after rows and is rejected by this contract. |
| ROZ / KOR_ROZ | Settlement or settlement-correction kind, explicit national settlement amounts and previous advance references. Corrections also identify the preceding invoice and may provide P_15ZK. |
| UPR | Explicit national amounts; omitted simplified buyer name and invoice lines are supported. |
Use Fa3FiscalAmounts and Fa3TaxSummary for national P_15 and P_13/P_14
declarations. They are not relabeled EN payable totals. Foreign-currency taxable
buckets require explicit PLN tax amounts; an exchange rate is never guessed.
Ordinary PLN invoices derive their amounts from the shared calculator and reject
FiscalAmounts overrides. Dates, country and currency codes, NIP and contact
fields are checked against the pinned schema restrictions. A NIP pattern check
does not establish tax registration.
Negative line formulas that require rounding need an explicit
DeclaredNetAmount. EN negative-half rounding does not become a Polish fiscal
calculation rule. EN prepayments, payable rounding and aggregate declarations
must not be passed as substitutes for national payment or settlement records.
Invoice and order rows reuse InvoiceLine. Row identifiers are distinct positive
integers, prices apply to one unit, and unsupported adjustments or item metadata
block output. Known unit codes map to national labels; UnitLabels supplies
literal labels for other codes. LineTaxLabels distinguishes national np I
and np II when the common category is O. An override must agree with the
common category and rate. The supported payment forms are cash, card, cheque and
generic transfer; a SEPA-specific code is reported rather than reduced silently.
Inspect reports each unsupported populated field. Write blocks when a finding
is an error and bounds output to 16 MiB. The qualified XSD cases cover all seven
kinds, foreign-currency tax, exemption and margin declarations, periods and bank
payments. Validation establishes structure, not Polish tax treatment or KSeF
acceptance. Additional parties and JST/GV roles, new means of transport,
previous-state correction rows, partial-payment histories, settlement charges,
national item classifications and attachments require additional native mappings.
Read Polish FA(3)
Fa3InvoiceReader reads the Polish FA(3), schema 1-0E, variant 3 document.
It retains national document kinds, creation timestamps, P_15, every P_13/P_14
VAT bucket, PLN tax declarations and literal line units. The common Invoice
projection contains supported identities, addresses, contacts, dates, ordinary
priced lines and payment instructions. A NIP remains a fiscal registration; it
does not establish VAT registration. Address lines remain free text.
var fa3 = Fa3InvoiceReader.Read(File.ReadAllBytes("polish-invoice.xml"));
Console.WriteLine($"{fa3.Kind}: {fa3.Invoice.Number}, P_15={fa3.DeclaredTotal}");
foreach (var diagnostic in fa3.UnmappedData.Concat(fa3.CommonMappingDiagnostics))
Console.WriteLine($"{diagnostic.Location}: {diagnostic.Message}");
byte[] original = fa3.GetOriginalBytes();
The common projection does not recalculate national amounts or infer an amount due. Corrections retain signed differences and previous-state rows; advance and settlement rows are not interpreted as ordinary billed lines. Missing simplified invoice quantities and prices remain absent in the national line data. Other national annotations, order details, correction references, settlement charges and attachments are reported as unmapped rather than discarded.
GetOriginalBytes() returns unchanged source bytes even after common-model edits.
WriteCommonInvoice blocks conversion when national semantics or unmapped fields
would be lost. Reading is separate from FA(3) authoring, fiscal validation and
KSeF acceptance. Use the offline FA(3) schema validator
to qualify exact source bytes against the official XSD.
Read and edit safely
var parsed = InvoiceParser.Read(File.ReadAllBytes("invoice.xml"));
foreach (var item in parsed.UnmappedData)
Console.WriteLine($"{item.Location}: {item.Message}");
if (parsed.HasCompleteMapping) {
parsed.Invoice.Lines[0].Quantity = 2m;
InvoiceCalculator.UpdateDeclaredAmounts(parsed.Invoice);
File.WriteAllBytes("edited.xml", parsed.Write(new InvoiceXmlOptions(
InvoiceSpecificationRelease.FacturX_1_09_2_Zugferd_2_5_2,
InvoiceSyntax.Cii,
InvoiceProfile.En16931)));
}
Unknown business elements, attributes and unsupported semantic variations are
reported. Rewriting blocks by default when it would discard them. The explicit
allowUnmappedDataLoss option accepts the reported loss; it does not preserve
unknown extensions. GetOriginalBytes() always returns the original bytes,
even after model edits.
For selected header changes on a source with unmapped extensions, use the preservation editor:
var source = InvoiceSourceDocument.Load("invoice.xml");
var result = InvoiceSourceEditor.Apply(source, new InvoiceSourceEdits(
number: "INV-002",
issueDate: new DateTime(2026, 9, 30),
buyerReference: "BUYER-002"));
if (result.Succeeded)
File.WriteAllBytes("edited.xml", result.Document!.ToBytes());
foreach (var diagnostic in result.Diagnostics)
Console.WriteLine($"{diagnostic.Location}: {diagnostic.Message}");
InvoiceSourceEdits replaces an existing number, issue date, due date, buyer
reference or payment reference. Each field must have one unambiguous plaintext
location. All replacements are checked before any mutation; a failed plan returns
no edited document. Number changes retain payment references unless a replacement
is supplied separately. Signed XML, structured or annotated fields, unsupported
date representations and UBL credit-note due dates are rejected.
Unedited sources return their exact original bytes. Editing emits UTF-8 XML and
retains other elements, attributes, comments, processing instructions and text,
including carriage returns; prefix placement, CDATA spelling and declaration
formatting can change. Input and output are bounded to 16 MiB, depth 128,
100,000 XML reader nodes and 200,000 attributes. Text replacements are limited to
4,096 characters. Dates accept existing CII format 102 or plain UBL yyyy-MM-dd.
Completion establishes that the requested edits were applied, so validate the
resulting business data and exact XML against the intended standards release.
The shared workflows
compose these checks and safe file publication.
Duplicate invoice-currency or accounting-currency VAT totals are reported as unmapped data. UBL parsing retains the first invoice-currency total that includes a VAT breakdown, keeping that group's amount and breakdown together. For CII and accounting-currency amounts, parsing retains the first total. An explicit lossy rewrite uses those retained values; the resulting model must still pass validation.
Declared source line and VAT amounts are preserved. Model checks permit up to
0.02 difference from the unrounded line formula and a conservative 0.01 VAT
rounding difference for taxable categories; zero-tax categories require exactly
zero VAT. Totals must match the resulting amounts exactly. These
checks do not replace release-specific rules. UpdateDeclaredAmounts explicitly
recalculates lines, VAT and totals after financial edits.
For aggregate-only MINIMUM or BASIC WL data, recalculation retains the available tax bases and VAT amounts and rebuilds dependent totals, including prepayment edits. It does not create missing invoice lines. Target inspection still rejects adjustments that the selected profile cannot carry, such as a MINIMUM prepayment or a lower-profile rounding adjustment.
Accounting-currency VAT needs an explicit decision after financial edits. When
the invoice VAT total changes or has no retained baseline,
UpdateDeclaredAmounts clears TaxAmountInAccountingCurrency and retains
TaxCurrency; validation blocks writing until the amount is refreshed. An
unchanged retained VAT total preserves its imported accounting-currency amount.
Changing the invoice currency or an established tax currency also clears that
amount. Set both currencies before supplying a refreshed amount.
Supply the applicable exchange rate as accounting-currency units per
invoice-currency unit to calculate that amount explicitly:
invoice.TaxCurrency = "PLN";
var edit = InvoiceEditor.Recalculate(invoice, taxExchangeRate: 4.25m);
foreach (var diagnostic in edit.Diagnostics)
Console.WriteLine($"{diagnostic.Location}: {diagnostic.Message}");
InvoiceEditor.Recalculate(invoice) reports a required accounting-currency
refresh as INV-ACCOUNTING-VAT-REFRESH. Neither API chooses a tax-point date,
fetches exchange rates, nor infers a rate from historical amounts. Supply the
rate required by the transaction's accounting rules.
Model validation requires a seller business, legal or VAT identifier, and an account for credit-transfer payment codes 30 and 58. VAT checks cover category-specific registration requirements, exemption reasons on the resulting breakdown, and intra-community delivery details. Imported header-only exemption reasons remain valid through recalculation. Outside-scope VAT cannot mix with other categories or carry party VAT identifiers. Official code lists and release-specific rules remain the responsibility of the optional standards validator.
Target checks require a mandate reference for direct-debit codes 49 and 59 in XRechnung and Peppol. XRechnung, and Peppol transactions where both parties are in Germany, also require creditor and debtor identifiers in the direct-debit group; code 59 cannot include transfer accounts or card details in those profiles. Base EN 16931 does not impose those additional profile requirements.
Quantity, price, base-quantity and percentage calculations retain intermediate precision until monetary rounding. Monetary results use two decimal places with ties towards positive infinity; values that exceed decimal capacity are rejected instead of silently losing cents. Caller text must contain valid XML characters. The model permits up to 4 MiB of combined UTF-8 text and 8 MiB of embedded bytes. Parsing and authoring share a 50,000-item budget across collections, including declared or calculated VAT breakdowns. Parsing rejects inputs with more than 1,000 mapping diagnostics. Model validation, target inspection and conversion return at most 999 detailed diagnostics plus a summary that retains the highest omitted severity. Diagnostic codes are bounded to 256 characters; messages and locations to 4,096, with an explicit truncation marker when needed. Serialized XML is limited to 16 MiB while it is written.
Convert CII and UBL
var conversion = InvoiceConverter.Convert(File.ReadAllBytes("invoice.xml"),
new InvoiceXmlOptions(
InvoiceSpecificationRelease.XRechnung_3_0_2_2026_08_31,
InvoiceSyntax.Ubl,
InvoiceProfile.XRechnung));
if (conversion.Succeeded)
File.WriteAllBytes("converted.xml", conversion.Xml!);
else
foreach (var diagnostic in conversion.Diagnostics)
Console.WriteLine($"{diagnostic.Location}: {diagnostic.Message}");
Conversion produces no bytes if observed source data cannot be represented in the target. Examples include a tax-registration scheme the target would relabel, a sales-order-only reference when writing UBL, CII card-network metadata, and conflicting exemption reasons or singleton payment fields. Every diagnostic names the exact source-model path and unsupported target meaning. A changed guideline is reported and requires validation against the target release. This is a bounded semantic conversion, with explicit loss reporting for fields outside the supported contract.
Inspect profile declarations
using OfficeIMO.Invoicing;
var declaration = InvoiceProfileDeclaration.Read(File.ReadAllBytes("invoice.xml"));
Console.WriteLine(declaration.GuidelineId);
if (declaration.Profile is InvoiceProfile profile && profile != InvoiceProfile.PeppolBis)
Console.WriteLine(InvoiceProfiles.GetXmpConformanceLevel(profile));
The catalogue distinguishes MINIMUM, BASIC WL, BASIC, EN 16931, EXTENDED, XRechnung 3.0, EXTENDED-CTC-FR, and Peppol BIS Billing 3.0. Unknown identifiers are returned with a null profile; missing, ambiguous, malformed, or oversized input is rejected. Peppol BIS does not define Factur-X XMP metadata.
The French profile identifier follows AFNOR XP Z12-012. Recognition does not establish French business-rule coverage.
For visible hybrid PDFs, add the optional OfficeIMO.Invoicing.Pdf adapter. The XML engine remains independent of PDF generation.
Measure equivalent workloads
OfficeIMO.Invoicing.Benchmarks
measures XML read, XML write, pinned rules validation and multilingual PDF
generation separately over the same deterministic 25-line invoice. Correctness
checks run outside measurement, and the suite owns explicit Windows, Linux and
macOS elapsed/allocation ceilings.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.7.2
- No dependencies.
-
.NETStandard 2.0
- No dependencies.
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (3)
Showing the top 3 NuGet packages that depend on OfficeIMO.Invoicing:
| Package | Downloads |
|---|---|
|
OfficeIMO.Workflows
Typed local document workflows that compose first-party OfficeIMO conversion, PDF, and provenance capabilities. |
|
|
OfficeIMO.Invoicing.Validation
Pinned schema and Schematron validation for OfficeIMO electronic invoices. |
|
|
OfficeIMO.Invoicing.Pdf
Optional PDF presentation and hybrid CII invoice generation for OfficeIMO.Invoicing. |
GitHub repositories
This package is not used by any popular GitHub repositories.