docs: correct app user role - store staff, not delivery drivers
The Flutter app is used by Prima Fresh Mart store staff (petugas toko) on duty at each store to receive/confirm deliveries and log products - not by the delivery drivers themselves. "Driver" only appears correctly now as a data field on the DO document (who drove the delivery), never as the app's operator. Fixes wording across README.md, CLAUDE.md, and screenshots/v2/WORKFLOW.md; also corrects CLAUDE.md's stale "up to 2 min" poll-timing note to match the real ~4.3min/130-retry value. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JSgYVUWJTGMk7SZqqHH8xy
This commit is contained in:
1 parent
e70ad37e6d
commit
be6cac1364
3 files changed
+13
-10
No files matched your search
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## What this repo is
|
||||
|
||||
An on-premise, GPU-accelerated OCR system that turns a driver's phone photo of a Delivery Order (DO) into structured, database-backed data. A **Flutter mobile app** (this root) is the field-facing client; a **Dockerized backend** (`backend/`) runs the Next.js API gateway, the PaddleOCR/vLLM pipeline, and Postgres.
|
||||
An on-premise, GPU-accelerated OCR system that turns a Prima Fresh Mart store staff member's phone photo of a Delivery Order (DO) into structured, database-backed data. The app's users are **store staff (petugas toko) on duty at each store**, not delivery drivers — each login account is bound to exactly one store (`accounts.role = 'store'`). A **Flutter mobile app** (this root) is the store-facing client; a **Dockerized backend** (`backend/`) runs the Next.js API gateway, the PaddleOCR/vLLM pipeline, and Postgres.
|
||||
|
||||
Read `README.md` first for the full architecture (mermaid diagrams, tech stack, accuracy numbers). Read `backend/CLAUDE.md` before touching anything under `backend/` — it documents backend-specific history, gotchas, and confidentiality notes not repeated here.
|
||||
|
||||
@@ -56,7 +56,7 @@ See `backend/CLAUDE.md` for the Next.js app commands (`npm run dev/build/lint`),
|
||||
## Architecture notes for the Flutter client
|
||||
|
||||
- **API base URL is resolved dynamically at startup**, not hardcoded to one value: `AppConfig.initializeApiBaseUrl()` in `lib/config/app_config.dart` tries a hardcoded LAN IP first, falling back to a fixed ngrok domain. If login/upload fails on a device, this is the first thing to check — re-run `start-dev-tunnel.ps1` if the host machine's LAN IP or ngrok tunnel has gone stale.
|
||||
- **Upload flow**: capture → Laplacian-variance blur check → GPS tag → multipart upload → the app polls `GET /documents` every 2s (up to 2 min) until the backend's OCR pass sets `parsed=true` → operator reviews/corrects on-device → `PUT /documents/:id` saves final data → PDF receipt generated/printed locally.
|
||||
- **Upload flow**: capture → Laplacian-variance blur check → GPS tag → multipart upload → the app polls `GET /documents` every 2s (up to ~4.3 min, 130 retries) until the backend's OCR pass sets `parsed=true` → operator reviews/corrects on-device → `PUT /documents/:id` saves final data → PDF receipt generated/printed locally.
|
||||
- **Pending-upload queue is persisted to a Hive box** (`lib/features/documents/pending_documents_provider.dart`, `lib/core/storage/local_storage.dart`) — an OS-level app kill mid-upload no longer loses the document; on next launch the queue reloads and resumes anything left non-terminal (fixed 2026-07-08, see `plans/next-enhancements.md` §3.1).
|
||||
- Auth (`lib/features/auth/`) performs real token verification against the backend `api/v1/auth/login` endpoint. Do not treat it as a demo stub anymore.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Prima Fresh Mart Scanner (app-pfm-ocr-v2)
|
||||
|
||||
An on-premise, GPU-accelerated OCR system that turns a driver's phone photo of a **Delivery Order (DO)** into structured, database-backed data — PO/SO/DO numbers, dates, store, and item lines — with a Flutter mobile client on one end and a Dockerized AI pipeline on the other.
|
||||
An on-premise, GPU-accelerated OCR system that turns a Prima Fresh Mart store staff member's phone photo of a **Delivery Order (DO)** into structured, database-backed data — PO/SO/DO numbers, dates, store, and item lines — with a Flutter mobile client on one end and a Dockerized AI pipeline on the other.
|
||||
|
||||
No cloud OCR API is used. Everything (layout detection, text recognition, LLM-assisted structuring) runs on your own GPU.
|
||||
|
||||
@@ -27,11 +27,11 @@ No cloud OCR API is used. Everything (layout detection, text recognition, LLM-as
|
||||
|
||||
## What This Is
|
||||
|
||||
A driver or warehouse operator photographs a DO paper on the Flutter app. The app checks the photo isn't blurry, tags it with GPS, and uploads it. The backend runs the image through a GPU OCR pipeline (deskew → layout detection → text recognition → LLM structuring), cross-checks every item line against a master SKU/store database, and saves the result. The app polls for the result, the operator reviews/corrects it on-device, and can print or export a signed delivery receipt as a PDF.
|
||||
A Prima Fresh Mart **store staff member (petugas toko)**, on duty at the store — not the delivery driver — photographs the DO paper on the Flutter app when a delivery arrives. The app checks the photo isn't blurry, tags it with GPS, and uploads it. The backend runs the image through a GPU OCR pipeline (deskew → layout detection → text recognition → LLM structuring), cross-checks every item line against a master SKU/store database, and saves the result. The app polls for the result, the store staff reviews/corrects it on-device and confirms receipt (entering their own name as receiver), and can print or export a signed delivery receipt as a PDF. Each account is bound to exactly one store (`role: store`), so whoever is on duty there uses the same login.
|
||||
|
||||
Two consumers of the same backend exist:
|
||||
- **Flutter mobile app** (`lib/`) — the primary, field-facing client.
|
||||
- **Next.js web pages** (`backend/pfm-web-app/src/app/*.tsx`) — internal tooling for manual labeling, accuracy comparison, and an OCR "arena" for engine comparison. Not part of the driver-facing product.
|
||||
- **Flutter mobile app** (`lib/`) — the primary, store-facing client used by staff at each Prima Fresh Mart location.
|
||||
- **Next.js web pages** (`backend/pfm-web-app/src/app/*.tsx`) — internal tooling for manual labeling, accuracy comparison, and an OCR "arena" for engine comparison. Not part of the store-facing product.
|
||||
|
||||
---
|
||||
|
||||
@@ -272,9 +272,9 @@ This drops the dev bind-mount and runs `npm start` against the image's own `npm
|
||||
|
||||
- **First build is large and slow.** The pipeline-api and vllm-server images are ~30GB each with GPU model weights. Budget real time and disk space for the first `docker compose up --build`.
|
||||
|
||||
- **Single GPU pipeline, no horizontal scaling.** There's one `pipeline-api` and one `vllm-server` container. Concurrent uploads queue behind the GPU; this is a real throughput ceiling worth load-testing before a multi-driver demo, not just a single-user smoke test.
|
||||
- **Single GPU pipeline, no horizontal scaling.** There's one `pipeline-api` and one `vllm-server` container. Concurrent uploads queue behind the GPU; this is a real throughput ceiling worth load-testing before a multi-store/multi-device demo, not just a single-user smoke test.
|
||||
|
||||
- **`/api/v1/*` enforces real auth; the classic dev routes deliberately don't.** `/api/v1/auth/login` checks a bcrypt-hashed password against a real `accounts` table and signs a JWT; `/api/v1/documents/*` (list, upload, PUT-by-id) reject any request with a missing/invalid token with a real `401`. The Flutter app always goes through this surface. The **classic routes** (`/api/upload`, `/api/parse`, `/api/history`, etc.) and the root/`scan-pfm`/`manual-label` web pages have no login flow and never will — they're dev-only internal tooling, not part of the driver-facing product. Every API route still sets `Access-Control-Allow-Origin: *`, so this is fine for a controlled LAN/demo deployment but not for exposing the stack to the open internet as-is.
|
||||
- **`/api/v1/*` enforces real auth; the classic dev routes deliberately don't.** `/api/v1/auth/login` checks a bcrypt-hashed password against a real `accounts` table and signs a JWT; `/api/v1/documents/*` (list, upload, PUT-by-id) reject any request with a missing/invalid token with a real `401`. The Flutter app always goes through this surface. The **classic routes** (`/api/upload`, `/api/parse`, `/api/history`, etc.) and the root/`scan-pfm`/`manual-label` web pages have no login flow and never will — they're dev-only internal tooling, not part of the store-facing product. Every API route still sets `Access-Control-Allow-Origin: *`, so this is fine for a controlled LAN/demo deployment but not for exposing the stack to the open internet as-is.
|
||||
|
||||
- **The Android release build is debug-signed.** `android/app/build.gradle.kts` has a `// TODO: Add your own signing config` and currently signs release builds with the debug key. Fine for internal install/testing, not for Play Store distribution.
|
||||
|
||||
|
||||
@@ -21,7 +21,10 @@ All 32 screenshots referenced below live in this folder (`screenshots/v2/`).
|
||||
|
||||
## 1. App at a glance
|
||||
|
||||
- **Purpose**: field app for Prima Fresh Mart store staff. Two independent
|
||||
- **Purpose**: in-store app for Prima Fresh Mart **store staff (petugas toko)
|
||||
on duty at each store** — not delivery drivers. A staff member photographs
|
||||
the DO when a delivery arrives and confirms receipt themselves; each login
|
||||
account is bound to exactly one store (`role: store`). Two independent
|
||||
scanning modes, switched from the drawer:
|
||||
- **DO Scan** — photograph a paper Delivery Order → OCR extracts header/items → operator reviews/corrects → confirms → PDF receipt.
|
||||
- **Product Scan** — photograph a product package → AI identifies the SKU + expiry date → operator confirms.
|
||||
@@ -74,7 +77,7 @@ All 32 screenshots referenced below live in this folder (`screenshots/v2/`).
|
||||
|
||||
| Screenshot | What it shows |
|
||||
|---|---|
|
||||
| `10-image-preview-blur-check.png` | Real DO photo just picked, blur-checked **live on-device**: Ketajaman Foto (sharpness) **75.6 / 100**, below the 80 minimum → red **"Foto Terdeteksi Blur!"** warning. The button row still lets the driver upload anyway (**Ambil Ulang** / retake vs. **Unggah Dokumen** / upload). |
|
||||
| `10-image-preview-blur-check.png` | Real DO photo just picked, blur-checked **live on-device**: Ketajaman Foto (sharpness) **75.6 / 100**, below the 80 minimum → red **"Foto Terdeteksi Blur!"** warning. The button row still lets the store staff upload anyway (**Ambil Ulang** / retake vs. **Unggah Dokumen** / upload). |
|
||||
| `21-product-image-preview.png` | Product photo, sharper framing → **100.0 / 100**, green **"Kualitas Foto Baik"**. |
|
||||
|
||||
**Algorithm — on-device blur detection** (`lib/features/camera/blur_detector.dart`, runs in a background isolate so the UI doesn't jank):
|
||||
|
||||
Reference in new issue
Block a user