Files
pfm-ocr/docs/workflow_detail_aplikasi.md
T

24 KiB

Analisis Detail Alur Kerja Aplikasi PFM OCR (Mobile & Backend API)

Dokumen ini memuat analisis mendalam mengenai seluruh alur kerja (workflow) sistem Prima Fresh Mart (PFM) Scanner OCR v2, dengan memfokuskan analisis secara eksklusif pada interaksi antara Aplikasi Mobile Flutter dan Layanan Backend API (API Gateway Next.js, Database PostgreSQL, Pipeline OCR FastAPI, vLLM Server, dan Server Klasifikasi YOLO).

Asumsi analisis ini didasarkan sepenuhnya pada peniadaan antarmuka web (web interface/dashboard), sehingga sistem diposisikan murni sebagai aplikasi mobile tersinkronisasi dengan backend OCR.


1. Arsitektur Aliran Data (Tanpa Web Interface)

Berikut adalah diagram alur kerja yang menggambarkan interaksi ujung-ke-ujung (end-to-end) antara aplikasi mobile Flutter dan komponen backend:

graph TD
    %% Sisi Client (Flutter)
    subgraph Client [Aplikasi Mobile Flutter]
        Start([Mulai: Supir/Operator Scan DO]) --> Capture[Ambil Foto via Camera API]
        Capture --> GPS[Ambil Geoposition via Geolocator]
        GPS --> BlurCheck{Cek Kualitas via BlurDetector}
        BlurCheck -- Blurry / Skor < 80 --> Capture
        BlurCheck -- Tajam / Skor >= 80 --> Queue[Tambahkan ke Pending Queue]
        Queue --> Upload[Kirim Multipart POST ke /api/v1/documents/upload]
        
        %% Polling
        Upload --> StartPoll[Mulai Polling GET /api/v1/documents]
        StartPoll --> PollRequest{Cek Dokumen di List?}
        PollRequest -- Belum Parsed / Timeout --> StartPoll
        PollRequest -- Berhasil Parsed --> StateUpdate[Perbarui UI via Riverpod]
        
        %% Editor & PDF
        StateUpdate --> Editor[Manual Correction & Validation]
        Editor --> SaveEdit[Kirim PUT ke /api/v1/documents/:id]
        SaveEdit --> PrintPDF[Cetak Label / A4 PDF Natively]
        PrintPDF --> End([Selesai])
    end

    %% Sisi Backend (Docker Stack)
    subgraph Backend [Backend API & AI Engine]
        %% Nginx & API Gateway
        Upload --> Nginx[Nginx Router :8000]
        Nginx --> UploadAPI[Next.js API Gateway :3000]
        UploadAPI --> DBInsert[SQL: Simpan Dokumen Awal - parsed=false]
        DBInsert --> TriggerParse[Panggil Internal POST /api/parse]
        
        %% Pipeline API
        TriggerParse --> Pipeline[FastAPI Pipeline API :8090]
        Pipeline --> Deskew{Hitung Sudut Rata-rata - Tilt > 1.0 deg?}
        Deskew -- Ya --> Unwarp[Unwarping via UVDoc & Re-Run]
        Deskew -- Tidak --> LayoutDet[Layout Detection via PP-DocLayoutV3]
        Unwarp --> LayoutDet
        
        %% Text Extraction & LLM
        LayoutDet --> TextOCR[PaddleOCR Text Recognition]
        TextOCR --> FormatMD[Format Hasil ke Markdown]
        FormatMD --> VllmProxy[vLLM Server: PaddleOCR-VL-1.6 :8118]
        VllmProxy --> SendMD[Kembalikan Hasil Teks Terstruktur]
        
        %% Next.js Post-processing
        SendMD --> ParserTS[parseDOMetadata - Regex Extraction]
        ParserTS --> StoreMatch[resolveStoreFromText - Fuzzy Toko]
        StoreMatch --> TripleCheck[Intelligent Triple-Check SKU / Nama / Unit]
        TripleCheck --> DBSave[SQL: UPDATE documents - parsed=true & ocr_items]
        DBSave --> PollRequest
        SaveEdit --> Nginx
    end

