feat(sku): canonical 14-digit timestamp SKUs so RFID/EPC tagging works (new items)
RecordGod generated 'RG'+random-hex SKUs, which the SCANGOD EPC codec rejects (it requires 14 digits: SKU × 10^9 + release_id packed onto the UHF chip). New inventory now gets a 14-digit product-added timestamp (YYYYMMDDHHMMSS) via _fresh_sku(), which bumps a second on collision to stay unique against the sku PRIMARY KEY. release_id stays its own column (the sku+space+release_id string is only the physical chip encoding, which the codec already builds). New items only — existing 'RG' SKUs are untouched (they keep working; they just aren't RFID-taggable until re-SKU'd, a separate migration if ever wanted). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
61601f1067
commit
00e08a86dd
@ -1,5 +1,6 @@
|
||||
import re
|
||||
import secrets
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
@ -547,9 +548,24 @@ class BulkIn(BaseModel):
|
||||
value: float | int | str | None = None
|
||||
|
||||
|
||||
async def _fresh_sku(db):
|
||||
"""Canonical SKU = 14-digit product-added timestamp (YYYYMMDDHHMMSS) — the format SCANGOD's EPC/RFID
|
||||
codec requires (packs SKU 14 digits × 10^9 + release_id onto the UHF chip). Bumps a second on
|
||||
collision so a same-second batch stays unique against the sku PRIMARY KEY.
|
||||
ponytail: check-then-insert; a truly concurrent same-second add on another connection could still hit
|
||||
the PK and be ON CONFLICT DO NOTHING'd. Fine for counter/admin stock entry; add retry-on-conflict if
|
||||
bulk-import concurrency grows."""
|
||||
base = datetime.now()
|
||||
for i in range(120):
|
||||
sku = (base + timedelta(seconds=i)).strftime("%Y%m%d%H%M%S")
|
||||
if not (await db.execute(text("SELECT 1 FROM inventory WHERE sku = :s"), {"s": sku})).first():
|
||||
return sku
|
||||
return (base + timedelta(seconds=120)).strftime("%Y%m%d%H%M%S")
|
||||
|
||||
|
||||
@router.post("/inventory/add")
|
||||
async def inv_add(body: InvItemIn, ident=Depends(require_token), db=Depends(get_db)):
|
||||
sku = (body.sku or "").strip() or ("RG" + secrets.token_hex(4).upper())
|
||||
sku = (body.sku or "").strip() or await _fresh_sku(db)
|
||||
title = body.title
|
||||
if body.release_id and not title: # pull the cached title if intaking by release
|
||||
r = (await db.execute(text("SELECT title FROM disc_cache WHERE release_id=:r"),
|
||||
@ -629,7 +645,7 @@ async def inv_import(body: ImportIn, ident=Depends(require_token), db=Depends(ge
|
||||
if not rid:
|
||||
not_found.append(ln)
|
||||
continue
|
||||
sku = "RG" + secrets.token_hex(4).upper()
|
||||
sku = await _fresh_sku(db)
|
||||
await db.execute(text("""
|
||||
INSERT INTO inventory (sku, store_id, kind, release_id, title, price, qty, in_stock, condition,
|
||||
crate_id, status, created_at, updated_at)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user