Skip to content

Public ID format (CHAR 13)

IndochinaPOS is migrating to a single id type for all primary and foreign keys: 13-character Crockford Base32 strings (uppercase), for example 2E6YF7PP4GFYC.

PropertyValue
Length13
Alphabet0-9, A-H, J-N, P-Z (no I, L, O, U)
StorageMySQL CHAR(13)
GenerationClient or server via generatePublicId()

From @indochina/shared:

  • generatePublicId() — mint a new id (offline-safe before sync)
  • isPublicId(value) — validate format
  • normalizePublicId(value) — trim, uppercase, map ambiguous chars
  • zPublicId() — Zod schema for forms and route params
  • Route params and JSON id fields use the public id string (not numeric legacy ids).
  • During the compat window, the backend may resolve old numeric/uuid values through id_migration_map (see internal migration runbook).

Desktop and browser clients mint id before insert. The same value is sent on sync — no separate client_order_id / server_order_id remap after migration completes.

From apps/backend (uses .env DB). One command runs waves 1–6 in FK order into {table}_v2 — it does not rename live tables unless you pass --swap=true.

FK remap copies created_at / updated_at from {table}_backup so MySQL ON UPDATE CURRENT_TIMESTAMP does not stamp conversion time. If a previous run already drifted those columns:

Terminal window
npm run id-migrate -- --step=restore-audit-timestamps --dry-run=true
npm run id-migrate -- --step=restore-audit-timestamps
Terminal window
npm run id-migrate -- --step=run-sequential --dry-run=true
npm run id-migrate -- --step=run-sequential
npm run id-migrate -- --step=run-sequential --swap=true

--swap=true skips tables whose live primary key is already CHAR(13) (a leftover {table}_legacy from a prior cutover is not an error). Internal runbook: docs/plans/char13-id-migration-checklist.md (Phụ lục D).

category_id is remapped in the INSERT JOIN. The category_ids text column is a JSON blob ([{"id":"1","position":0}] or legacy ["1"]) and is rewritten after convert so those nested ids become CHAR(13). Otherwise product edit re-submits the old ints as category_id and validation fails.

If items is already CHAR(13) (sequential skip), run:

Terminal window
npm run id-migrate -- --step=remap-items-category-ids --dry-run=true
npm run id-migrate -- --step=remap-items-category-ids
  • API conventions — global prefix and Swagger
  • Internal: docs/plans/char13-id-migration-checklist.md in the repository