2. Analisis Alur Kerja Sisi Client (Aplikasi Mobile Flutter)

Aplikasi mobile Flutter bertindak sebagai gerbang pertama pengumpulan data di lapangan oleh supir/operator pengiriman.

A. Pengambilan Gambar & Geolokasi

  1. Operator menggunakan modul kamera bawaan aplikasi Flutter untuk memotret kertas dokumen Delivery Order (DO).
  2. Bersamaan dengan pengambilan foto, aplikasi mengakses layanan lokasi perangkat menggunakan paket geolocator untuk mengambil titik koordinat GPS (latitude dan longitude). Koordinat ini akan disematkan sebagai penanda lokasi pengunggahan (geolocation tags).

B. Pendeteksian Blur (Blur Detection) secara Real-time

Untuk memastikan gambar yang dikirim memiliki kualitas yang layak dibaca oleh mesin OCR, aplikasi menjalankan deteksi ketajaman gambar via kelas BlurDetector sebelum diunggah:

  • Isolate Execution: Agar antarmuka (UI) tidak mengalami patah-patah (stuttering), pemrosesan gambar dipindahkan ke Dart Isolate terpisah menggunakan fungsi compute.
  • Preprocessing Gambar:
    1. Gambar asli diperkecil skalanya (resized) menjadi lebar 300 piksel untuk mempercepat kalkulasi.
    2. Dilakukan pemotongan area tengah sebesar 60% (center crop), dengan tujuan mengabaikan latar belakang (meja, lantai) dan fokus sepenuhnya pada teks dokumen.
    3. Mengaplikasikan Gaussian Blur dengan radius 1 untuk menyaring noise sensor kamera.
    4. Mengonversi gambar hasil crop ke format skala abu-abu (grayscale).
  • Algoritma Varians Laplacian:
    • Aplikasi memindai piksel gambar dan menerapkan matriks konvolusi Laplacian 3x3 untuk mendeteksi tepi (edge detection): \text{Laplacian} = 4 \times \text{Center} - \text{Left} - \text{Right} - \text{Top} - \text{Bottom}
    • Menghitung nilai varians dari seluruh piksel Laplacian. Nilai varians yang tinggi mengindikasikan banyaknya perubahan intensitas piksel yang tajam (tepi teks jelas), sedangkan varians rendah berarti transisi piksel halus (gambar blur).
  • Penentuan Batas Kelolosan (Thresholding):
    • Varians mentah dipetakan ke skala 0 hingga 100 dengan batas skor kelolosan sebesar 80.0 (setara dengan nilai varians mentah sebesar 400.0 dari batas maksimum 500.0).
    • Jika gambar di bawah skor 80.0, aplikasi memblokir proses unggah dan meminta pengguna mengambil ulang foto.

C. Antrean Unggah & Sinkronisasi Latar Belakang (Background Polling)

Setelah lolos uji blur, berkas gambar didaftarkan ke state management Riverpod melalui kelas PendingDocumentsNotifier:

  1. Status dokumen diatur menjadi PendingDocumentStatus.uploading.
  2. Aplikasi mengirimkan berkas gambar bersama koordinat GPS menggunakan objek FormData melalui pustaka Dio HTTP Client ke endpoint backend: POST /api/v1/documents/upload
  3. Begitu API mengonfirmasi bahwa gambar sukses diunggah ke server (HTTP status 201), aplikasi mengubah status menjadi PendingDocumentStatus.processing.
  4. Mekanisme Polling: Aplikasi Flutter memulai loop polling otomatis di latar belakang dengan melakukan request GET /api/v1/documents setiap 2 detik.
    • Aplikasi membandingkan ID dokumen yang sedang diunggah dengan daftar dokumen yang dikembalikan oleh server.
    • Server hanya akan menampilkan dokumen pada daftar tersebut jika kolom parsed di database sudah bernilai true.
    • Loop polling dibatasi maksimal 60 kali percobaan (timeout 2 menit). Begitu terdeteksi di server, status diubah menjadi PendingDocumentStatus.success dan data terisi ke UI.

D. Koreksi Manual & Standardisasi Sisi Editor

