Value Objects
Value objects encapsulate complex validation and formatting rules for core NF-e concepts like the 44-digit access key and CPF/CNPJ tax identifiers, providing immutable, equality-by-value abstractions.
Overview
Value objects encapsulate complex validation and formatting rules. They are immutable, equality-by-value, and have no identity. The fiscal module has two value objects representing core NF-e concepts.
Files: value-objects/access-key.ts, value-objects/tax-id.ts
AccessKey (access-key.ts)
The NF-e access key (chave de acesso) is a 44-digit numeric string that uniquely identifies every electronic invoice in Brazil.
Structure (44 digits)
35 2002 09944445000162 55 101 000123456 7 89012345 3
── ──── ────────────── ── ─── ───────── ─ ──────── ─
cUF AAMM CNPJ mod ser nNF tpEmis cNF DV| Position | Digits | Field | Description |
|---|---|---|---|
| 0-1 | 2 | cUF | IBGE state code (e.g. "35" = SP) |
| 2-5 | 4 | AAMM | Year+month of emission |
| 6-19 | 14 | CNPJ | Issuer's CNPJ |
| 20-21 | 2 | mod | Model (55 or 65) |
| 22-24 | 3 | serie | Series number |
| 25-33 | 9 | nNF | Invoice number |
| 34 | 1 | tpEmis | Emission type (1=normal, 9=offline) |
| 35-42 | 8 | cNF | Random numeric code |
| 43 | 1 | cDV | Check digit (mod-11) |
API
class AccessKey {
// Build from components (generates check digit)
static build(params: AccessKeyParams): AccessKey
// Validate existing key
constructor(key: string) // throws if not 44 numeric digits
// Mod-11 check digit calculation (weights 2-9 cycling)
static calculateMod11(digits: string): string
// Accessors
stateCode(): string // "35"
yearMonth(): string // "2002"
taxId(): string // "09944445000162"
model(): number // 55
series(): number // 101
number(): number // 123456
emissionType(): number // 7
numericCode(): string // "89012345"
checkDigit(): string // "3"
toString(): string // full 44-digit key
}Mod-11 algorithm
- Take the first 43 digits
- Apply weights 2, 3, 4, 5, 6, 7, 8, 9 cycling from right to left
- Sum the products
remainder = sum % 11- If remainder is 0 or 1, check digit = 0; otherwise check digit = 11 - remainder
Usage in the system
- Generated by
xml-builder.tsduring XML construction - Stored in
invoices.access_keycolumn - Used in SEFAZ requests (cancellation, consultation, events)
- Encoded in NFC-e QR codes
TaxId (tax-id.ts)
Discriminates between CPF (11 digits, individuals) and CNPJ (14 digits, companies) and handles XML tag generation.
API
class TaxId {
constructor(value: string)
isCpf(): boolean // length <= 11
isCnpj(): boolean // length > 11
tagName(): string // "CPF" or "CNPJ"
padded(): string // zero-pad to 11 (CPF) or 14 (CNPJ)
toXmlTag(): string // "<CPF>01234567890</CPF>" or "<CNPJ>09944445000162</CNPJ>"
toString(): string // raw value
}Why a value object?
Multiple places in the XML need CPF/CNPJ tags: <emit>, <dest>, <retirada>, <entrega>, <autXML>, event envelopes. Without TaxId, each place would need:
// Without value object (repeated everywhere):
const isCompany = taxId.length > 11;
const tagName = isCompany ? "CNPJ" : "CPF";
const padded = taxId.padStart(isCompany ? 14 : 11, "0");
const xmlTag = `<${tagName}>${padded}</${tagName}>`;
// With value object (one line):
const xmlTag = new TaxId(taxId).toXmlTag();Padding rules
- CPF: pad to 11 digits (
"1234567890"→"01234567890") - CNPJ: pad to 14 digits (
"9944445000162"→"09944445000162")
This is necessary because some systems store tax IDs without leading zeros.
Certificate & XML Signing
Brazilian electronic invoices require digital signing with an A1 e-CNPJ certificate in PFX format. This module handles PFX extraction via openssl, certificate info display, and XML digital signature per the NF-e MOC 4.00 specification.
Contingency Modes
When the primary SEFAZ web service is unavailable, NF-e/NFC-e can still be issued using contingency modes including SVC-AN, SVC-RS for NF-e and EPEC for NFC-e.