Files
pfm-ocr/docs/workflow_detail.md
T

15 KiB

Dokumentasi Detail Alur Kerja PFM OCR (Prima Fresh Mart)

Dokumen ini memuat penjelasan lengkap mengenai alur kerja (workflow) pengolahan gambar dokumen Delivery Order (DO) hingga data tersimpan di database dan divisualisasikan pada dashboard web.


Ringkasan Alur Kerja

Berikut adalah flowchart lengkap yang menggambarkan aliran data dan pemrosesan dari aplikasi mobile, gateway API, pipeline OCR, hingga penyimpanan database:

graph TD
    %% Sisi Client (Flutter)
    subgraph Client [1. Client-Side: Flutter App]
        Start([Mulai: Supir Scan DO]) --> Capture[Ambil Foto via Camera API]
        Capture --> GPS[Dapatkan Koordinat via Geolocator]
        GPS --> BlurCheck{Cek Kualitas via BlurDetector}
        BlurCheck -- Blur/Jelek --> Capture
        BlurCheck -- Tajam/Baik --> Upload[Kirim Form Data via Dio HTTP Client]
    end

    %% Web Gateway (Nginx & Next.js)
    subgraph Gateway [2. Gateway & API: Next.js & Nginx]
        Upload --> Proxy[Nginx Proxy: Terima Request di Port 8000]
        Proxy --> UploadAPI[Next.js Upload API: Simpan Gambar ke /uploads]
        UploadAPI --> SQLInit[pg Driver: Simpan Dokumen ke DB - parsed=false]
        SQLInit --> TriggerParse[Panggil API /api/parse & Kirim Base64]
    end

    %% Pipeline OCR (FastAPI)
    subgraph OCR [3. Layout Parsing & OCR Pipeline: FastAPI]
        TriggerParse --> OriCheck[Deteksi Orientasi via PP-LCNet]
        OriCheck --> TiltCalc[Hitung Sudut Kemiringan via block_polygon_points]
        TiltCalc --> TiltCheck{Kemiringan > 1.0 deg?}
        TiltCheck -- Ya --> Unwarp[Lakukan Unwarping via UVDoc]
        Unwarp --> ReRun[Proses Ulang Gambar Lurus]
        TiltCheck -- Tidak/Sudah Lurus --> LayoutDet[Segmentasi Tata Letak via PP-DocLayoutV3]
        ReRun --> LayoutDet
        LayoutDet --> TextRec[Deteksi & Pengenalan Teks via PP-OCRv6_medium]
        TextRec --> MarkdownGen[Susun Output Menjadi Teks Markdown]
    end

    %% VLM Server
    subgraph VLM [4. Vision-Language Server: vLLM]
        MarkdownGen --> QueryVLM[Query VLM Lokal via Port 8118]
        QueryVLM --> ModelVLM[Proses Model PaddleOCR-VL-1.6-0.9B]
        ModelVLM --> ResVLM[Kembalikan Hasil Teks Terstruktur]
    end

    %% Next.js Data Processing
    subgraph Processing [5. Pengolahan Data & Validasi: Next.js]
        ResVLM --> RegEx[Ekstraksi Regex PO/SO/DO via parseDOMetadata]
        RegEx --> StoreMatch[Pencocokan Toko via resolveStoreFromText ke DB]
        StoreMatch --> Sanitize[Sanitasi & Standardisasi Data via sanitizeParsedMetadata]
        Sanitize --> SQLSave[Update Dokumen & ocr_items ke DB - parsed=true]
    end

    %% Polling & Selesai
    subgraph Polling [6. Sinkronisasi Akhir: Flutter App]
        SQLSave --> PollRequest[Dio Polling GET /documents tiap 2s]
        PollRequest --> PollCheck{Status parsed = true?}
        PollCheck -- Belum --> PollRequest
        PollCheck -- Ya --> StateUpdate[Riverpod: Perbarui State UI & Tampilkan Data]
        StateUpdate --> PrintPDF[Cetak Label PDF via pdf & printing lib]
        PrintPDF --> End([Selesai])
    end

Tech Stack (Teknologi yang Digunakan)

Sistem ini dibangun menggunakan arsitektur modern yang membagi tugas antara aplikasi mobile client, API gateway, database, dan engine AI/OCR khusus:

1. Client-Side (Aplikasi Mobile)

  • Framework: Flutter (Dart SDK >=3.2.0)
  • State Management & DI: Riverpod (flutter_riverpod)
  • Routing: GoRouter (go_router)
  • HTTP Client: Dio (dio)
  • Local Storage / Cache: Hive (hive, hive_flutter) & SharedPreferences
  • Sensors & Utilities: Camera API (camera), Exif Data (exif), Location Service (geolocator), dan Blur Detection (sensors_plus)
  • PDF & Printing: PDF Generator & Printing (pdf, printing)

