From db205d6228e6096dee2b7deb5445917b0b65cff6 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Tue, 31 Mar 2026 02:10:14 +0000 Subject: [PATCH] feat(pos): add barcode generator with PostgreSQL sequence --- pos/migrations/v1.0_initial.sql | 3 ++ pos/services/barcode_generator.py | 61 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 pos/services/barcode_generator.py diff --git a/pos/migrations/v1.0_initial.sql b/pos/migrations/v1.0_initial.sql index 7422461..14f2e59 100644 --- a/pos/migrations/v1.0_initial.sql +++ b/pos/migrations/v1.0_initial.sql @@ -351,3 +351,6 @@ CREATE INDEX idx_audit_log_employee ON audit_log(employee_id); CREATE INDEX idx_audit_log_created ON audit_log(created_at); CREATE UNIQUE INDEX idx_inventory_branch_part ON inventory(branch_id, part_number); CREATE INDEX idx_employee_sessions_token ON employee_sessions(token); + +-- Barcode sequence +CREATE SEQUENCE IF NOT EXISTS barcode_seq START 1; diff --git a/pos/services/barcode_generator.py b/pos/services/barcode_generator.py new file mode 100644 index 0000000..a392cdf --- /dev/null +++ b/pos/services/barcode_generator.py @@ -0,0 +1,61 @@ +# /home/Autopartes/pos/services/barcode_generator.py +"""Generate internal barcodes for parts that don't have manufacturer barcodes. +Format: NX-{tenant_short}-{sequential_number} + +Uses a PostgreSQL sequence (barcode_seq) per tenant schema to guarantee uniqueness +under concurrent requests. No MAX+1 race conditions. +""" + + +def _ensure_sequence(conn): + """Create the barcode sequence if it doesn't exist yet.""" + cur = conn.cursor() + cur.execute(""" + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_class WHERE relkind = 'S' AND relname = 'barcode_seq' + ) THEN + CREATE SEQUENCE barcode_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE; + END IF; + END $$; + """) + cur.close() + + +def generate_barcode(conn, tenant_db_name): + """Generate the next barcode for this tenant. + + Format: NX-{tenant_short}-{NNNNNNN} + Example: NX-REFLOPEZ-0000001 + + Uses a PostgreSQL sequence to guarantee unique sequential numbers even + under concurrent requests (no race conditions). + """ + short = tenant_db_name.replace('tenant_', '').replace('_', '').upper()[:10] + prefix = f"NX-{short}-" + + _ensure_sequence(conn) + + cur = conn.cursor() + cur.execute("SELECT nextval('barcode_seq')") + next_num = cur.fetchone()[0] + cur.close() + + return f"{prefix}{next_num:07d}" + + +def generate_barcodes_batch(conn, tenant_db_name, count): + """Generate multiple sequential barcodes atomically.""" + short = tenant_db_name.replace('tenant_', '').replace('_', '').upper()[:10] + prefix = f"NX-{short}-" + + _ensure_sequence(conn) + + cur = conn.cursor() + # setval + generate range atomically via a single call + cur.execute("SELECT nextval('barcode_seq') FROM generate_series(1, %s)", (count,)) + nums = [r[0] for r in cur.fetchall()] + cur.close() + + return [f"{prefix}{n:07d}" for n in nums]