Jika terdapat karakter OCR yang tidak sempurna, operator dapat melakukan penyelarasan langsung pada aplikasi mobile (editor_screen.dart):

  • Menyesuaikan nomor dokumen (PO, SO, DO) atau detail pengiriman (Plat truk, nama driver, nama penerima).
  • Melakukan penyuntingan baris item barang (SKU, kuantitas/banyak, jumlah unit).
  • Memberi tanda bendera (is_flagged) dan catatan khusus (remark) jika ada ketidaksesuaian barang.
  • Menyimpan seluruh perubahan kembali ke database backend melalui endpoint: PUT /api/v1/documents/[id]

E. Ekspor PDF & Cetak Struk Nativel

Setelah data terverifikasi bersih, operator dapat langsung mencetak tanda terima fisik:

  1. Pustaka pdf menyusun tata letak lembaran A4 yang memuat logo PFM, ringkasan PO/SO/DO, informasi pengiriman (Driver, plat nomor, alamat toko), tabel barang hasil OCR yang sudah bersih, serta kolom tanda tangan basah untuk supir dan penjaga toko.
  2. Pustaka printing memicu manajer cetak bawaan sistem operasi (Native OS Print Spooler) untuk mencetak dokumen langsung ke printer thermal/portable via Bluetooth/Wi-Fi atau menyimpannya sebagai berkas PDF lokal.

3. Analisis Alur Kerja Sisi Backend (Gateway API & Database)

Backend bertindak sebagai API gateway dan orkestrator yang mengarahkan gambar dokumen ke unit pemrosesan AI, melakukan sanitasi, dan menyimpannya ke database relasional.

A. Penerimaan Berkas & Inisialisasi Database (/api/v1/documents/upload)

Ketika file diunggah dari aplikasi mobile, Next.js API Gateway memprosesnya sebagai berikut:

  1. Menyimpan berkas gambar fisik ke direktori /uploads dengan nama unik terstempel waktu (Date.now() - safe_name.jpg).
  2. Menghitung nilai hash SHA-256 dari berkas biner untuk melacak integritas data dan mencegah duplikasi konten.
  3. Melakukan insert record ke tabel documents dengan kolom parsed = false dan menyimpan metadata awal beserta koordinat GPS.
  4. Memicu eksekusi parsing secara sinkron dan cepat dengan menembak API internal /api/parse membawa payload nama file.

B. Alur Ekstraksi & Pipeline OCR (/api/parse)

Pada endpoint ini, Next.js meneruskan base64 gambar ke kontainer Python paddleocr-pipeline-api (FastAPI pada port 8090) yang menjalankan pipeline pengolahan gambar berbasis GPU:

1. Deteksi Sudut & Koreksi Kemiringan Otomatis (Auto-Deskew & Unwarping)

Dokumen yang difoto secara manual oleh supir sering kali miring atau melengkung.

  • Sistem mendeteksi poligon koordinat blok teks menggunakan model PP-LCNet_x1_0_doc_ori untuk klasifikasi arah orientasi dokumen.
  • Sistem menghitung kemiringan rata-rata teks secara trigonometri dari koordinat sudut poligon blok teks (block_polygon_points): \text{Kemiringan Rata-rata} = \frac{1}{N} \sum_{i=1}^{N} \arctan\left(\frac{y_1 - y_0}{x_1 - x_0}\right) \times \frac{180}{\pi}
  • Jika rata-rata kemiringan melampaui batas toleransi 1.0 derajat, backend akan menolak hasil bacaan awal dan memicu pemrosesan ulang dengan mengaktifkan modul unwarping dokumen UVDoc (useDocUnwarping: true). Modul ini merentangkan kembali kertas yang melengkung/miring agar teks sejajar horizontal sebelum dibaca ulang.

