# P3 Focus balance snapshot foundation — 2026-10-20

Status: implemented and verified on ERP DEVELOPMENT. This is not full inventory, accounting, ERP completion or production release approval.

## Confirmed owner decisions
- Continue uploading Focus balances during the transition until ERP is fully adopted.
- A file is an as-of balance, not a stock receipt: current20 and report15 becomes15, never35.
- Only included product quantities are reconciled. Explicit zero is applied; missing rows remain unchanged.
- Preserve existing descriptions, prices and unrelated warehouses/companies. Product identities may be created from warehouse reports only when new and explicitly authorized.
- Local movements occurring after report time must be preserved. Older/equal conflicting snapshots need review, not automatic overwrite.
- QA-ERP synthetic files and records are authorized and must remain for owner review. Do not delete/reset them until requested.

## Implementation
Migration040000 adds inventory_snapshots, inventory_balances, inventory_movements, warehouse stock_revision and batch stock_as_of/preview revision. It is additive; applied successfully to MariaDB after private SQL/file backup. Rollback intentionally refuses automatic destruction of posted inventory history.
Composite foreign keys bind warehouse, company product, batch and snapshot references to their company. Application access is rechecked at upload/confirmation and warehouse locks serialize stock writers. Stock quantities use scaled integers at4 decimal places, never binary float for posting arithmetic.
Source as-of time is explicitly entered in UAE timezone, stored/interpreted as UTC. Source date must agree and future reports are rejected. A trusted report timestamp is necessary; do not invent a time for an ambiguous export.
For each included SKU, target = reported quantity + local non-snapshot movement deltas after report time. Adjustment = target minus current balance. A read-only movement records before/delta/after, effective time, actor and source snapshot. The source and its checksum are retained privately. No deletion/edit endpoints exist for posted movements. Database-level tamper-proof auditing is still a separate pending security task.
New inventory.import permission is required to post warehouse imports in addition to products.import and explicit accessible-warehouse checks. Creating new identities additionally needs products.create. Existing inventory.adjust is used by the internal audited adjustment service; there is no new adjustment UI. Existing roles are not silently granted new stock import rights.

## Replay, concurrency and review protection
- Confirmation locks the batch then warehouse, rechecks file checksum, permissions, selection and current stock state inside one transaction; any row failure rolls the entire posting back.
- Snapshot source is unique per warehouse/checksum. An identical batch or identical re-upload with the same timestamp/selected quantities has no additional stock effect. Relabelling an old source with a new timestamp or different row selections is rejected.
- Snapshots advance per-product watermarks; older/equal conflicting reports are blocked. Missing products do not get their timestamps advanced.
- Each rendered preview carries an encrypted proof bound to batch, actor, company, source checksum/time, warehouse revision and selected rows. It expires after1h. Revision and selection are captured while the batch/warehouse are locked. Refreshing another tab cannot authorize a stale tab. Changed selection or intervening stock movement requires a new preview.
- Sparse imports are not complete stock counts. A global blank/absence-to-zero rule is deliberately not supported. Negative, oversized, conflicting or unrecognized source values stay in review.

## Screens and worker
- /erp/import-batches: separate warehouse snapshot and SOH price profiles, UAE report time and owner-created warehouse choice.
- /erp/import-batches/{batch}: current/file/later-movement/target/difference preview; stale/invalid rows block approval. HTML409/422 errors return to review with an explanation; JSON retains the correct status. Mobile tables scroll horizontally with a hint.
- /inventory/balances and /inventory/movements: company/warehouse-filtered, read-only, paginated balances and history, linked from permission-aware navigation.
- SOH continues to update prices only; unit prices preserve up to4 decimals. Inventory snapshot import never overwrites existing names/prices. Source barcode and SKU disagreements require review; repeated barcode alone never merges products.
- Upload commits a durable job on an exact per-batch queue erp-product-imports-{batch}. A bounded ERP-local background process drains ONLY that queue, then exits; no general queue draining, cron installation, system service or other application change. Database retry_after360 > job timeout300. Per-batch lock expires900s. Resume analysis is owner/company/permission checked and throttled.
- Command: /opt/cpanel/ea-php82/root/usr/bin/php /home/snyt/public_html/SubDomain/ERP/artisan erp:imports-work <EXACT_BATCH_ID>. Never run a broad queue worker for these tests. An initial broad-worker attempt was rejected by safety review; read-only checks showed zero pending jobs and the design was narrowed to per-batch processing before use.
- On-demand launch works in the tested cPanel environment. This is not an always-on supervisor/monitoring service. Host/process failure recovery and high-volume concurrency/load testing remain release work; analysis failure cannot post business stock.

