Sernuman is a portable Elixir library for scoped serial-number generation. It exists for the moment the serial one-liner stops fitting: the second document type, the per-tenant counter, the January reset, the server in another time zone. The library separates what a number looks like from where the counter lives — a template grammar decides the shape, a swappable store decides when the counter resets.
Why this exists
Serial-number code starts as a one-liner: current date, a prefix, a random suffix. It works until the second document type arrives. A request-for-quote number and a purchase-order number look similar. Then one side wants a different prefix, the other wants the counter to reset every January, and a warehouse in another time zone renders yesterday as today.
At that point the format string lives in two places, the counter in two tables, and the exceptions keep growing. I met this pattern in ERP-style work: contracts, equipment tags, warehouse bins, and machine identifiers all follow the same path from one-liner to small subsystem, and every copy of the one-liner carried different bugs.
Sernuman fixes the format once and the reset rule once, and gives every document type the same code path.
The shape
The shape of a number comes from a template: date tokens, variables, and a sequence counter, joined by literals.
- Date tokens are ISO 8601 pieces:
{YYYY},{MM},{DD}. - Variables come from a context map:
{id}. - Truncation and grouping are part of the grammar:
{id:9:3}trims a value to 9 characters and inserts a dash every 3 characters. - The sequence counter is a width-padded integer:
{seq:4:0}renders as0001.
The template SN-{id:9:3}-{YYYY}{MM}{DD}-{seq:4:0}, given an id from the context map and the date 29 October 2026, renders as SN-01k-3f2-axq-20261029-0001.
The counter
The counter lives behind a store. A scope is a pair: entity type and scope id. A machine in tenant 1 and a machine in tenant 2 keep different counters, and a document in either tenant keeps its own.
The reset boundary is configured: per day, per month, or per year. The date renders in a configured IANA time zone, so “today” is the business’s today, not the server’s. Two stores ship with the library: an ETS store that needs no database, and an Ecto store behind an optional dependency.
Two rules hold for any store. Issued serials are immutable: a correction mints a new number and voids the old one, and a counter never moves backwards. A change lock allows one template change per calendar year per entity type, so the format of the numbers issued in a given year stays stable.
What it is not
Sernuman does not manage documents, workflows, or tenants, and it does not migrate existing numbers. It is one grammar and a few stores, and that is the point.
Version 1.0.0, proprietary.