efaturaefatura
Beta

Beta documentation. APIs may change before the stable release.

Storage

@akira-io/efatura uses a SequenceStore to allocate fiscal document numbers before building sequential IUDs. The sequence scope is:

  • transmitter NIF
  • fiscal year
  • LED code
  • document type

Use a durable store in production. The in-memory store is useful for tests only.

Contract

import type { SequenceStore } from '@akira-io/efatura';

const sequenceStore: SequenceStore = {
  async next(scope) {
    return 1;
  },
  async current(scope) {
    return null;
  },
  async reset(scope) {
    return;
  },
};

Pass the store to createEfatura:

import { createEfatura } from '@akira-io/efatura';

const efatura = createEfatura(config, { sequenceStore });
const iud = await efatura.buildSequentialIud({
  issueDate: '2026-02-08',
  documentType: 'FTE',
  randomCode: '1234567890',
});

buildSequentialIud() calls nextDocumentNumber(), then builds the IUD with the generated number.

Store Options

StoreImportUse case
InMemorySequenceStore@akira-io/efaturaUnit tests and disposable demos
FileSequenceStore@akira-io/efaturaLocal development or single-process tools
KnexSequenceStore@akira-io/efatura/knexProduction apps that already use Knex
PrismaSequenceStore@akira-io/efatura/prismaProduction apps that already use Prisma

The root entry does not import Knex or Prisma. Install only the adapter you use.

In-Memory Store

import { InMemorySequenceStore } from '@akira-io/efatura';

const sequenceStore = new InMemorySequenceStore();

This store resets when the process restarts. It serializes concurrent next() calls inside the process, but it is not durable and must not be used for production fiscal numbering.

File Store

import { FileSequenceStore } from '@akira-io/efatura';

const sequenceStore = new FileSequenceStore('storage/efatura-sequences.json');

The file store persists numbers to JSON and serializes concurrent calls in the current process. Use it for local development, command-line tools, and controlled single-process deployments. Do not share the same file across multiple app instances.

Knex Store

import knex from 'knex';
import { KnexSequenceStore } from '@akira-io/efatura/knex';

const database = knex({ client: 'pg', connection: process.env.DATABASE_URL });
const sequenceStore = new KnexSequenceStore(database);

await sequenceStore.ensureSchema();

ensureSchema() creates the efatura_sequences table when it does not exist. The store keeps sequence numbers unique and gap-free where the database can run the native upsert transaction used by the adapter.

Prisma Store

Copy the shipped model into your Prisma schema:

npx @akira-io/efatura prisma

The command writes to prisma/schema/efatura-sequence.prisma by default.

Useful options:

# Write to a custom path
npx @akira-io/efatura prisma --out prisma/efatura-sequence.prisma

# Print the fragment without writing files
npx @akira-io/efatura prisma --print

# Print only model blocks so you can append to a single schema.prisma file
npx @akira-io/efatura prisma --models-only --print

# Replace an existing output file
npx @akira-io/efatura prisma --force

The model also ships at resources/prisma/efatura-sequence.prisma.

model EfaturaSequence {
  id            String   @id
  currentNumber BigInt
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt
}

After copying the model, run your normal Prisma migration command:

npx prisma migrate dev

or, for development environments that use schema push:

npx prisma db push

Then wire the generated delegate:

import { PrismaClient } from '@prisma/client';
import { PrismaSequenceStore } from '@akira-io/efatura/prisma';

const prisma = new PrismaClient();
const sequenceStore = new PrismaSequenceStore(prisma.efaturaSequence);

Prisma 7 and newer require a driver adapter when constructing PrismaClient. Configure Prisma according to your application before passing the generated delegate to PrismaSequenceStore.

Migrating Counters

Changing adapters does not copy counters. Move the current value for every active sequence scope before switching stores, or the next generated document number may restart from 1.

The scope key must preserve:

  • transmitter NIF
  • fiscal year
  • LED code
  • document type

Production Notes

  • Keep one sequence source for each emitter and LED.
  • Use Knex or Prisma when multiple app instances can issue documents.
  • Back up sequence data with the rest of the billing database.
  • Restrict manual resets to controlled recovery operations.