2. Segmentasi Tata Letak & Deteksi Teks

  • Model PP-DocLayoutV3 membagi area dokumen ke dalam beberapa tipe tata letak (Tabel, Paragraf, Judul, Header, Footer).
  • Untuk memastikan informasi penting di bagian tepi kertas (seperti Nomor PO, SO, dan Tanggal pengiriman) tidak dibuang oleh OCR, parameter markdown_ignore_labels pada pipeline_config_vllm.yaml disetel kosong ([]).
  • Teks dibaca per wilayah menggunakan engine PaddleOCR v6 dan disusun kembali menjadi representasi teks terstruktur berformat Markdown mentah.
  • Pipeline FastAPI mengirimkan teks Markdown tersebut ke kontainer vLLM Server (port 8118) yang memuat model lokal PaddleOCR-VL-1.6-0.9B untuk interpretasi kontekstual lanjutan guna menyusun kembali kalimat-kalimat yang terpecah akibat deteksi tabel.

4. Pengolahan Data Lanjutan & Validasi Lapis Dua (Next.js)

Setelah menerima respons Markdown terstruktur dari pipeline OCR, Next.js Gateway melakukan serangkaian pembersihan data menggunakan aturan logika bisnis yang ketat:

A. Ekstraksi Informasi Utama (Regex Matching)

Fungsi parser.ts menggunakan ekspresi reguler (Regex) untuk menarik data dari teks mentah:

  • No PO (Purchase Order):
    • Mengekstrak kode PO menggunakan pola regex /No\.?\s*PO\s*[:\-]\s*([A-Z0-9\-\/]+)/i.
    • Mengoreksi kegagalan bacaan OCR yang sering menyatukan karakter (fused digits) seperti PO1207000019 dengan memisahkannya kembali menjadi format standar PO/YY/NNNN+ (di mana YY disamakan dengan tahun pada tanggal dokumen).
  • No SO & DO:
    • Mencari pola 10 digit angka numerik (yang biasanya diawali angka 16 atau 10).
  • Plat Nomor Kendaraan:
    • Mencari format plat nomor Indonesia dengan pola [KODE_WILAYAH] [NOMOR_POLISI] [SERI_AKHIR]. Huruf depan divalidasi ke daftar kode plat resmi (seperti B, D, F, DK, dll.).
  • Koreksi Layout Tergeser (Shifted Layout Correction):
    • Kadang-kadang teks label ("Tanggal :", "No. SO :") tercetak terpisah jauh di atas nilainya karena tata letak visual bergeser. Jika parser mendeteksi nilai-nilai kunci kosong tetapi menemukan deretan angka 10-digit berurutan di baris-baris bawahnya, parser otomatis melakukan realinyasi indeks baris untuk menarik nilai ke kunci yang sesuai.

B. Sanitasi & Standardisasi Data (sanitizeParsedMetadata)

Semua metadata yang berhasil ditarik disaring kembali untuk menjamin kepatuhan data sebelum masuk ke database:

  • Tanggal: Harus lolos pola "dd Month yyyy" dengan nama bulan bahasa Inggris lengkap (contoh: "02 February 2026"). Jika OCR menghasilkan tanggal yang salah format atau di luar rentang wajar (2010 s.d. tahun depan), nilainya diganti menjadi "Not Found" agar memaksa operator melakukan koreksi manual, alih-alih melakukan auto-fallback ke tanggal hari ini yang berpotensi memalsukan tanggal dokumen asli.
  • SO & DO: Wajib berupa digit angka murni sepanjang 7 s.d. 12 karakter. Sistem otomatis mengoreksi kesalahan visual karakter OCR yang umum terjadi (visual digit correction):
    • Huruf O atau o di tengah angka \rightarrow 0
    • Huruf I, i, l, atau simbol | di tengah angka \rightarrow 1
    • Huruf S atau s \rightarrow 5
    • Huruf Z atau z \rightarrow 2
    • Huruf B atau b \rightarrow 8
    • Angka bulan salah seperti 012 atau 112 diubah menjadi 02 (Februari).

C. Pencocokan Toko Penerima (Fuzzy Store Matching)