2. Backend Gateway & Dashboard Web Admin

  • Framework: Next.js 16 (React 19 & TypeScript)
  • Styling (CSS): TailwindCSS v4
  • Database Driver: Node-postgres (pg)
  • Deployment/Runtime: Node.js & Docker

3. Database

  • Engine: PostgreSQL 15 (di-deploy via Docker)

4. Mesin AI & Pipeline OCR (Python & Docker)

  • Framework OCR: PaddleX / PaddleOCR (menggunakan GPU CUDA 12.6 devel base image)
  • Model Deteksi Tata Letak: PP-DocLayoutV3 (untuk segmentasi blok teks/tabel)
  • Model Koreksi Kemiringan: PP-LCNet_x1_0_doc_ori & UVDoc (untuk auto-deskew & unwarping)
  • Vision-Language Model (VLM): PaddleOCR-VL-1.6-0.9B (VLM 0.9B lokal)
  • VLM Inference Engine: vllm server (berfungsi sebagai back-end inference manager untuk akselerasi GPU)
  • Python Web Framework: FastAPI & Uvicorn (untuk expose /layout-parsing API endpoint)

5. Web Proxy / Server Gateway

  • Engine: Nginx (Alpine) sebagai reverse proxy untuk routing port internal kontainer Next.js, FastAPI (Pipeline-API), dan vLLM.

Interaksi Alur Kerja & Tech Stack

Di bawah ini adalah pemetaan langkah per langkah bagaimana komponen-komponen tech stack di atas berinteraksi secara real-time:

sequenceDiagram
    autonumber
    actor User as Pengguna
    participant Mobile as Flutter App (Dio & Riverpod)
    participant Nginx as Nginx Reverse Proxy
    participant Gateway as Next.js Gateway (TypeScript & pg)
    participant DB as PostgreSQL 15
    participant Pipeline as FastAPI OCR (PaddleX & Python)
    participant VLM as vLLM Server (PaddleOCR-VL-1.6)

    User->>Mobile: Ambil foto DO
    Note over Mobile: Kamera & GPS diakses via camera & geolocator.<br/>Pemeriksaan blur via BlurDetector.
    Mobile->>Nginx: POST /api/v1/documents/upload (Base64/Form Data via Dio)
    Nginx->>Gateway: Forward ke Next.js (port 3000)
    Gateway->>DB: Simpan data awal dokumen (pg driver)
    Gateway->>Pipeline: POST /layout-parsing (Base64)
    Note over Pipeline: 1. Koreksi kemiringan (PP-LCNet & UVDoc)<br/>2. Deteksi layout tabel & teks (PP-DocLayoutV3)<br/>3. Ekstraksi teks (PP-OCRv6)<br/>4. Hasil akhir diubah ke Markdown.
    Pipeline->>VLM: Query VLM lokal (port 8118 via vLLM)
    VLM-->>Pipeline: Respons teks terstruktur dari model VL-1.6-0.9B
    Pipeline-->>Gateway: Kembalikan Markdown lengkap & koordinat polygon
    Note over Gateway: 1. parseDOMetadata() mengurai PO, SO, DO via Regex.<br/>2. resolveStoreFromText() mencocokkan toko ke DB.<br/>3. sanitizeParsedMetadata() menstandardisasi tanggal & plat.
    Gateway->>DB: INSERT/UPDATE metadata & ocr_items (parsed = true)
    
    loop Polling (tiap 2 detik)
        Mobile->>Gateway: GET /documents (Status check via Dio)
        Gateway-->>Mobile: Kirim data final (di-render via Riverpod)
    end
    
    User->>Mobile: Cetak Label
    Note over Mobile: Generate & cetak dokumen PDF via pdf & printing lib.

