FinOpenPOS
Fiscal Module

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
PositionDigitsFieldDescription
0-12cUFIBGE state code (e.g. "35" = SP)
2-54AAMMYear+month of emission
6-1914CNPJIssuer's CNPJ
20-212modModel (55 or 65)
22-243serieSeries number
25-339nNFInvoice number
341tpEmisEmission type (1=normal, 9=offline)
35-428cNFRandom numeric code
431cDVCheck 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

  1. Take the first 43 digits
  2. Apply weights 2, 3, 4, 5, 6, 7, 8, 9 cycling from right to left
  3. Sum the products
  4. remainder = sum % 11
  5. If remainder is 0 or 1, check digit = 0; otherwise check digit = 11 - remainder

Usage in the system

  • Generated by xml-builder.ts during XML construction
  • Stored in invoices.access_key column
  • 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.

On this page