Toko penerima ditentukan secara cerdas dari teks area customer (customerInfo):

  1. Sistem mengambil teks mentah customer, melakukan normalisasi ke huruf kecil, membuang tanda baca, dan memotongnya menjadi token kata-kata unik (tokenization).
  2. Sistem menyaring kata umum atau kata henti (stop words) seperti "dan", "dki", "area", "yth", "kepada", "jalan", "raya", "kelurahan", "kecamatan".
  3. Token tersebut kemudian diiriskan (intersection) dengan seluruh data toko yang terdaftar di database store_master.
  4. Toko yang memiliki kecocokan minimal 2 token dengan skor bobot tertinggi dipilih secara otomatis. Alamat resmi dan nama toko dari database kemudian disematkan pada kolom orderUntuk dan alamat.

D. Sistem Validasi Barang Pintar (Intelligent Triple-Check Matcher)

Baris barang pada tabel DO rentan mengalami kesalahan pembacaan kode SKU maupun nama item. Next.js mengatasinya melalui mekanisme Triple-Check Matcher:

  1. Sistem memuat seluruh master barang dari database sku_master (meliputi nomor SKU, nama item, standar jumlah inner, dan jenis kemasan outer).
  2. Setiap baris barang hasil pembacaan tabel OCR dibandingkan dengan master menggunakan bobot gabungan (Weighted Combined Score):
    • Bobot SKU (50%): Skor kemiripan antara kode SKU hasil pembacaan dan SKU master menggunakan algoritma Levenshtein Distance. Jika ditemukan kecocokan SKU 100% presisi, sistem langsung menetapkan item tersebut sebagai kecocokan mutlak.
    • Bobot Nama Barang (40%): Skor kemiripan string nama barang hasil pembacaan dengan nama barang master.
    • Bobot Kemasan & Satuan (10%): Bonus kecocokan jika satuan kuantitas yang dibaca (misal: KRG, BOX, BAG sebagai kemasan luar/outer) atau harga unit (PAC, PC, KG sebagai kemasan dalam/inner) sesuai dengan definisi jenis kemasan item tersebut di database master.
  3. Penyelarasan & Pembersihan:
    • Jika skor kecocokan gabungan mencapai \ge 0.6, baris tersebut dianggap valid. Sistem otomatis mengganti nomor SKU dan nama barang dengan data master yang resmi, serta menstandardisasi satuan kuantitasnya (contoh: kuantitas angka murni "15" diautocomplete menjadi "15 KRG" berdasarkan jenis kemasan luar item bebek/ayam).
    • Jika skor kecocokan di bawah 0.6 tetapi nomor SKU yang terbaca merupakan angka valid 8 digit, baris item tetap dipertahaman (untuk mendukung pengenalan SKU baru yang belum terdaftar di database master).
    • Jika tidak ada kecocokan dan SKU tidak valid 8-digit, baris tersebut langsung dibuang karena diidentifikasi sebagai kebisingan layout tabel (table noise seperti garis pembatas tabel, tanda tangan supir, atau coretan kupon).

5. Alur Kerja Deteksi & Klasifikasi Produk (Scan Produk Tunggal)

Selain mengolah dokumen DO utuh, aplikasi mobile juga mendukung pemindaian produk/kemasan secara individual untuk memverifikasi kecocokan barang dan mendeteksi tanggal kedaluwarsa (expiry date).

graph TD
    StartScan([Mulai: Scan Produk Tunggal]) --> SendImg[POST /api/scan-pfm dengan Base64 Gambar]
    SendImg --> ForwardAPI[Next.js Meneruskan ke Python FastAPI :8120]
    
    subgraph PyServer [FastAPI Classify-OCR Server]
        ForwardAPI --> YOLO[1. YOLO Classification: Prediksi Kelas Produk]
        YOLO --> Paddle[2. PaddleOCR: Deteksi & Ekstraksi Seluruh Baris Teks]
        Paddle --> ExpExtract[3. extract_expired_date: Regex Pencarian Tanggal]
        ExpExtract --> VisualDraw[4. Menggambar Bounding Box: Teal=Teks, Amber=EXP]
        VisualDraw --> CropExp[5. Crop Region Tanggal Exp sebagai Base64]
        CropExp --> SpotAPI[6. Call Layout-Parsing untuk Spotting Image]
    end
    
    PyServer --> ReturnData[Kembalikan Hasil OCR & Visualisasi ke Next.js]
    ReturnData --> FuzzyMatch[Levenshtein Match: Hasil YOLO vs sku_master]
    FuzzyMatch --> Top5[Ambil Top 5 Hasil dengan Skor Sim > 0.1]
    Top5 --> RespondMobile[Kirim Hasil Akhir JSON ke Flutter Client]
    RespondMobile --> EndScan([Selesai])

