feat(pos): add barcode generator with PostgreSQL sequence
This commit is contained in:
@@ -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;
|
||||
|
||||
61
pos/services/barcode_generator.py
Normal file
61
pos/services/barcode_generator.py
Normal file
@@ -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]
|
||||
Reference in New Issue
Block a user