## Verification and retained QA
- Full isolated suite:83 tests /522 assertions PASS. Covers replacement20→15, explicit zero/missing products, two warehouses, duplicate barcode identities, later local movements, stale report atomic rollback, price/name preservation, replay even after later movements, exact fractional arithmetic, permissions, cross-company route and database isolation, backdated movement rejection, changed checksum, expired/invalid preview gating mechanics, multi-tab stale proof and changed selection, HTML error return and out-of-range review. No time-expiry boundary test was added yet.
- Real browser E2E on erp.snyt.xyz via existing CUA in-app session: private CSV upload → automatic exact-batch analysis → comparison → explicit confirmation → balances/history. Re-uploaded the exact update file and confirmed no duplicate movement. Stale and negative sources stayed unconfirmed with disabled controls. SOH4 prices retained129/64.998/79.002/6.996 without stock changes. Fresh signed-preview approval also passed against MariaDB.
- Desktop1280x720 and mobile390x844 inspected. Correct title/meaningful content, no blank/error overlay, relevant console warn/error list empty, native inputs/links/upload/confirm flows worked. Dedicated Browser plugin absent; existing CUA browser/Playwright APIs used. Viewport reset; original company restored; QA tab closed, user's original tab retained.
- QA company3, code QA-ERP-FOCUS-20261020, name QA-ERP Focus Test Company. Test warehouses1/2 labelled QA-ERP-A/B. Existing admin user1 is an owner REFERENCE ONLY, never a cleanup target.
- Eight retained import batches:1 opening,2 update,3 exact replay,4 warehouseB,5 SOH,6 stale blocked,7 negative blocked,8 signed-preview proof. Four snapshots/eight movement rows. WarehouseA: A15/B8/C0; warehouseB: A5. Four source prices on productA. No non-QA inventory balances and no pending ERP import jobs at verification checkpoint. No external assortment references to QA shared masters at checkpoint.
- Private fixture/cleanup manifest: storage/app/private/qa/QA-ERP-FOCUS-20261020/manifest.json. Contains exact company/warehouse/master/assortment/batch/snapshot/movement IDs and archived source paths. Commands erp:qa-focus-seed --confirm-development and erp:qa-focus-checkpoint are explicit, idempotent/non-deleting development aids. No purge command exists. Before future approved cleanup, back up and recheck external references; do not delete by a broad QA name prefix or delete the owner account.
- Local user-facing fixture copies under outputs/QA-ERP-FOCUS-20261020; primary source checksums match the private server copies. Screenshots: erp-focus-preview-desktop.png, erp-focus-preview-mobile.png, erp-focus-balances.png, erp-focus-soh-prices.png, erp-focus-stale-blocked.png, erp-focus-movement-history.png.

## Backup and scope
Private backups under /home/snyt/backups/snyt-erp/2026-10-20-focus-snapshots/ : files-before.tar.gz and database-before.sql. SQL SHA256 c0eb420e7f5da4b57730813a05e47d540e89672031e23729ed6da7ff7e6b6eb6. Credentials were temporary/private and not printed. No real Focus data posted, no Catalog/VAN code or data changed, no destructive migration, no Git push/commit. Synthetic QA data deliberately retained.

## Remaining before broader release
Actual company warehouse setup and representative real Focus XLS/XLSX source/header/time mapping acceptance; real large-upload/time/load and worker crash recovery tests; DB restore drill; explicit correction/reversal workflow for erroneous snapshots at the same timestamp; legacy/shared product reconciliation and media/PDF; reservations/transfers/counts and sales posting integration; accounting and remaining ERP phases. Do not label full P3 or final ERP acceptance complete.