Penjelasan Alur Data & Teknologi:

  1. Pengambilan Gambar (Flutter Client): Pengguna mengambil foto Delivery Order (DO). Aplikasi Flutter mengakses kamera via Camera API dan mengambil GPS via Geolocator. Setelah lolos uji kualitas blur via BlurDetector, data dikirim menggunakan Dio HTTP Client via request POST Multipart Form ke Nginx pada port 8000 (eksternal) ke endpoint /api/v1/documents/upload (untuk supir/mobile) atau /api/upload (untuk dashboard web admin).
  2. Reverse Proxy (Nginx): Kontainer paddleocr-nginx mendengarkan di port 80 (internal) / 8000 (eksternal). Nginx merutekan request dari client eksternal secara internal di dalam Docker network:
    • Request ke /api/* diteruskan ke kontainer paddleocr-pfm-web-app pada port 3000.
    • Request langsung ke /layout-parsing diteruskan ke kontainer paddleocr-pipeline-api-v10 pada port 8090.
    • Request langsung ke /v1 diteruskan ke kontainer paddleocr-vllm-server pada port 8118.
  3. Penyimpanan Dokumen Awal (Next.js Gateway): Kontainer paddleocr-pfm-web-app menerima request upload di port 3000, menulis gambar fisik ke folder /uploads, dan menyimpannya ke database paddleocr-db pada port 5432 dengan query INSERT (status parsed = false) menggunakan driver pg (node-postgres). Setelah itu, Next.js mengirimkan data Base64 via request POST secara internal ke kontainer paddleocr-pipeline-api-v10 pada port 8090 ke endpoint /layout-parsing.
  4. Layout-Parsing (FastAPI OCR Pipeline): Kontainer paddleocr-pipeline-api-v10 menerima request di port 8090 pada endpoint /layout-parsing. Menjalankan kode Python dengan model-model:
    • Klasifikasi orientasi & unwarping kemiringan via PP-LCNet_x1_0_doc_ori & UVDoc.
    • Deteksi tata letak via PP-DocLayoutV3.
    • Pengenalan teks via PP-OCRv6_medium dengan akselerasi GPU CUDA 12.6.
    • Hasil parsing ditransformasi menjadi teks Markdown mentah.
  5. Pemahaman Konten Dokumen (vLLM Server): FastAPI (pipeline-api) meneruskan request secara internal ke kontainer paddleocr-vllm-server di port 8118 pada endpoint /v1/chat/completions dengan menggunakan Vision-Language Model PaddleOCR-VL-1.6-0.9B untuk menganalisis teks secara kontekstual.
  6. Ekstraksi Metadata & Fuzzy Matching (Next.js): Next.js menerima respons balik, mengekstrak data PO, SO, DO via Regex (parseDOMetadata), mencocokkan toko penerima ke database PostgreSQL port 5432 (resolveStoreFromText), menstandardisasi format data (sanitizeParsedMetadata), lalu memperbarui status dokumen di PostgreSQL menjadi parsed = true dan menyimpan item barang ke tabel ocr_items.
  7. Sinkronisasi (Flutter Polling): Aplikasi Flutter mengirim request GET secara berkala setiap 2 detik menggunakan Dio ke port 8000 (eksternal) ke endpoint /api/documents (atau /api/documents/[id]) untuk memantau status parsing. Begitu status berubah menjadi true, Riverpod memperbarui UI secara real-time pada perangkat supir. Pengguna juga dapat mengekspor data ke format PDF menggunakan pustaka pdf & printing Flutter.

Detail Langkah Per Langkah

Langkah 1: Client-Side (Flutter App)

  1. Layar Konfirmasi: Setelah memotret dokumen, pengguna diarahkan ke image_preview_screen.dart. Di layar ini, sistem mendeteksi apakah gambar blur menggunakan kelas BlurDetector.
  2. Unggah Dokumen: Pengguna menekan tombol "Unggah Dokumen". Ini memicu aksi pada PendingDocumentsNotifier di pending_documents_provider.dart:
    • Status dokumen diatur ke PendingDocumentStatus.uploading.
    • Koordinat GPS (latitude & longitude) diambil menggunakan LocationService.
    • Gambar dikirim menggunakan form data ke backend (endpoint /documents/upload).
    • Halaman otomatis beralih ke daftar dokumen (/documents) untuk memulai background polling setiap 2 detik.

Langkah 2: Gateway Penerimaan (Next.js Upload)

Ada dua gerbang masuk upload:

  1. API Web (route.ts (api/upload)): Digunakan oleh dashboard website admin.
  2. API Mobile v1 (route.ts (api/v1/documents/upload)): Digunakan oleh aplikasi Flutter.

Mekanisme Penyimpanan:

  • Berkas disimpan ke folder /uploads dengan nama unik (Date.now()-filename).
  • Koordinat GPS disimpan.
  • Memicu pemanggilan API parse sinkron /api/parse dengan menyertakan nama file.

Langkah 3: Mesin Ekstraksi & OCR Pipeline (/api/parse)

Di dalam file route.ts (api/parse):

1. Layout-Parsing (OCR Pipeline)

  • Berkas dikonversi menjadi format Base64 dan diposkan ke API Pipeline (http://localhost:7871/layout-parsing).
  • Pipeline membedah tata letak halaman (tabel, teks, paragraf) dan menghasilkan format terstruktur (Markdown) beserta informasi polygon koordinat tiap blok teks.
  • Konfigurasi markdown_ignore_labels disetel kosong ([]) pada berkas pipeline_config_vllm.yaml agar elemen tata letak seperti header, footer, footnote, number, dan aside_text tidak di-filter oleh detektor layout. Hal ini memastikan informasi administratif penting (seperti nomor PO/SO/Tanggal) yang terletak di bagian atas atau bawah halaman tetap terekstrak secara akurat.

2. Auto-Deskew & Unwarping (Rotasi Gambar Otomatis)

  • Sudut kemiringan dihitung secara trigonometris melalui calculateAverageTilt(data) dengan melihat titik polygon block_polygon_points: \text{Angle} = \arctan\left(\frac{dy}{dx}\right) \times \frac{180}{\pi}
  • Jika kemiringan rata-rata melampaui 1.0 derajat, backend otomatis memicu pemrosesan ulang dengan menyalakan flag:
    • useDocUnwarping: true
    • useDocOrientationClassify: true
  • Hal ini menjamin pembacaan teks lurus dan akurat walaupun dokumen difoto miring.

3. Ekstraksi Metadata Lapis 1 (Regex & Alinyemen Layout)

Fungsi parseDOMetadata(markdownText) mengurai konten Markdown hasil OCR:

  • Vendor & Customer: Mencari baris teks berawalan PT. (Vendor dibedakan dengan Customer/Primafood).
  • Nomor Dokumen:
    • No PO: Pola PO/YY/XXXX (dipaksa tahun berjalan).
    • No SO & DO: Pola numerik 10 digit (misal: dimulai dengan 16).
  • Koreksi Layout Tergeser (Shifted Layout): Jika hasil OCR tidak menyejajarkan teks label dengan nilainya (misal, nilai nomor SO tergeser di bawah label DO), parser memindai baris terdekat untuk melakukan pemetaan ulang berdasarkan kedekatan indeks baris.

4. Sanitasi Lapis 2 (Validator Sanity Check)

Fungsi sanitizeParsedMetadata() menjamin kepatuhan data sebelum disimpan ke database:

  • Tanggal: Wajib berformat "dd Month yyyy" (Januari s.d. Desember Inggris). Jika tidak valid atau di luar rentang (2010 s.d. tahun depan), nilainya diganti "Not Found".
  • SO & DO: Wajib berupa digit angka murni sepanjang 7 s.d. 12 karakter.
  • Plat Nomor: Wajib memiliki format plat nomor Indonesia yang valid (misalnya: B 1234 ABC).

5. Pencocokan Toko (Fuzzy Database Match)

Fungsi resolveStoreFromText() mencocokkan teks penerima dengan data toko aktif di database (store_master):

  • Kata umum seperti "dan", "dki", "jalan", "raya", "kelurahan" dihapus dari token OCR.
  • Dilakukan penghitungan rasio irisan token (token intersection score) dengan seluruh kombinasi nama_toko dan alamat di database.
  • Toko dengan kecocokan minimal 2 token dan skor tertinggi dipilih untuk mengisi kolom orderUntuk dan alamat.

6. Parsing Tabel Item SKU

  • Backend memindai tag <table> di Markdown.
  • Kolom kode barang dibersihkan dan difilter ketat untuk hanya menerima format 8 digit angka murni (sesuai standar SKU item internal).
  • Jika kolom kuantitas (banyak) tidak terbaca satuannya (misal hanya angka "10"), backend melengkapinya berdasarkan pola SKU (contoh: SKU 11310024 dilengkapi menjadi "10 KRG").

Langkah 4: Penyimpanan Database (PostgreSQL)

  1. Metadata dokumen disimpan atau diperbarui pada tabel documents:
    • Kolom status parsing diubah menjadi parsed = true.
    • Kolom JSONB metadata menyimpan struktur PO, SO, DO, Plat Truk, Tanggal, dll.
    • Kolom JSONB layout_parsing_result menyimpan mentahan respons Pipeline OCR (termasuk data tilt).
    • Kolom JSONB processing_logs menyimpan log terperinci tiap tahapan ekstraksi.
  2. Item lama yang terkait dengan dokumen di tabel ocr_items dihapus untuk menghindari data usang, kemudian data item hasil ekstraksi baru dimasukkan kembali via bulk-insert.