InvoiceCore 0.3.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package InvoiceCore --version 0.3.0
                    
NuGet\Install-Package InvoiceCore -Version 0.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="InvoiceCore" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="InvoiceCore" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="InvoiceCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add InvoiceCore --version 0.3.0
                    
#r "nuget: InvoiceCore, 0.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package InvoiceCore@0.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=InvoiceCore&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=InvoiceCore&version=0.3.0
                    
Install as a Cake Tool

InvoiceCore

Zero-dependency invoicing primitives for .NET. Predictable, documented rounding, multi-rate tax (inclusive and exclusive), status rules, JSON/CSV export. Bring your own storage and rendering.

NuGet CI License: MIT


Quick start

using System;                      // Console, DateOnly
using System.Collections.Generic;  // List<T>
using InvoiceCore;                 // InvoiceService, CreateInvoiceRequest, CustomerInfo, LineItem, TaxRate

var svc = new InvoiceService();

var invoice = svc.Create(new CreateInvoiceRequest
{
    InvoiceNumber = "INV-001",
    IssuedDate    = new DateOnly(2025, 1, 15),
    DueDate       = new DateOnly(2025, 2, 15),
    CurrencyCode  = "USD",
    Customer      = new CustomerInfo { Name = "Acme Corp" },
    LineItems     = new List<LineItem> { new LineItem { Description = "Consulting", Quantity = 2, UnitPrice = 50m } },
    TaxRates      = new List<TaxRate>  { new TaxRate  { Name = "VAT", Percentage = 20m } },
});

Console.WriteLine($"Subtotal : {invoice.Subtotal:C}");   // $100.00
Console.WriteLine($"VAT 20%  : {invoice.TaxAmount:C}");  // $20.00
Console.WriteLine($"Total    : {invoice.Total:C}");       // $120.00
Console.WriteLine(svc.ExportToJson(invoice));

JSON export

ExportToJson returns a camelCase JSON string. Two options control the output:

Option Default Effect
MoneyFormat MoneyFormat.String Monetary amounts as quoted decimal strings ("10.50")
IncludeNulls false Omit null optional fields

Default output (MoneyFormat.String, IncludeNulls=false):

{
  "invoiceNumber": "INV-001",
  "currencyCode": "USD",
  "subtotal": "100.00",
  "taxAmount": "20.00",
  "total": "120.00",
  "lineItems": [{ "unitPrice": "50.00", "total": "100.00", ... }],
  ...
}

Monetary strings are formatted to the currency's minor-unit precision using InvariantCulture: JPY produces "1000", KWD produces "100.010". Percentages, quantities, and exchangeRate stay as JSON numbers.

Legacy numeric output (restores pre-0.3.0 behaviour):

var json = svc.ExportToJson(invoice, new JsonExportOptions
{
    MoneyFormat  = MoneyFormat.Number,
    IncludeNulls = true,
});

Breaking change in 0.3.0: The defaults changed from MoneyFormat.Number + IncludeNulls=true to MoneyFormat.String + IncludeNulls=false. Consumers that parse monetary fields as numbers or compare exact JSON byte-for-byte must update their code.


Tax arithmetic: worked example

Both modes produce the same correctly-rounded totals. Subtotal is always tax-exclusive: this is the value most likely to surprise callers switching between modes.

Exclusive mode (prices are net)

Line:  Qty 2 × $50.00 = $100.00
                         ───────
Subtotal (net)           $100.00   ← tax-exclusive in both modes
VAT 20%  on $100.00    =  $20.00
                         ───────
Total                    $120.00

Inclusive mode (same gross price, tax extracted)

using InvoiceCore; // TaxMode

// TaxMode = TaxMode.Inclusive, one line Qty 1 × $120.00, VAT 20%
Line total (gross):      $120.00
Subtotal  = Round($120.00 / 1.20) =  $100.00   ← tax-exclusive net
VAT 20%   = Round($100.00 × 0.20) =  $ 20.00
Residual reconciliation keeps Total = $120.00 exactly
                                      ───────
Total                                 $120.00

Note: Subtotal is the tax-exclusive net in both modes. In Inclusive mode it is the back-calculated base, not the gross line-item figure.

Multiple rates are additive, never compounded:

Subtotal              $1 000.00
VAT 20%    $200.00
Levy  5%   $ 50.00
           ───────
TaxAmount             $  250.00
Total                 $1 250.00

Rounding policy

All money arithmetic uses MidpointRounding.AwayFromZero (half-up), routed exclusively through a single internal Money.Round call site. There are no ad-hoc Math.Round or decimal.Round calls anywhere in the library.

Minor-unit precision is resolved per ISO-4217:

Digits Codes (examples)
0 JPY, KRW, VND
3 KWD, BHD, OMR
2 USD, EUR, GBP and everything else

Unknown codes default to 2 digits and never throw.


What this is not

  • Not a payment processor. No partial payments, payment allocation, or credit notes. Negative quantities are rejected in v1.
  • Not a PDF renderer. Use InvoiceCore.Pdf (planned) for that.
  • Not an accounting ledger. No bank feeds, journal entries, or chart of accounts.
  • Not a tax jurisdiction lookup. You supply the rate; InvoiceCore applies it correctly.
  • Not an e-invoicing compliance layer. Peppol, ZATCA, and FatturaPA are out of scope.
  • Not a recurring-invoice engine. No schedules, templates, or auto-numbering.
  • Not a currency converter. An exchange rate can be stored on an invoice for reporting grouping; it is never applied.
  • Not a per-line tax engine. Tax rates apply at invoice level in v1. Line items with differing VAT rates are not supported yet.

How this was built

See docs/PROCESS.md. The test suite was adversarially verified by mutation testing, which found four defects a green 202-test suite could not see.


Prior art

Library What it does Why InvoiceCore is different
InvoiceSdk Fluent PDF invoicing (.NET 6) Depends on QuestPDF + ServiceStack.Text
InvoicerNETCore, Invoicer, InvoiceGenerator.Core PDF generators Not model libraries

InvoiceCore replaces the 400 lines of subtly-wrong tax arithmetic every SaaS team writes by hand, with no dependencies, no rendering, and documented, tested rounding behaviour.


Roadmap

Package Status
InvoiceCore v0.2.0 (released), v0.3.0 (in progress)
InvoiceCore.Pdf Planned
InvoiceCore.EfCore Planned
InvoiceCore.Blazor Planned

License

MIT © 2026 Aftab Bashir

Product Compatible and additional computed target framework versions.
.NET 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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.0 46 8/25/2026
0.3.0 92 8/19/2026
0.2.0 100 8/18/2026
0.1.1 87 8/18/2026
0.1.0 97 8/16/2026