A. Klasifikasi YOLO & Deteksi OCR

  1. Aplikasi Flutter mengambil foto produk tunggal dan mengirimkannya ke endpoint Next.js /api/scan-pfm, yang diteruskan ke server FastAPI khusus (classify_ocr_server.py pada port 8120).
  2. YOLO Classification: Server memuat bobot model YOLO yang telah dilatih (produk-pfm-classifier-26n-*e-*.pt) untuk memprediksi kelas produk (nama produk) beserta probabilitas keyakinannya (confidence score).
  3. PaddleOCR Text Extraction: Server menjalankan deteksi teks untuk mengambil seluruh baris tulisan yang tercetak pada label/kemasan produk.

B. Ekstraksi & Pembersihan Tanggal Kedaluwarsa (Expiry Date Spotting)

Pendeteksian tanggal kedaluwarsa pada kemasan produk sangat menantang karena kualitas cetakan dot-matrix pada plastik kemasan sering kali buruk. Server FastAPI menggunakan serangkaian aturanRegex berlapis:

  • Pencarian Kata Kunci EXP: Mencari kata kunci kedaluwarsa seperti EXP, EXPIRY, TGL EXP, BBD, BEST BEFORE, atau BB.
  • Regex Format Tanggal:
    • Pola tanggal rapat tanpa spasi/karakter pembatas (contoh: 05032027 via DDMMYYYY_RE).
    • Pola tanggal dengan pembatas spasi, titik, hubung, atau garis miring (contoh: 05-03-2027 via DD_MM_YYYY_RE).
    • Pola tanggal langsung setelah kata kunci (contoh: BB05032027 via BB_ATTACHED_DATE_RE).
  • Pembersihan Noise Karakter Khusus:
    • Mengganti noise cetakan tanda kurung () atau karakter tidak sengaja 1) menjadi angka 0.
    • Memperbaiki kesalahan interpretasi huruf/angka kembar seperti B8, 8B, atau 88 menjadi BB (Best Before) jika diikuti oleh digit angka.
    • Mengoreksi kegagalan bacaan bulan cetak rapat seperti 012 atau 112 di belakang tanggal menjadi bulan 02 (Februari) (contoh: 020122027 dikoreksi menjadi 02/02/2027).
  • Visualisasi & Pemotongan Area (Visual Cropping):
    • Server memotong poligon area koordinat teks kedaluwarsa yang berhasil diidentifikasi dan mengembalikannya sebagai gambar thumbnail terpotong berformat Base64 (expired_date_crop_base64). Hal ini berguna untuk validasi visual oleh pengguna pada aplikasi mobile.
    • Menggambar kotak pembatas (bounding box) pada gambar produk: kotak Teal untuk tulisan biasa, dan kotak Amber (oranye) dengan label "EXP" untuk menyoroti letak teks tanggal kedaluwarsa.

C. Fuzzy Similarity Matching (Next.js)

Begitu Next.js menerima hasil klasifikasi YOLO dan ekstraksi OCR:

  1. Sistem menghitung kemiripan string menggunakan jarak Levenshtein antara prediksi nama produk YOLO dengan data nama_item pada tabel sku_master.
  2. Mengembalikan Top 5 kecocokan terbaik yang memiliki skor kemiripan >0.1, dengan menetapkan item teratas sebagai isBestMatch = true.
  3. Seluruh hasil klasifikasi produk, kecocokan master SKU, teks kedaluwarsa yang dideteksi, beserta visualisasi gambar kotak pembatas dikirimkan kembali ke aplikasi mobile Flutter.

6. Spesifikasi Skema Database PostgreSQL

Berikut adalah spesifikasi detail tabel-tabel PostgreSQL yang digunakan dalam workflow aplikasi mobile dan backend:

A. Tabel documents

Menyimpan berkas informasi utama dokumen DO yang diunggah.

Nama Kolom Tipe Data Deskripsi
id SERIAL (PK) ID unik dokumen (Auto-increment).
filename VARCHAR(255) Nama file unik yang tersimpan di /uploads (Unique).
upload_time TIMESTAMP Tanggal dan waktu pengunggahan dokumen.
size INTEGER Ukuran berkas gambar dalam satuan bytes.
parsed BOOLEAN Penanda status apakah dokumen telah selesai di-OCR (true/false).
metadata JSONB Menyimpan hasil ekstraksi terstruktur (PO, SO, DO, Tanggal, Plat nomor, Driver).
layout_parsing_result JSONB Menyimpan mentahan respons JSON dari kontainer Pipeline OCR FastAPI.
is_sample BOOLEAN Menandai apakah dokumen merupakan data contoh bawaan sistem (true/false).
file_hash VARCHAR(64) Hash SHA-256 berkas gambar untuk mencegah duplikasi data biner.
processing_logs JSONB Log jejak audit detail dari seluruh tahapan pemrosesan sistem OCR.
latitude NUMERIC Titik koordinat garis lintang (GPS) lokasi unggah dari perangkat mobile.
longitude NUMERIC Titik koordinat garis bujur (GPS) lokasi unggah dari perangkat mobile.

B. Tabel ocr_items

Menyimpan baris item produk yang tertera pada tabel dokumen DO. Terikat relasi One-to-Many terhadap tabel documents.

Nama Kolom Tipe Data Deskripsi
id SERIAL (PK) ID unik baris item (Auto-increment).
document_id INTEGER (FK) Relasi kunci asing merujuk ke documents(id) dengan aksi ON DELETE CASCADE.
row_index INTEGER Nomor urut baris tabel item dalam satu dokumen.
kode_barang_original VARCHAR(255) Kode SKU barang asli hasil pembacaan awal mesin OCR sebelum dicocokkan.
kode_barang VARCHAR(255) Kode SKU barang hasil verifikasi akhir / standardisasi master.
nama_barang VARCHAR(255) Nama/deskripsi barang yang dikirim.
banyak_original VARCHAR(255) Kuantitas barang asli hasil pembacaan awal OCR.
banyak VARCHAR(255) Kuantitas barang hasil standardisasi (misalnya: "15 KRG").
jumlah_original VARCHAR(255) Nilai total barang asli hasil pembacaan awal OCR.
jumlah VARCHAR(255) Nilai total barang hasil standardisasi (misalnya: "450 PAC").
is_flagged BOOLEAN Penanda bendera untuk perhatian khusus / manual check (true/false).
remark VARCHAR(1000) Catatan koreksi atau alasan penandaan bendera pada baris item.

Catatan: Terdapat constraint UNIQUE(document_id, row_index) untuk mencegah tumpang tindih indeks baris.

C. Tabel sku_master

Menyimpan daftar master barang PFM untuk validasi Triple-Check.

Nama Kolom Tipe Data Deskripsi
id SERIAL (PK) ID unik master SKU.
no_sku VARCHAR(255) Nomor SKU barang sepanjang 8 digit angka unik (Unique).
nama_item VARCHAR(255) Deskripsi nama resmi barang.
standar_jumlah VARCHAR(255) Satuan kemasan dalam/inner (contoh: PAC, PC, KG).
jenis_outer VARCHAR(255) Satuan kemasan luar/outer (contoh: Karung, Box, Bag).
created_at TIMESTAMP Tanggal pendaftaran SKU ke database.

D. Tabel store_master

Menyimpan data master toko Prima Fresh Mart untuk pencocokan tujuan pengiriman DO.

Nama Kolom Tipe Data Deskripsi
id SERIAL (PK) ID unik master toko.
nama_toko VARCHAR(255) Nama resmi outlet/toko PFM.
kode_toko VARCHAR(255) Kode unik toko (Unique, contoh: 02-ANC2).
alamat TEXT Alamat lengkap toko PFM untuk tujuan pengiriman.
created_at TIMESTAMP Tanggal pendaftaran toko ke database.