Dokumentasi Lengkap · Diekspor dari README.md

Analisis Performance Kasir/Toko via Video x Data Transaksi — Perusahaan Jam Tangan

Project baru, di luar unit bisnis biasa. Client: perusahaan jam tangan yang ingin melihat perilaku konsumen di toko/booth via kamera. Fokus saat ini bukan gamifikasi, tapi: mengukur performance kasir/toko dengan meng-korelasikan durasi/jenis interaksi (dari video) dengan hasil transaksi aktual (dari POS) — closing rate dan nominal transaksi per konsumen. Skema gamifikasi (poin/leaderboard) ditunda, lihat §11.

Ringkasan Eksekutif

Daftar Isi

  1. Kebutuhan Data
  2. Prinsip Desain
  3. Arsitektur (2 Lapis: CV Murah + VLM Batch)
  4. Pilihan Model VLM untuk Layer 2
  5. Fokus Saat Ini: Korelasi Video x Data Transaksi — termasu §5.3 backtrack loyalty, §5.6 marker staf, §5.7 employee monitoring
  6. Privasi & Kepatuhan
  7. Rencana Bertahap (MVP → Scale)
  8. Pertanyaan Terbuka untuk Client
  9. Tech Stack Konkret
  10. Data Model (skema tabel inti: video+POS)
  11. Skema Gamifikasi (Ditunda)
  12. Estimasi Biaya Kasar
  13. Katalog Metrik Performance Toko dari Video — Tier 1/2/3
  14. Rencana Implementasi Konkret
  15. Data yang Direkam (Data Dictionary Ringkas)
  16. Ide Pengembangan Proses Bisnis & Gamifikasi Lanjutan
  17. Eksplorasi Mendalam: Gamifikasi Staff
  18. Riset Eksternal (Referensi Gamifikasi)
  19. Data Model Gamifikasi (Konsumen & Staf) — termasuk §19.5 contoh end-to-end "Budi"
  20. Ringkasan Opsi "Optimal Tapi Rawan"
  21. Sinyal Amatan Lanjutan untuk Upselling & Analisis Konsumen
  22. Dari Data ke Aksi: Sintesis Output per Pemangku Kepentingan
  23. Program Loyalitas Fisik: Baterai Gratis & Trade-In Refurbish
  24. Strategi Loyalitas Konsumen — Analisis Berbasis Haidilao
  25. Sistem Gamifikasi & Loyalitas Pelanggan — Rancangan Final
  26. Deteksi Presisi per Etalase — SKU-Level Tanpa VLM

1. Kebutuhan Data

Perusahaan ingin tahu, per sesi kunjungan (bukan per identitas orang — lihat catatan di bawah):

Metrik Definisi Sumber
Dwell time Berapa lama dia berada di area toko/booth Person tracking (masuk → keluar frame)
Interaction time Berapa lama dia berinteraksi langsung — pegang jam, coba, ngobrol dengan SPG/SPB Deteksi interaksi objek/tangan + tracking
Viewing/attention time Berapa lama dia melihat ke arah display/produk (bukan sekadar lewat) Head/gaze orientation estimation

Semua ini tidak butuh realtime — cukup direkap per hari/per shift, lalu diagregasi. Ini penting karena membuka opsi arsitektur yang jauh lebih murah.

Catatan penting (update arah project): identifikasi siapa konsumennya (dan frekuensi kunjungan lintas hari) tidak diperlukan lewat biometrik. Fokusnya adalah kenapa transaksi terjadi (atau tidak) — jadi metrik di atas cukup hidup untuk satu sesi kunjungan (track_id sekali pakai, tidak perlu di-link ke kunjungan sebelumnya lewat Re-ID), lalu dikorelasikan ke hasil transaksi di §5.

Kenapa desain ini dipilih — bukan cuma soal UU PDP: alasan utamanya justru teknis & biaya, privasi cuma bonus. Person tracking dalam 1 sesi (orang A/B/C/D ditandai dari masuk sampai keluar frame, lalu di-relabel jadi identitas transaksi/member begitu dia bayar — alurnya persis §5.3) itu CV klasik yang murah: deteksi + tracking sederhana (YOLO+ByteTrack, §14), tidak butuh model wajah sama sekali. Re-ID biometrik persisten (simpan face/body embedding permanen supaya bisa kenali "orang yang sama" lintas hari) itu kelas masalah yang jauh lebih berat: butuh resolusi kamera tinggi buat wajah jelas, model embedding yang lebih besar & lebih mahal secara compute, storage untuk database embedding yang terus tumbuh, dan proses matching yang makin lambat/rawan salah seiring database membesar. Skema "tag sementara → relabel saat transaksi" di §5.3 menghindari semua beban itu — plus kebetulan juga lebih aman secara UU PDP (win-win, bukan trade-off).

⚠️ Versi optimal (rawan UU PDP + mahal secara compute): kalau kedua batasan itu diabaikan, opsi paling optimal adalah Re-ID biometrik persisten — ini memberi frekuensi kunjungan untuk semua pengunjung, bukan cuma yang transaksi + scan loyalty seperti alur backtrack di §5.3. Lebih kaya data, tapi mahal secara infrastruktur DAN melanggar UU PDP — dua alasan independen untuk skip, bukan cuma satu (lihat §20 untuk daftar lengkap opsi optimal yang di-skip).

2. Prinsip Desain

3. Arsitektur (2 Lapis: CV Murah + VLM Batch)

[Kamera/CCTV] 
     │  (rekam terus, buffer lokal 5–15 menit/klip)
     ▼
[Layer 1 — Edge/Cheap CV, jalan terus-menerus, MURAH]
  • Person detector + tracker  (YOLOv8/v11 + ByteTrack/DeepSORT)
  • Re-ID sederhana → deteksi "orang yang sama datang lagi"
  • Hitung dwell time otomatis dari track masuk–keluar
  • Estimasi head-pose/gaze kasar (opsional, model ringan) → proxy viewing time
  • Output: tabel event mentah (track_id, timestamp masuk/keluar, koordinat, snapshot thumbnail)
     │  → simpan ke DB lokal / object storage (bukan LLM, murni CV klasik)
     ▼
[Layer 2 — Batch VLM Pass, jalan PERIODIK (mis. tiap 15–30 menit atau end-of-day)]
  • Ambil sampel frame/klip pendek dari track yang "menarik" saja:
      - dwell time di atas threshold
      - ada interaksi tangan-objek terdeteksi Layer 1
  • Kirim ke VLM murah untuk enrichment semantik:
      - "orang ini memegang jam tangan model apa?"
      - "sedang mencoba di pergelangan tangan / hanya lihat-lihat?"
      - "berinteraksi dengan staff?"
  • Output: label semantik ditempel ke track_id yang sudah ada
     │
     ▼
[Aggregation & Scoring Engine]
  • Gabungkan Layer 1 (angka: durasi, frekuensi) + Layer 2 (label: jenis interaksi)
  • Hitung skor gamifikasi per sesi/per konsumen/per toko
     │
     ▼
[Dashboard / Leaderboard]
  • Insight harian, badge, ranking toko/SPG, dsb.

Kenapa dipisah begini: Layer 1 (CV klasik) itu murah banget secara compute dan bisa jalan terus 24 jam di edge device (mini PC/NVR) tanpa biaya API. Layer 1 sendirian sudah cukup untuk 3 dari 4 metrik (dwell time, frekuensi, viewing proxy). VLM/LLM (Layer 2) baru dipanggil untuk hal yang butuh "pemahaman" — dan itu di-batch, bukan realtime, jadi biaya API jauh lebih kecil (mis. dari ribuan frame/hari jadi puluhan klip/hari yang benar-benar relevan).

4. Pilihan Model VLM untuk Layer 2 (batch, efisien)

Tidak perlu model paling pintar — cukup yang murah & cukup akurat untuk klasifikasi visual sederhana:

5. Fokus Saat Ini: Korelasi Video x Data Transaksi (Performance Kasir/Toko)

Ide inti: video analytics sendirian cuma tahu "berapa lama orang berinteraksi". Itu baru berguna kalau di-join dengan data transaksi POS — baru bisa jawab: interaksi yang lebih lama/jenis tertentu, apakah benar berkorelasi dengan closing rate lebih tinggi atau nominal transaksi lebih besar. Ini memungkinkan, dan justru lebih actionable daripada gamifikasi murni, karena ada ground truth (uang masuk) untuk validasi.

5.1 Syarat kunci: join key yang sama

Video track dan transaksi POS adalah dua sumber data terpisah. Supaya bisa disatukan per orang/per momen, butuh kunci penghubung:

Kunci Dari mana Catatan
store_id Sama-sama ada di video & POS Wajib
counter_id / till_id Kamera diarahkan/di-zoning ke meja kasir spesifik, bukan cuma "toko" secara umum Kalau 1 toko ada beberapa kasir, ini penting supaya interaksi ke-link ke kasir yang benar
cashier_id POS sudah pasti catat siapa yang login/proses transaksi Ini yang bikin bisa breakdown performance per orang kasir
Waktu (entered_atexited_at vs transaksi.created_at) Video kasih rentang waktu interaksi, POS kasih timestamp transaksi selesai Match kalau transaksi.created_at jatuh di dalam [entered_at, exited_at + buffer beberapa menit] pada counter_id yang sama

Kalau kamera masih general (bukan per-counter), korelasi masih bisa dibuat di level toko/shift (bukan per kasir individu) — tetap berguna, cuma resolusinya lebih kasar.

5.2 Definisi "closing" vs "non-closing"

Track di zona kasir yang diikuti transaksi dalam window waktu tertentu → dianggap closing. Track yang tidak diikuti transaksi dalam window itu → non-closing (calon pembeli yang batal/hanya tanya-tanya). Ini penting: tanpa kelas non-closing, kamu cuma punya data orang yang beli, bukan funnel lengkap "berapa dari total interaksi yang jadi closing".

5.3 Bonus: Backtrack Identitas via Transaksi Loyalty (bukan Re-ID)

Insight tambahan dari ide "tandai A/B/C/D dulu, identitas baru muncul saat transaksi": kalau transaksi pakai program loyalty (member scan kartu/nomor HP saat checkout), identitas riil konsumen muncul sendiri di titik transaksi — tidak perlu dipaksa lewat face recognition/Re-ID selama dia keliling toko.

Alurnya: 1. Selama di toko, konsumen cuma "orang A/B/C/D" — track_id anonim biasa dari Layer 1, persis seperti desain di §1 (sekali pakai, tanpa identitas). 2. Begitu dia transaksi dan POS mencatat loyalty_id (karena dia scan member), track_transaction_match (§10) sudah otomatis tahu track_id mana yang match ke transaksi itu (via window waktu + counter_id, lihat §5.1). 3. Karena transaksi_id sekarang bisa punya loyalty_id, tinggal 1 join lagi: loyalty_idtransaksi_idtrack_idseluruh histori sesi kunjungan itu (dwell time, viewing time, produk yang dilihat, urutan interaksi) bisa di-backtrack dan diatribusikan ke member tersebut.

Kenapa ini lebih baik dari Re-ID biometrik: - Identitas cuma nempel kalau konsumennya sendiri yang memilih identifikasi diri (scan member) — bukan sistem yang diam-diam mengenali wajah/tubuh dia dari video. Ini consent-based, bukan surveillance-based. - Tidak butuh model Re-ID/embedding sama sekali untuk dapat manfaat ini — cukup 1 kolom loyalty_id di tabel transaksi yang sudah ada, dan join sederhana. - Frekuensi kunjungan pun bisa didapat kembali tanpa biometrik apa pun: kalau loyalty_id yang sama muncul di beberapa transaksi lintas hari, itu otomatis histori kunjungan dia. Bedanya dengan Re-ID murni: ini cuma menangkap kunjungan yang berujung transaksi + scan member — kunjungan yang cuma lihat-lihat tanpa beli tetap anonim total, dan itu memang sudah sesuai arah §1 (gak perlu tahu siapa yang cuma lihat-lihat).

Syarat: POS/program loyalty perlu catat loyalty_id di tabel transaksi (kolom tambahan, lihat §10), dan matching track_transaction_match harus cukup presisi (counter_id + window waktu ketat) — kalau kamera masih general per-toko, ada risiko salah atribusi sesi ke orang lain yang kebetulan ada di dekat kasir saat transaksi berlangsung.

5.4 Metrik turunan yang bisa dihitung

5.5 Hati-hati: korelasi ≠ sebab-akibat

Interaksi yang lama bisa berarti dua hal berlawanan: kasir jago closing (interaksi persuasif) atau transaksi itu memang rumit (banyak pertanyaan karena barang mahal/kompleks) sehingga makan waktu meski akhirnya closing. Supaya kesimpulan tidak salah arah: - Kontrol dengan jumlah item per transaksi (item_count) — transaksi besar wajar makan waktu lebih lama. - Bandingkan kasir yang pegang traffic/jam sibuk yang mirip, jangan bandingkan kasir shift sepi vs shift ramai langsung. - Perlakukan hasil awal sebagai hipotesis untuk didiskusikan dengan store manager, bukan vonis final performance.

5.6 Identifikasi Kasir Tanpa Face — Visual Tag/Marker

Ide: tandai staff pakai penanda fisik di seragam (mis. patch/armband warna di pundak) alih-alih pakai face recognition untuk tahu "ini kasir siapa". Ini lebih baik, bukan cuma alternatif — karena target adalah staff (bukan konsumen), staff bisa diminta pakai marker; konsumen tidak bisa.

2 opsi implementasi:

Opsi Cara kerja Kapasitas ID Kapan dipakai
Color patch/armband Warna solid cerah (mis. kuning, merah, hijau) di bahu/kerah. Deteksi via HSV color segmentation — klasik CV, sangat ringan, robust di resolusi rendah karena blob warna besar tetap kedeteksi walau gambar buram ~6–8 warna yang gampang dibedakan kamera CCTV di berbagai kondisi cahaya; kalau staff lebih banyak, kombinasikan 2 warna atau posisi kiri/kanan Toko kecil, jumlah kasir sedikit (≤6–8), mau setup paling murah & cepat
Fiducial marker (ArUco/AprilTag) Badge kecil pola hitam-putih unik per orang, decodable via cv2.aruco (OpenCV, built-in). Didesain khusus untuk robust di resolusi rendah & sudut pandang, tidak bergantung warna/cahaya seperti color patch Ratusan ID unik Multi-toko/banyak staff, butuh scalable & lebih tahan kondisi cahaya toko yang bervariasi

Implikasi ke arsitektur: - cashier_id sekarang bisa langsung didapat dari deteksi marker di Layer 1 (CV klasik yang sudah jalan untuk person tracking) — tidak perlu embedding biometrik apa pun untuk staff. Ini menyederhanakan pipeline sekaligus memperkuat privasi (nol data biometrik staff yang disimpan). - Karena marker menempel di badan/seragam (bukan posisi kamera tetap), staff yang berpindah counter/booth tetap ter-track dengan benar — gak perlu asumsi kaku "kasir X selalu di counter Y" seperti di §5.1. - Mapping warna/kode marker → nama kasir cukup disimpan di tabel kecil terpisah (cashier_marker_map), diupdate manual oleh store manager tiap kali ada rotasi staff atau ganti seragam — bukan bagian dari pipeline CV. - Catatan penting: ini solusi khusus untuk staff (marker fisik). Untuk konsumen, marker jelas tidak relevan — tapi itu juga tidak masalah, karena frekuensi kunjungan konsumen (kalau memang dibutuhkan) sudah bisa didapat lewat jalur lain yang lebih baik: backtrack via loyalty program saat transaksi, lihat §5.3. Jadi konsumen tidak butuh Re-ID/embedding tubuh sama sekali di skema ini.

5.7 Yang perlu diperhatikan: ini employee monitoring

Berbeda dari analytics konsumen anonim — ini secara langsung menilai kinerja individu karyawan (kasir). Perlu: - Transparansi ke karyawan bahwa video dipakai juga untuk evaluasi performance (bukan cuma "keamanan toko"), idealnya tertulis di kebijakan internal/kontrak kerja. - Dipakai sebagai coaching tool (identifikasi siapa butuh training) lebih aman secara relasi kerja daripada langsung jadi dasar punitive (potong bonus, dsb) tanpa proses lain.

6. Privasi & Kepatuhan (wajib dipikirkan, ini CCTV orang publik)

Update dari riset eksternal (§18): kalau nanti sistem gamifikasi konsumen (§16-B) benar-benar dibangun dan mengumpulkan data pribadi (nama, no. HP, tanggal lahir lewat pendaftaran member/loyalty), berlaku kepatuhan tambahan di luar soal CCTV:

Lihat detail lengkap di riset-eksternal/gemini-2.md §I dan riset-eksternal/chatgpt-1.md.

⚠️ Versi optimal (rawan UU PDP): tanpa batasan consent/minimization, opsi paling optimal adalah auto-enroll semua pengunjung ke sistem (tanpa opt-in), retensi video mentah tanpa batas waktu (bukan purge 7–30 hari — supaya bisa diproses ulang pakai model masa depan yang lebih akurat), dan enrichment profil lewat inferensi VLM (estimasi umur, gender, kelas ekonomi dari pakaian/aksesoris) untuk personalisasi lebih tajam. Semua ini teknis mungkin dan akan menambah akurasi/insight, tapi masing-masing melanggar prinsip lawful basis, data minimization, dan storage limitation UU PDP — lihat ringkasan lengkap di §20.

7. Rencana Bertahap (MVP → Scale)

  1. MVP: 1 kamera per toko/booth, Layer 1 saja (dwell time + frekuensi kasar via re-ID), rekap harian manual/CSV. Buktikan dulu datanya berguna.
  2. Tambah Layer 2: aktifkan batch VLM untuk sample klip dwell time tinggi → dapat label interaksi.
  3. Dashboard otomatis + scoring gamifikasi setelah skema A/B di atas jelas.
  4. Scale ke multi-toko setelah pipeline 1 toko stabil dan biaya per-toko sudah terukur.

8. Pertanyaan Terbuka untuk Client

9. Tech Stack Konkret (usulan MVP, 1 toko)

Komponen Pilihan Alasan
Kamera IP camera 1080p existing CCTV, atau webcam USB kalau booth kecil Tidak perlu kamera khusus untuk MVP
Edge compute Mini PC / NUC dengan GPU kecil (mis. Jetson Orin Nano) atau cukup CPU kalau model ringan Layer 1 (YOLO+tracker) ringan, bisa jalan di CPU utk 1 kamera
Person detection + tracking YOLOv8n/v11n + ByteTrack (Ultralytics, open-source) Model kecil, real-time di edge, gratis
Re-ID konsumen (deteksi "orang yang sama datang lagi") OSNet embedding sederhana, cosine similarity antar track Tidak butuh wajah utuh, cukup embedding vektor
Identifikasi kasir (bukan face) HSV color segmentation untuk color patch/armband, atau cv2.aruco untuk badge fiducial marker Nol biometrik staff yang disimpan, robust di resolusi rendah, jauh lebih murah dari face recognition — lihat §5.6
Storage klip & buffer Disk lokal, rolling buffer 7–30 hari (auto-purge) Kontrol retensi untuk privasi
Batch job scheduler Cron / simple worker (tiap 15–30 menit) yang scan track baru → panggil VLM Tidak perlu infra streaming yang berat
VLM Layer 2 Gemini Flash / GPT-5-mini / Claude Haiku (API), atau SmolVLM self-host kalau volume tinggi Model-agnostic, tinggal ganti endpoint
Database Postgres/SQLite — 1 tabel track mentah, 1 tabel enrichment, 1 tabel skor agregat Simpel, sesuai skala 1 toko dulu
Dashboard Web sederhana (bisa reuse pattern Aldis ERP: Supabase + static admin page) kalau mau cepat Konsisten dengan tooling yang sudah biasa dipakai

10. Data Model (skema tabel inti)

-- Layer 1: hasil tracking mentah, ditulis terus-menerus oleh edge
CREATE TABLE track_event (
    track_id        TEXT PRIMARY KEY,      -- id unik per kemunculan orang di frame
    store_id        TEXT NOT NULL,
    counter_id       TEXT,                  -- kasir/meja mana (null kalau kamera masih general per-toko)
    detected_marker TEXT,                  -- kode warna/ArUco id kalau track ini staff (lihat cashier_marker_map) → null kalau konsumen
    entered_at      TIMESTAMP NOT NULL,
    exited_at       TIMESTAMP,
    dwell_seconds   INT,                    -- exited_at - entered_at
    gaze_seconds    INT,                    -- estimasi durasi menghadap display (proxy dari head-pose)
    thumbnail_path  TEXT,                   -- 1-2 snapshot untuk keperluan Layer 2, bukan video mentah
    has_hand_object_interaction BOOLEAN DEFAULT FALSE  -- trigger buat masuk antrian Layer 2
);

-- Layer 2: enrichment semantik, ditulis oleh batch VLM job
CREATE TABLE track_enrichment (
    track_id            TEXT REFERENCES track_event(track_id),
    interaction_type    TEXT,   -- 'lihat_saja' | 'pegang_coba' | 'ngobrol_staff' | dst
    product_hint        TEXT,   -- deskripsi model jam yang kelihatan, kalau VLM bisa infer
    interaction_seconds INT,
    processed_at        TIMESTAMP,
    model_used          TEXT    -- catat model VLM yang dipakai, buat audit biaya/akurasi
);

-- Definisi zona toko (Tier 2, §14 Fase 2) — dipetakan manual sekali, dipakai ulang oleh semua sesi
CREATE TABLE zone_definition (
    zone_id       TEXT PRIMARY KEY,   -- mis. 'A', 'B', 'C', 'D'
    store_id      TEXT NOT NULL,
    label         TEXT,               -- mis. "Rak Chronograph Series", "Display Dress Watch"
    product_category TEXT             -- kategori/koleksi produk di zona ini — dipakai untuk cocokkan ke item yang dibeli
);

-- Breakdown dwell time PER ZONA dalam satu sesi kunjungan — ini yang sebelumnya belum ada
-- track_event.dwell_seconds cuma total sesi; tabel ini pecah jadi per-zona (A 3 menit, B 4 menit, dst)
CREATE TABLE zone_dwell_segment (
    id            SERIAL PRIMARY KEY,
    track_id      TEXT REFERENCES track_event(track_id),
    zone_id       TEXT REFERENCES zone_definition(zone_id),
    entered_at    TIMESTAMP NOT NULL,
    exited_at     TIMESTAMP NOT NULL,
    dwell_seconds INT NOT NULL      -- SUM(dwell_seconds) per track_id boleh > track_event.dwell_seconds kalau orang bolak-balik ke zona yang sama
);

-- Mapping marker → staff, diupdate manual oleh store manager (bukan bagian pipeline CV)
CREATE TABLE cashier_marker_map (
    marker_code TEXT PRIMARY KEY,   -- mis. 'yellow_shoulder' atau ArUco id '#14'
    cashier_id  TEXT NOT NULL,
    active_from DATE,
    active_to   DATE                -- null kalau masih dipakai; diisi kalau ganti marker/staff resign
);

-- Data POS existing (asumsi sudah ada, tinggal dibaca via replikasi/export, bukan ditulis oleh pipeline video)
-- transaksi(transaksi_id, store_id, counter_id, cashier_id, created_at, nominal_total, item_count, loyalty_id)
-- loyalty_id: null kalau konsumen tidak scan member — itu wajar & tidak masalah, cukup dianalisis sebagai sesi anonim (§5.4/§5.5).
-- Kalau ada, ini kunci backtrack identitas per §5.3 — tanpa perlu Re-ID/embedding sama sekali.

-- Join layer: hasil matching video x POS, ini yang jadi basis analisis performance kasir
CREATE TABLE track_transaction_match (
    track_id         TEXT REFERENCES track_event(track_id),
    transaksi_id     TEXT,                  -- null kalau track ini non-closing (interaksi tanpa transaksi mengikuti)
    cashier_id       TEXT,
    is_closing       BOOLEAN,
    match_confidence TEXT,                  -- 'exact_counter' | 'store_level_fallback' — transparansi soal seberapa presisi matching-nya
    matched_at       TIMESTAMP
);

-- Hand-off multi-staf per sesi (§17.3) — sebelumnya disebut di prosa tapi belum pernah didefinisikan di sini
-- Log SEMUA staf yang terdeteksi berinteraksi dengan 1 track_id, bukan cuma cashier_id di titik transaksi
CREATE TABLE track_staff_interaction (
    id                   SERIAL PRIMARY KEY,
    track_id             TEXT REFERENCES track_event(track_id),
    cashier_id           TEXT NOT NULL,     -- dari deteksi marker (§5.6), bisa lebih dari 1 baris per track_id
    interaction_seconds  INT,
    role_hint            TEXT               -- 'approach' | 'closing' | dst — opsional, buat dasar split kredit di §17.3
);

-- Presence staf per zona (Tier 2-B, §13-B) — sebelumnya cuma disebut di prosa (§14 Fase 2 poin 4), belum ada tabelnya
-- Diisi dari deteksi marker staf (§5.6) yang di-track terus-menerus, bukan cuma di counter kasir
CREATE TABLE staff_zone_presence (
    id            SERIAL PRIMARY KEY,
    cashier_id    TEXT NOT NULL,
    zone_id       TEXT REFERENCES zone_definition(zone_id),
    entered_at    TIMESTAMP NOT NULL,
    exited_at     TIMESTAMP,
    -- dari sini bisa dihitung: idle_time (durasi presence tanpa ada track_event konsumen tumpang tindih di zona sama),
    -- response_time (selisih customer entered_at zona vs staff entered_at zona berikutnya),
    -- coverage_gap (rentang waktu 1 zona tanpa baris staff_zone_presence sama sekali)
);

Tidak ada kolom identitas/embedding konsumen di skema ini — track_id untuk konsumen sengaja hanya berlaku dalam satu sesi kunjungan (tidak di-link ke kunjungan lain), karena tujuan analisisnya adalah "kenapa transaksi terjadi", bukan "siapa dia". Ini juga berarti nol data biometrik konsumen yang perlu disimpan atau di-retensi.

track_transaction_match diisi oleh batch job terpisah (jalan setelah Layer 1 & POS sync harian): untuk tiap track_event di suatu counter_id, cari transaksi dengan created_at di dalam [exited_at, exited_at + N menit] pada counter_id/cashier_id yang sama → kalau ketemu, is_closing = true dan simpan transaksi_id; kalau tidak, is_closing = false.

11. Skema Gamifikasi (Ditunda — untuk fase lanjutan)

Ditunda per arahan client. Disimpan di sini karena arsitektur data (§10) sudah mendukung tanpa perubahan besar kalau nanti mau dilanjutkan — tinggal tambah scoring layer di atas track_event + track_enrichment (dan sekarang juga bisa ambil sinyal dari track_transaction_match, mis. poin ekstra untuk kasir yang closing rate-nya tinggi).

Ada 2 opsi (belum perlu diputuskan sekarang):

Contoh Skema Poin (2 opsi, tinggal pilih kalau fase ini dilanjutkan)

Opsi A — Gamifikasi konsumen (butuh link ke member lewat loyalty_id — bukan Re-ID, tapi backtrack saat scan member/QR check-in, persis alur §5.3 & tabel member di §19.1):

Aksi Poin
Kunjungan baru ke toko +10
Dwell time > 3 menit +15
Viewing produk tertentu > 30 detik +10
Coba/pegang jam (interaction_type = pegang_coba) +25
Kunjungan ke-N dalam sebulan (loyalty streak) +5 × N (cap)

Opsi B — Gamifikasi staff/toko (funnel tanpa identitas konsumen):

Skor toko/shift = Σ (interaction_seconds / gaze_seconds) per track_id yang punya gaze_seconds > 0

→ makin tinggi rasio "lihat → pegang/interaksi", makin tinggi skor konversi staff. Bisa dipecah per SPG kalau ada kamera/zona yang bisa diasosiasikan ke staff yang sedang jaga (butuh jadwal shift sebagai input tambahan).

Update dari Riset Eksternal (§18) — Referensi Konkret untuk Opsi A

Riset Haidilao (§18) kasih 2 contoh skema tier nyata yang bisa jadi starting point kalau Opsi A dilanjutkan — jangan pakai nama-nama Haidilao asli (Black Sea, dsb — risiko trademark, lihat catatan legal di §6), tapi pola tier & threshold-nya bisa diadaptasi:

Sumber Nama Tier (contoh, jangan dipakai persis) Pola Threshold
gemini-2.md "Rasa Coins": Bronze (Warga) → Silver (Tetangga) → Gold (Keluarga) Berbasis spending tahunan (mis. Rp2jt → Rp5jt)
chatgpt-1.md "Ramah Circle": Teman → Sahabat → Istimewa → Prioritas Rolling 12 bulan, dari kunjungan + spend (bukan cuma nominal)

Prinsip desain tier yang divalidasi kedua riset (bukan cuma tebakan): pakai window rolling 12 bulan (bukan lifetime), threshold berbasis kombinasi spend + frekuensi kunjungan (bukan salah satu saja), dan manfaatkan efek "near-miss" — tampilkan ke konsumen berapa lagi jaraknya ke tier berikutnya supaya jadi dorongan balik.

Angka ekonomi sebagai acuan awal (dari chatgpt-1.md, skenario ilustratif — bukan angka pasti, tapi lebih baik dari menebak): biaya reward efektif disarankan tetap 0,8–1,5% dari sales member, dengan break-even uplift kunjungan sekitar 5–14% tergantung margin kontribusi toko. Kalau nanti Opsi A dieksekusi, angka ini jadi baseline untuk simulasi biaya sebelum diluncurkan — bukan dikira-kira dari nol.

Guardrail legal penting (lihat §6 update): kalau skema poin nanti melibatkan elemen keberuntungan apa pun (spin wheel, blind box), itu perlu izin UGB Kemensos. Rekomendasi: desain Opsi A selalu deterministik (aksi pasti → reward pasti), bukan random — supaya bisa diluncurkan tanpa proses perizinan tambahan.

⚠️ Versi optimal (rawan Permensos UGB, bukan UU PDP): literatur behavioral economics (§18, chatgpt-1.md §D.1) menunjukkan variable reward (hadiah acak, tidak pasti besarnya) menghasilkan engagement lebih tinggi daripada reward deterministik — inilah kenapa Haidilao pakai lucky-wheel & secret code dengan hadiah tak terduga. Tanpa batasan UGB, mekanik random ini akan lebih optimal secara psikologis. Tapi karena butuh izin Kemensos + hibah 10% + pajak 25% dari nilai hadiah, opsi ini sengaja di-skip di desain final — lihat §20.

12. Estimasi Biaya Kasar (per toko, per bulan — MVP)

Catatan: ini cuma biaya infrastruktur video (Layer 1+2). Kalau nanti gamifikasi konsumen (§11, §16-B) juga dieksekusi, ada biaya terpisah untuk program reward — lihat angka ekonomi (biaya reward % sales, break-even uplift) di §11 "Update dari Riset Eksternal" dan detail skenario lengkap di riset-eksternal/chatgpt-1.md §Analisis Risiko Finansial. Dua pos biaya ini independen — biaya video tidak naik walau program gamifikasi konsumen belum/tidak jalan.

13. Katalog Metrik Performance Toko dari Video (di luar kasir)

Ini kumpulan sinyal performance level toko yang bisa ditarik dari video, di luar korelasi kasir↔transaksi di §5. Dikelompokkan per tier kesulitan implementasi — mulai dari yang paling gampang dulu.

Tier 1 — Paling mudah, implementasi duluan (A + E)

Murni Layer 1 (CV tracking dasar: deteksi + hitung orang di suatu area/waktu). Tidak butuh marker staff, tidak butuh zone mapping rumit, tidak butuh VLM. Akurasinya juga paling tinggi karena cuma "hitung orang di area X pada waktu Y" — kasus penggunaan paling matang untuk CV klasik.

A. Traffic & Footfall - Jumlah pengunjung per jam/hari (hitung track_id unik yang masuk). - Pola jam ramai vs sepi → dasar atur jadwal staff. - Conversion rate toko secara umum = total transaksi ÷ total pengunjung (beda dari conversion per kasir di §5 — ini level toko). - Window shopping vs benar-benar masuk (kalau kamera juga cover area depan toko/etalase).

E. Antrian & Waktu Tunggu - Panjang antrian dari waktu ke waktu (hitung orang mengelompok di zona kasir). - Rata-rata waktu tunggu sebelum dilayani. - Jam-jam antrian overflow → indikasi butuh tambahan kasir di jam itu.

Tier 2 — Medium, implementasi setelah Tier 1 stabil (B + D)

Masih Layer 1, tapi butuh 2 hal tambahan yang menaikkan kompleksitas setup (bukan akurasi model-nya): (1) marker staff (§5.6) harus aktif & ter-track terus-menerus di seluruh toko, bukan cuma pas di counter kasir — perlu kalibrasi ulang cakupan kamera; (2) definisi zona toko (zone_id per area/rak/display) perlu dipetakan manual ke koordinat kamera, dan berubah tiap kali layout toko diubah. D juga butuh Layer 2 (VLM) untuk label produk, jadi mewarisi biaya & keterbatasan akurasi VLM dari §4.

B. Coverage & Responsiveness Staff (skema tabelnya: staff_zone_presence, §10) - Response time: berapa lama dari konsumen masuk suatu zona sampai staff mendekat/menyapa. - Staff idle time: staff diam tanpa interaksi konsumen berapa lama. - Rasio staff : konsumen per jam → deteksi jam understaffed. - Coverage gap: zona toko yang "kosong" dari staff dalam durasi tertentu.

D. Product & Zone Engagement (heatmap) - Heatmap zona toko: area mana paling banyak dilalui/dilihat vs paling sepi → evaluasi layout & penempatan produk. - Produk yang sering dipegang/dicoba vs yang sering dilihat tapi tidak dipegang — sinyal mismatch (mungkin harga tidak sesuai ekspektasi, atau butuh penjelasan staff lebih aktif). - Dwell time per zona → validasi apakah display tertentu benar-benar menarik perhatian atau cuma dilewati.

Tier 3 — Lebih sulit & kurang akurat, pertimbangkan matang-matang sebelum investasi

Ini yang secara teknis paling riskan — bukan berarti tidak bisa, tapi akurasinya lebih rendah dan/atau butuh effort setup yang gak sebanding dengan sinyal yang didapat. Ditaruh di sini supaya ekspektasinya realistis dari awal, bukan baru ketahuan pas sudah dibangun.

C. Kualitas Interaksi (bagian ekspresi/body language) - Rasio konsumen didekati vs diabaikan, dan durasi interaksi staff–konsumen — ini sebenarnya masih masuk akal (mewarisi akurasi Tier 2, cuma perluasan cakupan waktu dari §5). - Yang sulit: sinyal ekspresi/body language/nada (ramah vs jutek, dst). Kesulitannya: - Subjektif & tidak ada ground truth yang gampang — beda dari "closing/tidak" (ada di POS) atau "orang ada di zona X" (obyektif), "ramah" itu penilaian manusia yang bisa beda-beda, sulit divalidasi model-nya benar atau tidak. - Resolusi CCTV retail biasanya tidak cukup untuk baca ekspresi wajah dari jarak & sudut kamera keamanan standar — beda dari kasus video interview/webcam yang jarak dekat. - Aktivitas staff yang ambigu: staff yang "diam" di kamera bisa lagi idle, bisa juga lagi merapikan display atau ngecek stok — sulit dibedakan CV tanpa false positive tinggi. - Rekomendasi: skip dulu, tidak worth investasinya dibanding metrik durasi/frekuensi yang sudah robust.

F. Operasional & Kepatuhan - Kehadiran staff sesuai shift, jam buka/tutup aktual, zona wajib-ada-staff. - Kesulitannya bukan di CV-nya (marker detection sendiri robust, §5.6), tapi di operasional marker itu sendiri: marker bisa ketutup barang yang lagi dipegang staff, staff lupa/malas pakai marker, marker kotor/pudar seiring waktu, dan tiap rotasi staff/ganti seragam butuh update manual cashier_marker_map (§10) — kalau tidak rajin di-maintain, datanya jadi tidak reliable pelan-pelan tanpa ketahuan. - Juga butuh integrasi ke sistem jadwal/absensi yang terpisah (bukan cuma video) — ada dependency ke sistem lain yang mungkin belum ada.

G. Sinyal Anomali - Dwell time tinggi tanpa closing, atau lonjakan "masuk lalu keluar cepat" — secara data gampang dihitung (turunan dari Tier 1/2), tapi interpretasinya susah: - Butuh baseline historis yang cukup panjang dulu untuk tahu "normal"-nya seperti apa, sebelum bisa bilang sesuatu itu "anomali". - False positive tinggi: banyak sebab non-toko untuk "masuk-keluar cepat" (lupa dompet, salah toko, telepon masuk, dsb) yang video sendiri tidak bisa bedakan. - Anomali cuma kasih sinyal "ada sesuatu yang aneh di sini", bukan jawaban "kenapa" — tetap butuh follow-up manual/kualitatif dari manager toko untuk jadi actionable, bukan langsung insight otomatis.

Rekomendasi prioritas kalau mau mulai: A (traffic) + E (antrian) + §5 (kasir↔transaksi) dulu — murni Layer 1, paling murah, ROI paling cepat divalidasi. B dan D butuh perluasan sistem marker staff & Layer 2 VLM yang sudah dirancang, jadi bisa nyusul begitu MVP jalan. C bagian ekspresi/body language sebaiknya di-skip dulu.

14. Rencana Implementasi Konkret (Tier 1 → Tier 2)

Fase 1: Tier 1 (Traffic + Antrian) — MVP tercepat

  1. Pastikan posisi kamera cocok. Butuh sudut yang meng-cover pintu masuk (buat hitung traffic masuk/keluar) dan area depan kasir (buat hitung antrian). Kalau kamera existing belum pas posisinya, ini yang harus dibenahi duluan — sebelum ngoding apa pun.
  2. Setup deteksi + tracking. Pakai Ultralytics YOLOv8n/11n (person detection) + ByteTrack yang sudah built-in (model.track(source=..., tracker="bytetrack.yaml"), pip install ultralytics). Model kecil ini cukup jalan di CPU mini PC untuk 1–2 kamera, gak perlu GPU mahal di fase ini.
  3. Definisikan counting line & zona antrian. Ini konfigurasi manual satu kali per kamera: gambar garis virtual di depan pintu (hitung crossing = masuk/keluar), dan polygon area kasir (hitung berapa track ada di dalamnya tiap beberapa detik = panjang antrian). Ultralytics juga sudah punya modul siap pakai (solutions.ObjectCounter) supaya gak perlu nulis logic counting dari nol.
  4. Log ke DB sederhana. SQLite lokal per toko sudah cukup di tahap ini — belum perlu Postgres/Supabase. Simpan event crossing (timestamp, arah) dan snapshot panjang antrian per interval.
  5. Rekap harian. Skrip cron sederhana yang agregasi SQLite → jumlah pengunjung/jam, rata-rata & puncak panjang antrian/jam. Bisa keluarkan ke CSV/Google Sheet dulu — dashboard web belum prioritas di fase ini.
  6. Validasi akurasi sebelum dipercaya penuh. Bandingkan hasil sistem vs hitungan manual (staff hitung manual 1 jam di jam ramai & sepi). Kalau ada bias sistematis (mis. orang jalan cepat suka ke-miss), kalibrasi ulang posisi/sensitivitas counting line.

→ Realistis: 1–2 minggu untuk 1 toko kalau kamera sudah terpasang dengan baik — sebagian besar waktu habis di kalibrasi zona & testing di kondisi pencahayaan toko yang sebenarnya, bukan di coding model-nya.

Fase 2: Tier 2 (Coverage Staff + Zone Engagement) — setelah Fase 1 stabil

  1. Rollout marker staff (§5.6). Tentukan warna solid kontras per staff, siapkan patch/armband fisik. Uji di kondisi pencahayaan toko yang sebenarnya (siang vs lampu kuning toko malam bisa menggeser pembacaan warna) — kalibrasi range HSV khusus untuk kondisi toko itu, bukan pakai nilai default.
  2. Tambahkan deteksi marker ke pipeline tracking yang sudah jalan. Tiap bounding box hasil tracking di-cek juga apakah ada blob warna marker di dalamnya (HSV color mask) → kalau ada, track itu ditandai sebagai staff dengan cashier_id tertentu, bukan konsumen.
  3. Petakan zona toko. Butuh denah kasar toko, lalu tentukan koordinat kamera untuk tiap zona penting (rak/display jam per kategori). Ini kerja manual, dan perlu diulang kalau layout toko berubah.
  4. Hitung response time & idle time. Logic tambahan: tandai "approached" kalau jarak piksel antara track staff dan track konsumen di bawah threshold tertentu dalam suatu zona; response time = selisih waktu konsumen masuk zona vs momen "approached" pertama terjadi.
  5. Aktifkan Layer 2 untuk D (product engagement). Tiap kali has_hand_object_interaction = true di suatu zona, ambil snapshot → batch job VLM harian untuk label produk/interaksi, sesuai desain §4 & §10 (tabel track_enrichment).

→ Realistis: 3–4 minggu tambahan di atas Fase 1 — waktunya lebih banyak habis di kalibrasi marker & zone mapping daripada coding, karena ini yang paling sensitif terhadap kondisi fisik toko yang sebenarnya (pencahayaan, sudut kamera, layout).

Catatan tooling

15. Data yang Direkam (Data Dictionary Ringkas)

Rangkuman semua data amatan yang benar-benar tercatat di seluruh skema ini, dikelompokkan per sumber. Ini konsolidasi dari §10, §13, §14 — dikumpulkan di satu tempat biar gampang dicek.

Dari kamera — Layer 1 (CV tracking, per sesi kunjungan/track)

Dari kamera — Layer 2 (batch VLM, hasil enrichment dari snapshot)

Dari counting zone (Tier 1 — traffic & antrian, §14 Fase 1)

Dari zona toko (Tier 2 — coverage & engagement, §14 Fase 2)

Dari marker staff

Dari POS (dibaca saja, bukan ditulis oleh pipeline video)

Hasil join (yang menghubungkan video ↔ transaksi)

Yang sengaja tidak direkam

16. Ide Pengembangan Proses Bisnis & Gamifikasi Lanjutan

Dikelompokkan jadi 3: gamifikasi staff (bisa langsung dieksekusi karena pipeline data-nya sudah ada dari §5), gamifikasi konsumen (butuh layer baru: app/loyalty), dan perbaikan proses bisnis yang lebih luas (pakai data yang sama, tapi bukan gamifikasi).

A. Gamifikasi Staff — bisa dieksekusi begitu §5 jalan

B. Gamifikasi Konsumen — butuh layer baru (app/QR/loyalty), di luar scope video murni

Update dari riset eksternal (§18) — mekanik tambahan yang lebih konkret dari sekadar ide:

C. Perbaikan Proses Bisnis (pakai data yang sama, bukan gamifikasi)

Yang paling masuk akal dieksekusi duluan: bagian A (gamifikasi staff) dan sebagian C (perbaikan skema komisi + manajemen antrian) — karena keduanya langsung pakai data yang sudah dirancang di §5 dan §13, tidak butuh infrastruktur tambahan (app, QR, loyalty system) seperti bagian B.

17. Eksplorasi Mendalam: Gamifikasi Staff

17.1 Mekanik gamifikasi yang relevan

Bukan cuma satu leaderboard datar — kombinasi mekanik ini biasanya lebih sehat secara motivasi:

Mekanik Bentuk konkret Kenapa dipakai
Poin Akumulasi dari composite score (§17.2) Basis kuantitatif untuk semua mekanik lain
Level/Tier Bronze/Silver/Gold/Platinum berdasarkan rolling 30-hari Progres personal — staff bersaing dengan versi dirinya sendiri, bukan cuma dibanding orang lain
Badge/Achievement "Approach Master" (response time tercepat), "Consistency Award" (variance rendah), "Product Expert" (closing merata di banyak model, bukan cuma 1 SKU favorit) Mengapresiasi jenis kekuatan yang beda-beda, bukan cuma nominal penjualan terbesar
Quest/Challenge mingguan "Minggu ini: tingkatkan approach rate ke >80%" — target achievable, bukan kompetitif Cocok untuk staff yang levelnya belum tinggi, tetap punya cara menang tanpa harus juara umum
Leaderboard tim (toko) "Toko health score" (§16-A) Menyeimbangkan kompetisi individu dengan kerja sama — penting karena closing sering butuh lebih dari 1 staff (lihat §17.3)

17.2 Composite score, bukan satu metrik tunggal

Kalau cuma pakai 1 metrik (mis. closing rate saja), gampang di-game dan gampang bias. Gabungkan beberapa sinyal dari §5 & §13 dengan bobot, contoh kerangka (bobot tinggal disesuaikan ke prioritas bisnis):

Skor = w1 × conversion_rate_ternormalisasi
     + w2 × avg_basket_value_ternormalisasi
     + w3 × approach_rate (§13-B)
     + w4 × product_diversity (jumlah model berbeda yang berhasil closing, bukan cuma 1 SKU termudah)
     - penalti × non_response_rate (konsumen di zona tapi tidak pernah didekati)

"Ternormalisasi" penting: bandingkan staff hanya dengan staff lain di kondisi traffic/jam yang sebanding (lihat §5.5) — supaya staff shift sepi tidak otomatis kalah dari staff shift ramai.

17.3 Masalah atribusi: hand-off antar staff

Realita di toko: staf A menyapa & menjelaskan produk (approach), tapi transaksi diproses staf B di kasir (cashier_id dari POS beda dengan staf yang muncul di interaksi awal). Kalau atribusi cuma ambil cashier_id dari POS (default di §5.1), staf A yang kerja "closing"-nya justru tidak dapat kredit.

Solusi: log semua marker staff (§5.6) yang terdeteksi berinteraksi dalam satu track_id yang sama, bukan cuma cashier di titik transaksi — tabel track_staff_interaction (§10) menyimpan ini. Skema poin lalu bisa split kredit (mis. 70% ke yang proses transaksi, 30% ke yang approach/menjelaskan, pakai role_hint) — atau, lebih simpel di fase awal, biarkan dulu semua staf yang tercatat di sesi itu dapat kredit penuh (double count sengaja) sampai pola hand-off-nya cukup jelas untuk ditentukan bobot split yang wajar.

17.4 Risiko "gaming the metric" & mitigasi

Begitu staff tahu videonya dinilai, mereka akan menyesuaikan perilaku ke arah metrik — beberapa lubang yang perlu diantisipasi dari awal desain, bukan ditambal belakangan:

Update dari riset eksternal (§18): riset chatgpt-1.md (§ Analisis Risiko) mengonfirmasi pola gaming yang sama terjadi di industri lain lewat istilah "staff collusion" — karyawan memanipulasi sistem reward pelanggan untuk kepentingan sendiri (di F&B: staf scan struk pelanggan yang ditinggal ke akun sendiri/teman). Analog di toko jam: staf berpotensi memanipulasi data supaya closing yang sebenarnya bukan hasil kerjanya sendiri tercatat sebagai miliknya (relevan ke masalah hand-off di §17.3). Mitigasi konkret dari riset yang bisa diadopsi: velocity check (batasi berapa kali suatu pola "closing" bisa diklaim per staf per hari sebelum di-flag), dan pastikan atribusi closing selalu tervalidasi silang ke cashier_id asli di POS (§10) — bukan cuma laporan staf sendiri.

17.5 Desain reward: mulai non-finansial, phased trust-building

Karena ini employee monitoring (§5.7), lompat langsung ke "skor menentukan bonus" berisiko menimbulkan resistensi kalau staff belum percaya sistemnya akurat. Rollout bertahap:

  1. Fase observasi (1–2 bulan) — dashboard cuma dilihat manager/coach, staff belum tahu skornya secara formal. Tujuannya validasi: apakah skor memang masuk akal dibanding penilaian manual manager selama ini?
  2. Fase pengakuan non-finansial — buka akses dashboard ke staff sendiri (lihat progress harian/mingguan), mulai kasih badge & pengakuan publik (papan skor di ruang staff/app internal), belum diikat ke uang.
  3. Fase insentif — baru diikat ke bonus/insentif setelah staff terbiasa dan percaya sistemnya adil (ada juga jalur banding/appeal kalau staff merasa skornya tidak representatif, mis. hari itu banyak transaksi rumit).

Urutan ini juga jadi mitigasi risiko hukum ketenagakerjaan (§5.7) — ada jejak bahwa evaluasi diperkenalkan bertahap dan transparan, bukan tiba-tiba dipakai buat memotong bonus tanpa staff pernah tahu kriterianya.

⚠️ Versi optimal (rawan UU PDP + Ketenagakerjaan): tanpa batasan, monitoring paling optimal adalah biometrik staf real-time — deteksi ekspresi/mood (§13-C yang sebelumnya di-skip karena akurasi & privasi) dipakai untuk skor instan, langsung dipotong dari insentif tanpa fase trust-building 3 tahap di atas, dan tanpa jalur banding (staff_appeal_log, §19.2) sama sekali. Ini secara data lebih "real-time actionable", tapi biometrik karyawan adalah kategori data yang diproteksi ketat di UU PDP, dan penilaian sepihak tanpa transparansi berisiko pelanggaran UU Ketenagakerjaan soal keadilan evaluasi kerja — makanya desain final tetap pakai rollout bertahap di atas. Ringkasan lengkap di §20.

18. Riset Eksternal (Referensi Gamifikasi)

Catatan hasil riset dari luar (Gemini/ChatGPT deep research) soal framework/referensi gamifikasi yang relevan — ditempel di file terpisah biar §16–17 di atas tidak bercampur dengan kutipan mentah. Update §16/§17 kalau ada temuan yang perlu direvisi berdasarkan riset ini.

Temuan-temuan kunci dari ketiga riset ini sudah diintegrasikan ke §6 (kepatuhan UU PDP/UGB), §11 (contoh tier & angka ekonomi), §16-B (mekanik konsumen: WhatsApp-first, Queue/Table Mission), dan §17.4 (pola staff collusion & mitigasi).

19. Data Model Gamifikasi (Konsumen & Staf)

Ini pelengkap §10/§15 yang sebelumnya cuma mencakup data video+POS. Skema di bawah menurunkan semua ide di §11, §16, §17 jadi tabel konkret — siap dipakai sebagai basis implementasi, bukan cuma daftar ide.

19.1 Skema Konsumen (Membership, Poin, Misi, Redemption)

-- Data member — diisi saat registrasi WhatsApp-first (§16-B), bukan dari deteksi video
CREATE TABLE member (
    loyalty_id       TEXT PRIMARY KEY,       -- sama dengan loyalty_id yang dipakai backtrack di §5.3
    nama             TEXT,
    no_hp            TEXT NOT NULL,          -- data minimal sesuai prinsip data minimization UU PDP (§6)
    bulan_lahir      INT,                    -- cukup bulan, bukan tanggal lahir lengkap — kurangi data sensitif
    store_registrasi TEXT,
    current_tier     TEXT NOT NULL DEFAULT 'Bronze',
    registered_at    TIMESTAMP NOT NULL,
    deleted_at       TIMESTAMP               -- soft delete untuk penuhi hak "right to erasure" UU PDP
);

-- Log consent — wajib untuk pembuktian kepatuhan UU PDP (§6), terpisah dari data member itu sendiri
CREATE TABLE consent_log (
    id             SERIAL PRIMARY KEY,
    loyalty_id     TEXT REFERENCES member(loyalty_id),
    consent_type   TEXT NOT NULL,   -- 'data_processing' | 'marketing_whatsapp'
    channel        TEXT,            -- 'whatsapp_flow' | 'qr_form', dst
    granted_at     TIMESTAMP,
    revoked_at     TIMESTAMP        -- null kalau masih berlaku
);

-- Ledger poin — SATU-SATUNYA sumber kebenaran untuk saldo poin (dihitung dari SUM(points_delta), bukan disimpan sebagai kolom terpisah yang gampang drift)
CREATE TABLE points_ledger (
    id           SERIAL PRIMARY KEY,
    loyalty_id   TEXT REFERENCES member(loyalty_id),
    event_type   TEXT NOT NULL,     -- 'earn_transaksi' | 'earn_mission' | 'redeem' | 'expire' | 'referral_bonus' | 'birthday_bonus'
    points_delta INT NOT NULL,      -- positif untuk earn, negatif untuk redeem/expire
    source_ref   TEXT,              -- transaksi_id, mission_id, atau referral_id terkait — untuk audit trail
    created_at   TIMESTAMP NOT NULL
);

-- Histori tier — perlu disimpan terpisah dari member.current_tier supaya bisa dianalisis kapan orang naik/turun tier
CREATE TABLE tier_history (
    id             SERIAL PRIMARY KEY,
    loyalty_id     TEXT REFERENCES member(loyalty_id),
    tier           TEXT NOT NULL,
    effective_from TIMESTAMP NOT NULL,
    effective_to   TIMESTAMP,       -- null = masih berlaku
    reason         TEXT             -- 'rolling_recompute' | 'manual_adjustment'
);

-- Definisi misi (Queue Quest / Table Mission / Social Challenge, §16-B) — dikelola manual oleh tim marketing, bukan hasil sistem
CREATE TABLE mission_definition (
    mission_id     TEXT PRIMARY KEY,
    mission_type   TEXT NOT NULL,   -- 'queue_quest' | 'table_mission' | 'social_challenge' | 'referral'
    description    TEXT,
    reward_points  INT NOT NULL,
    is_deterministic BOOLEAN NOT NULL DEFAULT TRUE,  -- WAJIB true kecuali sudah lolos review legal UGB (§6) — flag ini sengaja eksplisit supaya tidak kelewat saat desain misi baru
    active_from    DATE,
    active_to      DATE
);

-- Penyelesaian misi oleh member
CREATE TABLE mission_completion (
    id           SERIAL PRIMARY KEY,
    loyalty_id   TEXT REFERENCES member(loyalty_id),
    mission_id   TEXT REFERENCES mission_definition(mission_id),
    track_id     TEXT,             -- opsional: link ke track_event (§10) kalau misi terkait sesi kunjungan tertentu, mis. Table Mission
    completed_at TIMESTAMP NOT NULL,
    reward_points_ledger_id INT REFERENCES points_ledger(id)  -- link ke baris ledger yang dibuat akibat penyelesaian ini
);

-- Redemption reward (barang/voucher ditukar poin)
CREATE TABLE redemption_log (
    id            SERIAL PRIMARY KEY,
    loyalty_id    TEXT REFERENCES member(loyalty_id),
    reward_item   TEXT NOT NULL,
    points_spent  INT NOT NULL,
    store_id      TEXT,
    approved_by   TEXT,            -- cashier_id yang validasi di kasir (§10)
    redeemed_at   TIMESTAMP NOT NULL
);

-- Referral (§16-B) — 1 baris per pasangan referrer-referee, status berubah seiring waktu
CREATE TABLE referral_log (
    id                    SERIAL PRIMARY KEY,
    referrer_loyalty_id   TEXT REFERENCES member(loyalty_id),
    referee_loyalty_id    TEXT REFERENCES member(loyalty_id),
    referee_first_transaksi_id TEXT,   -- diisi begitu referee transaksi pertama kali (syarat reward cair, §18 chatgpt-1.md)
    status                TEXT NOT NULL DEFAULT 'pending',  -- 'pending' | 'completed' | 'rejected'
    reward_granted_at     TIMESTAMP
);

-- Broadcast ulang tahun (§16-B) — log terpisah dari consent supaya bisa diaudit terkirim/diklaim atau tidak
CREATE TABLE birthday_broadcast_log (
    id              SERIAL PRIMARY KEY,
    loyalty_id      TEXT REFERENCES member(loyalty_id),
    broadcast_month DATE NOT NULL,
    sent_at         TIMESTAMP,
    claimed_at      TIMESTAMP       -- null = belum diklaim
);

-- Sinyal "minat sekunder" — zona yang di-dwell lama tapi BUKAN zona produk yang akhirnya dibeli (contoh Budi, §19.5)
-- Diisi oleh batch job harian: untuk tiap track_id yang closing (is_closing=true di track_transaction_match),
-- ambil zone_dwell_segment dengan dwell_seconds di atas threshold, KECUALI zona yang product_category-nya cocok dengan item yang dibeli
CREATE TABLE secondary_interest_signal (
    id                SERIAL PRIMARY KEY,
    loyalty_id        TEXT REFERENCES member(loyalty_id),   -- diisi lewat backtrack §5.3 — makanya sinyal ini cuma ada untuk sesi yang closing + scan member
    track_id          TEXT REFERENCES track_event(track_id),
    zone_id           TEXT REFERENCES zone_definition(zone_id),  -- zona minat sekunder, mis. 'D'
    purchased_zone_id TEXT,   -- zona yang produknya benar-benar dibeli, mis. 'C' — buat konteks/audit
    dwell_seconds     INT,
    detected_at       TIMESTAMP NOT NULL
);

-- Broadcast penawaran personal hasil dari secondary_interest_signal (atau trigger lain di masa depan)
-- Terpisah dari birthday_broadcast_log karena trigger & timing-nya beda, tapi pola tabelnya sama
CREATE TABLE personalized_offer_log (
    id           SERIAL PRIMARY KEY,
    loyalty_id   TEXT REFERENCES member(loyalty_id),
    trigger_type TEXT NOT NULL,   -- 'secondary_interest' | 'birthday' | 'reactivation_90d', dst — bisa diperluas
    trigger_ref  INT,             -- id ke secondary_interest_signal.id (atau tabel trigger lain sesuai trigger_type)
    offer_detail TEXT,            -- mis. "diskon 10% zona D"
    sent_at      TIMESTAMP,
    claimed_at   TIMESTAMP        -- null = belum diklaim
);

19.2 Skema Staf (Skor, Badge, Level, Coaching, Appeal)

-- Snapshot skor komposit harian per kasir — historikal, bukan cuma angka "saat ini" (§17.2)
CREATE TABLE staff_score_daily (
    id                SERIAL PRIMARY KEY,
    cashier_id        TEXT NOT NULL,
    score_date        DATE NOT NULL,
    conversion_rate   NUMERIC,      -- ternormalisasi terhadap traffic sebanding (§17.2)
    avg_basket_value  NUMERIC,
    approach_rate     NUMERIC,      -- dari §13-B
    product_diversity INT,          -- jumlah model berbeda yang closing (§17.2)
    composite_score   NUMERIC NOT NULL,
    computed_at       TIMESTAMP NOT NULL,
    UNIQUE (cashier_id, score_date)
);

-- Badge yang didapat staf (§17.1) — log historis, bukan cuma status boolean
CREATE TABLE staff_badge_earned (
    id           SERIAL PRIMARY KEY,
    cashier_id   TEXT NOT NULL,
    badge_code   TEXT NOT NULL,     -- 'approach_master' | 'consistency_award' | 'product_expert', dst
    earned_at    TIMESTAMP NOT NULL,
    evidence_ref TEXT               -- referensi ke staff_score_daily.id atau periode yang jadi dasar badge ini, untuk audit
);

-- Histori level/tier staf (Bronze/Silver/Gold/Platinum, §17.1)
CREATE TABLE staff_level_history (
    id             SERIAL PRIMARY KEY,
    cashier_id     TEXT NOT NULL,
    level          TEXT NOT NULL,
    effective_from DATE NOT NULL,
    effective_to   DATE
);

-- Kasus coaching otomatis (§16-A: sesi non-closing durasi tinggi yang di-flag buat dibahas)
CREATE TABLE staff_coaching_case (
    id             SERIAL PRIMARY KEY,
    track_id       TEXT,            -- link ke track_event (§10) yang men-trigger kasus ini
    cashier_id     TEXT NOT NULL,
    flagged_reason TEXT,            -- 'high_duration_non_closing' | 'low_approach_rate', dst
    status         TEXT NOT NULL DEFAULT 'open',  -- 'open' | 'reviewed' | 'resolved'
    reviewed_by    TEXT,
    reviewed_at    TIMESTAMP,
    notes          TEXT
);

-- Jalur banding/appeal (§17.5) — staf bisa keberatan atas skor tertentu, ini tempat pencatatannya
CREATE TABLE staff_appeal_log (
    id                SERIAL PRIMARY KEY,
    cashier_id        TEXT NOT NULL,
    related_score_date DATE,        -- merujuk ke staff_score_daily
    reason            TEXT,
    submitted_at      TIMESTAMP NOT NULL,
    decision          TEXT,         -- 'upheld' | 'adjusted' | 'rejected'
    decided_by        TEXT,
    decided_at        TIMESTAMP
);

19.3 Skema Bersama: Anti-Fraud

-- Flag anomali — dipakai untuk member (redemption/referral abuse) MAUPUN staf (gaming metrik, §17.4)
CREATE TABLE anomaly_flag (
    id           SERIAL PRIMARY KEY,
    entity_type  TEXT NOT NULL,     -- 'member' | 'staff'
    entity_id    TEXT NOT NULL,     -- loyalty_id atau cashier_id
    flag_type    TEXT NOT NULL,     -- 'velocity_check' | 'duplicate_claim' | 'referral_self_dealing' | 'staff_collusion_suspect'
    detected_at  TIMESTAMP NOT NULL,
    details      TEXT,
    status       TEXT NOT NULL DEFAULT 'open'  -- 'open' | 'dismissed' | 'confirmed'
);

19.4 Prioritas Implementasi: Tabel Mana untuk MVP vs Advanced

Selaras dengan pentahapan MVP 30-hari vs advanced 6-12 bulan di riset (§18) dan rollout bertahap gamifikasi staf (§17.5) — tidak semua tabel di atas perlu dibangun sekaligus:

Fase Tabel yang dibutuhkan Catatan
MVP konsumen (30 hari) member, consent_log, points_ledger, redemption_log Cukup untuk loop inti: daftar → dapat poin → tukar reward. Bisa jalan di Google Sheet dulu sebelum DB sungguhan (§18 chatgpt-1.md)
Advanced konsumen tier_history, mission_definition, mission_completion, referral_log, birthday_broadcast_log Ditambah setelah loop inti tervalidasi — sesuai prinsip "jangan bangun kompleksitas sebelum validasi" (§18)
Fase observasi staf (§17.5 fase 1) staff_score_daily saja Manager lihat dulu, staf belum tahu — belum butuh badge/level/appeal
Fase pengakuan & insentif staf (§17.5 fase 2-3) staff_badge_earned, staff_level_history, staff_coaching_case, staff_appeal_log Baru dibangun begitu staf mulai lihat skornya sendiri
Kapan pun ada reward/insentif nyata anomaly_flag Jangan ditunda — begitu ada nilai (poin/uang) yang bisa disalahgunakan, anti-fraud harus sudah ada, bukan ditambah belakangan setelah insiden
Butuh Tier 2 penuh (§13/§14 Fase 2) lebih dulu zone_definition, zone_dwell_segment, staff_zone_presence, secondary_interest_signal, personalized_offer_log Semua butuh pemetaan zona toko dulu (§14 Fase 2) — jangan dibangun kalau Tier 1 (traffic/antrean) saja belum stabil
Begitu ada >1 staf per sesi (hand-off, §17.3) track_staff_interaction Bisa ditunda sampai pola hand-off cukup sering terjadi untuk worth di-track — di awal, atribusi 1 staf per sesi (cashier_id dari POS) sudah cukup

19.5 Contoh End-to-End: Kasus "Budi"

Skenario konkret buat validasi seluruh alur — tiap angka di skenario ini datang dari tabel spesifik:

Konsumen A datang, berkeliling 30 menit total (20 menit lihat-lihat produk, 10 menit ngobrol dengan staf). Dia menyusuri zona A (3 menit), B (4 menit), C (5 menit), D (10 menit) — tapi akhirnya beli barang dari zona C. Karena D dapat perhatian paling lama tapi tidak dibeli, sistem kirim penawaran diskon khusus zona D lewat WhatsApp beberapa hari kemudian. Saat transaksi, dia scan member — namanya jadi "Budi".

Begini pemetaannya ke skema yang sudah dirancang:

  1. Selama di toko — Layer 1 (§10) bikin 1 baris track_event (track_id sementara, dwell_seconds=1800 detik/30 menit, gaze_seconds≈1200 detik/20 menit "lihat-lihat"). track_enrichment dari Layer 2 catat interaction_seconds≈600 detik (10 menit ngobrol staf) dan interaction_type.
  2. Per zonazone_dwell_segment dapat 4 baris untuk track_id yang sama: (A, 180 detik), (B, 240 detik), (C, 300 detik), (D, 600 detik) — total 1320 detik ≈ 22 menit (selisih wajar dari 30 menit total karena sebagian waktu di jalur antar-zona, bukan di dalam zona manapun).
  3. Transaksi terjadi — POS catat transaksi dengan counter_id & waktu tertentu, plus loyalty_id karena dia scan member. track_transaction_match (§10) otomatis link track_idtransaksi_id lewat window waktu+counter (§5.1) → is_closing = true.
  4. Relabeling identitas — persis alur §5.3: loyalty_id di transaksi di-join ke member.loyalty_id → track_id anonim tadi sekarang "dipetakan" ke Budi. Seluruh histori sesi (poin 1-2 di atas) otomatis terhubung ke member Budi lewat 1 join, tanpa pernah butuh face recognition.
  5. Deteksi minat sekunder — batch job harian scan zone_dwell_segment untuk track yang closing: zona C (yang dibeli) dikecualikan (via zone_definition.product_category dicocokkan ke item di transaksi), sisanya diurutkan by dwell_seconds — zona D (600 detik) di atas threshold → masuk secondary_interest_signal (loyalty_id=Budi, zone_id='D', purchased_zone_id='C').
  6. Push notification — job terpisah baca secondary_interest_signal yang belum ada offer, generate entry di personalized_offer_log (trigger_type='secondary_interest', offer_detail="diskon zona D"), kirim via WhatsApp broadcast (§16-B), catat sent_at. Kalau Budi datang lagi & redeem, claimed_at terisi.

Kesimpulan validasi: skenario ini match penuh dengan arsitektur yang sudah dirancang — cuma butuh 2 tabel tambahan (zone_dwell_segment, secondary_interest_signal + personalized_offer_log) yang sekarang sudah masuk ke §19.1/§10. Tidak ada satu pun langkah yang butuh Re-ID biometrik atau data di luar yang sudah dirancang di §5.3 dan §14 Fase 2.

20. Ringkasan Opsi "Optimal Tapi Rawan" (Skip dari Desain Final)

Ini kompilasi semua tag ⚠️ yang tersebar di dokumen — versi paling optimal secara data/akurasi/engagement kalau seandainya batasan hukum tidak ada, dan kenapa masing-masing sengaja tidak dipilih di desain final. Disimpan di satu tempat supaya gampang direview ulang kalau suatu saat kondisinya berubah (mis. sudah dapat consent eksplisit yang membuat sebagian opsi ini jadi legal).

Area Versi Compliant (dipakai) Versi Optimal (di-skip) Kenapa Lebih Optimal Regulasi yang Dilanggar Hambatan Non-Legal (tetap jadi alasan skip meski regulasi diabaikan)
Identifikasi konsumen (§1) track_id sekali pakai per sesi (tag A/B/C/D dari CV klasik, di-relabel ke identitas transaksi/member saat bayar, §5.3) Re-ID biometrik persisten (face/body embedding permanen) Frekuensi kunjungan untuk semua pengunjung, bukan cuma yang transaksi+scan loyalty UU PDP — pemrosesan data biometrik tanpa consent Ini alasan utama, bukan cuma UU PDP: butuh kamera resolusi tinggi buat wajah jelas, model embedding jauh lebih berat dari CV tracking biasa, storage database embedding yang terus tumbuh, dan matching yang makin lambat/error-prone seiring database membesar. Skema compliant justru lebih murah, bukan cuma "lebih aman" — win-win, bukan trade-off
Retensi data (§6) Snapshot retensi pendek (7–30 hari), video di-purge Video mentah disimpan tanpa batas waktu Bisa diproses ulang pakai model masa depan yang lebih akurat; retrospective analysis UU PDP — prinsip storage limitation Storage jangka panjang untuk video mentah (bukan cuma snapshot) mahal secara linear dengan jumlah kamera & waktu — retensi pendek juga menghemat biaya storage, bukan cuma soal hukum
Enrollment member (§16-B) Opt-in eksplisit (scan QR/consent form) Auto-enroll semua pengunjung tanpa consent Basis data lebih besar dari hari pertama, tidak bergantung willingness konsumen UU PDP — lawful basis & consent Butuh sumber identitas (nama/no HP) dari suatu tempat — tanpa consent, satu-satunya cara "auto-enroll" adalah lewat Re-ID biometrik di atas, jadi mewarisi masalah cost yang sama
Profil konsumen (§6, §19.1) Data minimal: nama, no HP, bulan lahir Enrichment penuh via VLM: estimasi umur, gender, kelas ekonomi dari pakaian/aksesoris Personalisasi marketing jauh lebih tajam (ala AI personalization Haidilao, uplift 12–18%, §18) UU PDP — prinsip data minimization Tiap sesi butuh panggilan VLM tambahan (Layer 2, §4) khusus untuk inferensi demografis — menambah biaya API per kunjungan tanpa manfaat langsung ke tujuan utama (closing rate), bukan cuma soal privasi
Skema reward (§11, §16-B) Selalu deterministik (aksi pasti → reward pasti) Variable reward acak (lucky wheel, blind box, secret code ala Haidilao) Terbukti secara behavioral economics lebih tinggi engagement-nya daripada reward pasti (§18) Permensos No. 3/2024 (UGB) — butuh izin Kemensos + hibah 10% + pajak 25% dari nilai hadiah Minim — ini murni soal legal, bukan soal cost/teknis (beda dari baris-baris lain di tabel ini)
Monitoring staf (§17.5) Rollout 3 fase (observasi → non-finansial → insentif), skor staff_score_daily batch harian, ada staff_appeal_log Biometrik staf real-time (deteksi ekspresi/mood) → potong insentif instan tanpa banding Feedback "actionable" seketika, tanpa jeda trust-building UU PDP (data biometrik karyawan) + UU Ketenagakerjaan (keadilan evaluasi kerja) Deteksi ekspresi/mood real-time butuh inferensi VLM terus-menerus (bukan batch), bertentangan dengan prinsip non-realtime hemat biaya di §2 — juga akurasinya sendiri masih diragukan (§13-C)

Poin paling penting dari tabel ini: untuk 4 dari 6 baris, alasan skip bukan cuma kepatuhan hukum — arsitektur compliant kebetulan juga yang paling murah/efisien secara teknis. Cuma baris "skema reward" yang murni trade-off legal (variable reward secara teknis sama mudahnya dibangun, cuma butuh izin). Jadi kalau suatu saat UU PDP berubah atau sudah dapat consent penuh, sebagian besar opsi "optimal" di kolom ketiga tetap tidak worth dikejar karena cost-nya, bukan cuma karena legal — kecuali kasus reward random yang murni soal izin administratif.

Catatan jujur soal risiko yang tetap ada di luar UU PDP: menghapus batasan UU PDP secara hipotetis tidak menghapus semua risiko. Beberapa yang tetap relevan: - Platform ToS — WhatsApp Business API (dipakai di §16-B) melarang penggunaan data untuk profiling/tracking di luar konteks yang diizinkan; pelanggaran bisa berarti akun bisnis diblokir Meta, terlepas dari status hukum lokal. - Regulasi lain yang tidak hilang — Permensos UGB dan UU Ketenagakerjaan berdiri sendiri dari UU PDP; menghilangkan UU PDP tidak menghilangkan kewajiban di dua area itu. - Reputational risk — konsumen/staf yang sadar dipantau biometrik tanpa consent bisa menyebabkan backlash publik (viral di media sosial) yang dampaknya sering lebih mahal daripada denda administratif. - Risiko regulasi masa depan — UU PDP masih relatif baru (2022) dan penegakannya terus berkembang; desain yang "aman" hari ini karena celah penegakan belum berarti aman permanen.

Karena itu, tabel di atas dimaksudkan sebagai referensi trade-off, bukan rekomendasi implementasi — desain final di seluruh dokumen ini tetap versi compliant di kolom kedua.

21. Sinyal Amatan Lanjutan untuk Upselling & Analisis Konsumen

Begitu zone_dwell_segment (§10) ada, banyak sinyal baru terbuka di luar "minat sekunder" (§19.5). Ini kumpulannya, dikelompokkan by kebutuhan tambahan skema.

21.1 Cukup pakai skema yang sudah ada (tinggal query beda)

21.2 Butuh tambahan kecil ke skema

Prioritas kalau mau mulai: §21.1 dulu (query ulang dari data yang sudah ada, tanpa perubahan skema) — terutama path-to-purchase dan "almost bought", karena langsung actionable untuk campaign follow-up tanpa investasi tambahan. §21.2 nyusul begitu ada kapasitas untuk perluasan kecil skema, dengan voluntary check-in sebagai yang paling bernilai karena memperluas cakupan dari "cuma yang closing" ke "semua yang mau opt-in", bukan cuma nice-to-have.

22. Dari Data ke Aksi: Sintesis Output per Pemangku Kepentingan

Seluruh dokumen ini menghasilkan banyak tabel & sinyal — pertanyaan penutupnya: siapa yang benar-benar memakai apa, untuk keputusan apa? Dikelompokkan per audiens supaya jelas siapa dapat output apa, bukan cuma "data ada" tanpa pemilik.

22.1 Store Manager (harian/mingguan)

22.2 Tim Marketing/CRM

22.3 Manajemen SDM / Area Manager

22.4 Owner/Eksekutif

22.5 Tim Produk/Purchasing

22.6 Mana yang Otomatis, Mana yang Tetap Butuh Manusia

Penting supaya ekspektasi realistis — tidak semua output dari §22.1–22.5 bisa berjalan sendiri:

Otomatis penuh (sistem jalan sendiri) Butuh keputusan manusia
Trigger personalized_offer_log dari sinyal minat sekunder Desain isi/nominal penawaran, kapan campaign dihentikan
Flag staff_coaching_case dari pola non-closing Isi pembicaraan coaching, keputusan promosi/teguran
Skor komposit harian staf (staff_score_daily) Keputusan insentif/bonus final, terutama kalau ada banding (§19.2 staff_appeal_log)
Alert antrean overflow & traffic pattern Keputusan jadwal staf final (tetap mempertimbangkan cuti, ketersediaan, dll di luar data)
Heatmap & path-to-purchase report Keputusan reposisi produk aktual (mempertimbangkan biaya pindah display, estetika toko, dll)
Toko health score & KPI dashboard Keputusan strategis (ekspansi, tutup cabang, ganti principal) — data jadi salah satu input, bukan satu-satunya

Prinsip umum: sistem ini dirancang untuk mempersempit & memprioritaskan pilihan manusia (mana yang perlu diperhatikan duluan), bukan menggantikan keputusan manusia sepenuhnya — konsisten dengan catatan korelasi≠sebab-akibat di §5.5 dan kebutuhan jalur banding di §17.5.

23. Program Loyalitas Fisik: Baterai Gratis & Trade-In Refurbish

Dua ide bisnis konkret (di luar video analytics/gamifikasi digital) yang justru menyambung rapi ke sistem yang sudah dirancang — dijadikan section terpisah karena ini perluasan model bisnis, bukan cuma fitur teknis.

23.1 Baterai Gratis Seumur Hidup — Touchpoint Loyalitas, Siklus 6 Bulan

Mekanik: konsumen yang beli jam dapat servis ganti baterai gratis selamanya, dengan notifikasi proaktif tiap 6 bulan — bukan menunggu baterai benar-benar habis (baterai quartz secara teknis bisa tahan 1–2 tahun), tapi dijadwalkan sebagai "cek rutin gratis" tiap 6 bulan. Biaya ke perusahaan nyaris nol (baterai belum tentu diganti tiap kunjungan kalau memang belum habis — cek rutin bisa jadi cuma servis kecil/kalibrasi), tapi nilai yang dirasakan konsumen tinggi dan frekuensi kontaknya jauh lebih terjadwal.

Kenapa siklus 6 bulan (bukan nunggu baterai habis) adalah keputusan tepat: - 2x kontak wajib per tahun — dua kali lebih sering dari asumsi siklus baterai alami (1–2 tahun). Ini yang menjadikan servis baterai jantung utama seluruh sistem loyalitas (§25), bukan cuma perk pelengkap. - Titik kontak untuk info rilisan baru — staf dapat 2 kesempatan per tahun per member untuk mengangkat topik koleksi terbaru, tanpa terasa "jualan paksa" karena bungkusnya "cek rutin gratis". - Sumber data tambahan untuk sistem tracking — tiap kunjungan adalah momen loyalty_id yang pasti diketahui (§1) — 2x lebih sering dari asumsi awal, memperkaya sinyal reinforcement minat (§21.2) jauh lebih cepat. - Basis mekanik streak — karena jadwalnya tetap (tiap 6 bulan), ini bisa digamifikasi jadi "streak" konsistensi (§25.2) — member yang rutin datang tepat waktu dapat bonus, mirip pola check-in harian di app lain tapi disesuaikan ke kadensi 6 bulanan yang realistis untuk jam tangan.

23.2 Program Trade-In & Refurbish

Mekanik: konsumen tukar jam lama dengan jam baru + bayar selisih (xxx). Jam lama dinilai internal (Class A/B/C atau reject), direfurbish (memungkinkan karena perusahaan adalah produsennya sendiri — akses ke spare part asli & standar QC), lalu dijual kembali sebagai unit refurbished tidak bisa ditukar lagi — sekaligus positioning ramah lingkungan (circular economy).

Ini pola yang sudah terbukti di industri lain (trade-in program Apple, Certified Pre-Owned Rolex) — masuk akal justru karena perusahaan adalah produsen sendiri, bukan reseller yang harus menebak kualitas barang bekas.

23.3 Matematika Margin: Kenapa Diskon 50% Gagal, dan Diskon 20% (Bertingkat by Grade) Jauh Lebih Sehat

Update setelah dicek dengan margin riil (~3x, artinya COGS ≈ 33% dari harga jual — margin tinggi khas retail jam tangan): diskon 50% flat tidak worth it. Bukan cuma soal margin persentase tergerus, tapi masalah yang lebih dalam: setelah dikurangi biaya refurbish, ruang yang tersisa untuk trade-in credit ke konsumen jadi terlalu kecil — program jadi tidak menarik dari sisi konsumen yang mau nukar, bukan cuma dari sisi margin perusahaan.

Contoh konkret (harga jam baru Rp10.000.000, margin 3x → COGS ≈ Rp3.330.000):

Skenario diskon Harga jual refurbish Target COGS refurbish (margin% dijaga) Biaya refurbish (servis+baterai+QC, ~Rp700rb) Sisa ruang untuk trade-in credit Kesimpulan
50% off Rp5.000.000 Rp1.670.000 Rp700.000 ~Rp970.000 Terlalu kecil untuk jam bernilai jutaan — konsumen merasa tidak dihargai, program sepi peminat
20% off Rp8.000.000 Rp2.670.000 Rp700.000 ~Rp1.970.000 Hampir 2x lipat ruang trade-in credit dibanding 50% off — margin tetap terlindungi dan trade-in credit lebih realistis

Insight kunci: besar diskon ke konsumen akhir secara langsung menentukan seberapa besar trade-in credit yang bisa ditawarkan ke konsumen yang nukar — diskon besar bukan cuma "biaya buat perusahaan", tapi juga "mencekik" budget trade-in credit, jadi merugikan di dua sisi sekaligus.

Rekomendasi: diskon bertingkat sesuai grade (§23.2), bukan flat 50%:

Grade Diskon dari harga baru Alasan
A (nyaris mulus, minim refurbish) 15–20% Biaya refurbish rendah, kondisi mendekati baru — diskon kecil tetap terasa layak buat konsumen
B (ada tanda pakai, perlu servis sedang) 25–30% Biaya refurbish sedang, diskon lebih besar untuk kompensasi kondisi
C (banyak tanda pakai, butuh penggantian part) 35–40%, atau jangan dijual sebagai refurbish sale Kalau target COGS di grade ini sudah tidak realistis, lebih baik dikanibal jadi spare part daripada dipaksa dijual rugi

Formula operasional per grade: trade_in_credit_diberikan + refurb_cost_estimate ≤ target_COGS_refurbish(grade) — pagar ini yang dipakai staf saat menilai grade & menentukan nilai tukar tambah di trade_in_appraisal (§23.4). refurb_inventory.listed_price (§23.4) jadi bervariasi per grade, bukan flat 50%.

23.4 Data Model Tambahan

-- Perluasan transaksi (POS existing, §10) — tambah kolom tipe transaksi
-- transaksi.transaction_type: 'new_sale' | 'battery_service' | 'trade_in' | 'refurb_sale'

-- Penilaian trade-in — 1 baris tiap kali konsumen bawa jam lama untuk ditukar
CREATE TABLE trade_in_appraisal (
    id                    SERIAL PRIMARY KEY,
    loyalty_id            TEXT REFERENCES member(loyalty_id),
    transaksi_id          TEXT,              -- transaksi trade-in terkait (transaction_type='trade_in')
    watch_model_traded    TEXT,
    grade                 TEXT,              -- 'A' | 'B' | 'C' | 'reject'
    trade_in_credit       NUMERIC,           -- nilai kredit yang diberikan ke konsumen
    refurb_cost_estimate  NUMERIC,
    appraised_by          TEXT,              -- staff yang menilai (cashier_id, §10)
    appraised_at          TIMESTAMP NOT NULL,
    photos_ref            TEXT               -- foto before/after — dasar audit konsistensi grading antar staf/toko
);

-- Inventori unit refurbish — hasil dari trade_in_appraisal yang lolos grade (bukan reject)
CREATE TABLE refurb_inventory (
    refurb_unit_id       TEXT PRIMARY KEY,
    source_appraisal_id  INT REFERENCES trade_in_appraisal(id),
    grade                TEXT,
    refurb_completed_at  TIMESTAMP,
    listed_price         NUMERIC,           -- bervariasi per grade (§23.3): ~80-85% (A) / ~70-75% (B) / ~60-65% (C) dari harga baru model setara
    target_cogs          NUMERIC,           -- dari formula §23.3 (Skenario B)
    sold_transaksi_id    TEXT,              -- null kalau belum terjual (transaction_type='refurb_sale')
    non_exchangeable     BOOLEAN NOT NULL DEFAULT TRUE   -- eksplisit: unit ini tidak bisa di-trade-in lagi
);

-- Log servis baterai — touchpoint loyalitas, BUKAN transaksi penjualan (§23.1)
CREATE TABLE battery_service_log (
    id                     SERIAL PRIMARY KEY,
    loyalty_id             TEXT REFERENCES member(loyalty_id),
    store_id               TEXT,
    watch_model            TEXT,
    staff_id               TEXT,
    serviced_at            TIMESTAMP NOT NULL,
    next_due_at            TIMESTAMP NOT NULL,     -- serviced_at + 6 bulan, dihitung saat insert — basis reminder §24.6 & streak §25.2
    was_on_time            BOOLEAN,                -- true kalau serviced_at <= next_due_at kunjungan SEBELUMNYA + grace period (mis. 2 minggu) — basis streak
    notified_new_release   BOOLEAN DEFAULT FALSE   -- flag: apakah staf sempat sampaikan info koleksi baru di kunjungan ini
);

-- Reminder 6-bulanan — 1 baris dijadwalkan tiap kali battery_service_log baru diinsert (next_due_at dari baris itu)
CREATE TABLE battery_reminder_log (
    id            SERIAL PRIMARY KEY,
    loyalty_id    TEXT REFERENCES member(loyalty_id),
    due_at        TIMESTAMP NOT NULL,   -- = battery_service_log.next_due_at yang memicunya
    sent_at       TIMESTAMP,            -- kapan WhatsApp reminder benar-benar terkirim (biasanya H-7 dari due_at)
    responded_at  TIMESTAMP             -- diisi begitu member benar-benar datang servis lagi (link ke battery_service_log baru)
);

Catatan integrasi: zone_definition (§10) sudah cukup untuk area "refurbished corner" di toko — tinggal set product_category = 'refurbished', tanpa perlu kolom baru. Dwell time & minat di zona ini otomatis ter-track lewat zone_dwell_segment yang sudah ada, sehingga bisa dibandingkan minat ke refurbished vs ke produk baru — insight langsung tanpa infrastruktur tambahan.

23.5 Risiko & yang Perlu Dipikirkan Sebelum Eksekusi

Risiko Penjelasan Mitigasi
Kanibalisasi penjualan baru Kalau diskon refurbish terlalu besar dibanding kondisinya, sebagian calon pembeli baru bisa geser ke refurbish Sudah dimitigasi via diskon bertingkat by grade (§23.3, 15–40%, bukan flat 50%) — ditambah batasi refurbish ke model lama/discontinued, bukan rilisan terbaru
Konsistensi grading Class A/B/C bisa subjektif antar staf/toko kalau tidak ada standar jelas SOP grading tertulis + photos_ref wajib di trade_in_appraisal untuk audit
Siklus trade-in tak berujung Tanpa aturan jelas, unit refurbish bisa di-trade-in lagi, bikin akuntansi & kualitas makin sulit dikontrol non_exchangeable = true eksplisit di skema (§23.4), dikomunikasikan jelas ke konsumen saat beli refurbish
Klaim ramah lingkungan tanpa bukti "Ramah lingkungan" sebagai marketing tanpa angka konkret berisiko dianggap greenwashing Laporkan angka nyata (mis. "X unit diselamatkan dari limbah tahun ini") dari data refurb_inventory, bukan klaim umum
Margin meleset kalau salah skenario (§23.3) Kalau tim finance/marketing asumsi margin nominal padahal secara matematis tidak feasible, program bisa rugi tanpa disadari sampai laporan keuangan keluar Validasi skenario A vs B (§23.3) di depan, sebelum program diluncurkan — bukan setelah berjalan

23.6 Koneksi ke Sistem yang Sudah Dirancang

24. Strategi Loyalitas Konsumen — Analisis Mendalam Berbasis Haidilao

Sejauh ini gamifikasi konsumen (§16-B) baru berupa daftar ide yang tersebar. Section ini menyatukannya jadi satu strategi loyalitas yang koheren, dipetakan eksplisit dari tiap mekanik Haidilao (§18, 3 riset eksternal) ke konteks toko jam tangan — bukan cuma video tracking, tapi keseluruhan hubungan dengan konsumen.

24.1 Kenapa Loyalitas Jam Tangan Secara Struktural Beda dari F&B — dan Kenapa Itu Peluang

Playbook Haidilao dibangun di atas satu fakta: orang makan tiap minggu, jadi ada banyak kesempatan alami untuk engagement berulang. Jam tangan sebaliknya — orang beli jam sekali per beberapa tahun. Kalau strategi loyalitas cuma dibangun di sekitar momen pembelian, brand akan "hilang" dari radar konsumen selama bertahun-tahun di antara pembelian.

Tapi ada 2 touchpoint yang justru dimiliki toko jam dan TIDAK dimiliki restoran: - Servis baterai berkala (§23.1) — kunjungan wajib tiap 6 bulan (notifikasi proaktif, bukan menunggu baterai habis), dengan alasan legitimate (bukan dipaksakan) untuk konsumen datang ke toko — 2x setahun, jauh lebih sering dari siklus baterai alami. - Trade-in/upgrade (§23.2) — momen "naik kelas" yang justru tidak ada padanannya di F&B sama sekali (orang tidak "tukar tambah" makanan).

Insight strategis kunci: strategi loyalitas toko jam harus dibangun di sekitar momen servis, bukan cuma momen pembelian — ini pengganti "makan lagi minggu depan" ala restoran, dan sudah match dengan §23 yang baru dirancang.

24.2 Pemetaan Mekanik Haidilao → Toko Jam Tangan

Mekanik Haidilao Fungsi Psikologis (§18) Adaptasi untuk Toko Jam Status
Waiting area entertainment (origami, manikur, arcade) Resiprositas, sunk cost fallacy, alihkan persepsi waktu tunggu Waktu tunggu servis baterai (15–30 menit) — mini-quiz produk via WhatsApp sambil menunggu (§24.3) Baru — sebelumnya §16-B "Queue Quest" generik tanpa momen nyata untuk hidup di dalamnya
Secret codes / social hacks Social proof, status insider, viral UGC Frasa khusus terkait servis/produk untuk unlock bonus kecil (deterministik, bukan random — §6) Adaptasi, prioritas rendah
Tiered membership + efek "near-miss" Status seeking, loss aversion, progresi Tier berbasis spend + kunjungan servis + trade-in (bukan cuma spend) — §24.5 Diperkuat di section ini
Birthday benefits Emotional attachment, katalis group booking Sangat relevan — jam tangan produk gifting alami (ulang tahun, anniversary, wisuda) Sudah ada di §16-B, diperkuat konteksnya
Co-creation (racik saus sendiri) → IKEA effect Nilai dirasakan naik karena ikut "membuat" Personalisasi/gravir case-back — momen paling natural untuk jam tangan, versi asli dari mekanik ini Baru — insight paling kuat dari analisis ini
Pertunjukan tableside (Noodle Dance) Halo effect, teatrikal, momen shareable Reveal movement lewat exhibition caseback untuk jam mekanik — momen visual yang cuma dimiliki jam, bukan quartz biasa Baru — spesifik ke kategori produk
Surprise & delight (staf beri hadiah spontan) Endowment effect, reward variabel Budget kecil staf untuk gestur spontan (kartu ucapan, cleaning gratis saat servis) Sudah ada di §16-A, diperluas
Referral rewards Akuisisi via jaringan sosial Sudah dirancang skemanya (§19.1 referral_log) Sudah ada
Reaktivasi member lapse Bawa kembali user dorman Reminder servis baterai jadi trigger reaktivasi paling natural — lihat §24.6 Diperkuat — sebelumnya cuma "90 hari tidak aktif" generik
Gamifikasi HR internal (micro-promotion, badge) Motivasi staf Gen Z Sudah dirancang mendalam (§17) Sudah ada

24.3 Momen Tunggu di Toko Jam: Servis Baterai sebagai Pengganti Antrean

Haidilao mengeksploitasi waktu tunggu meja sebagai engagement surface utama. Toko jam biasanya tidak punya antrean besar — tapi servis baterai/reparasi kecil (15–30 menit) adalah momen tunggu yang setara, dan sebelumnya belum dimanfaatkan di desain manapun di dokumen ini.

24.4 Momen Teatrikal Khas Jam Tangan: Movement Reveal & Personalisasi

Dua mekanik Haidilao yang paling ikonik (Noodle Dance, racik saus sendiri) punya padanan yang justru lebih natural di jam tangan dibanding kebanyakan kategori retail lain:

Kenapa ini penting dimasukkan: dua momen ini adalah yang paling "Instagram-able" dari seluruh pengalaman beli jam — replikasi langsung dari insight §9 (Dampak Kuantitatif) gemini-1.md bahwa service innovation adalah faktor paling kuat ke loyalitas (skor 0,79), lebih kuat dari service quality standar.

24.5 Skema Tier — Rekomendasi Final (bukan lagi 2 opsi terbuka)

Sebelumnya (§11, §16-B) skema tier masih berupa beberapa opsi tanpa keputusan. Berdasarkan analisis ini, rekomendasi final:

Threshold tier berbasis 3 dimensi (bukan cuma spend seperti kebanyakan program loyalitas retail biasa):

Skor_tier = spend_12_bulan (60%) + jumlah_kunjungan_termasuk_servis (25%) + aktivitas_trade_in (15%)

Alasan bobot ini: - Spend tetap dominan (60%) — tetap harus jadi basis utama karena mencerminkan nilai ekonomi riil member ke bisnis. - Kunjungan termasuk servis baterai (25%) — ini yang membedakan dari program loyalitas retail generik: member yang rajin servis (jadi sering ke toko, sering dengar info produk baru) dihargai, bukan cuma yang belanja besar sekali lalu hilang. - Trade-in (15%) — sinyal member yang aktif "upgrade siklus", biasanya jadi pelanggan jangka panjang paling bernilai.

Tier (nama contoh — ganti sebelum dipakai, hindari nama Haidilao asli seperti §6) Threshold Skor_tier Privilese
Dasar Baru terdaftar Poin dasar per pembelian & servis
Menengah Skor setara ~2 kunjungan servis + 1 pembelian/tahun Prioritas booking servis, voucher ulang tahun
Utama Skor setara pembelian rutin + servis konsisten + minimal 1 trade-in Personalisasi gravir gratis, priority access rilisan baru
Kolektor Skor tertinggi, riwayat multi-trade-in/multi-pembelian Undangan private preview koleksi baru, dedicated service line

Implikasi ke data model (§19.1): points_ledger.event_type perlu ditambah 'earn_battery_service' dan 'earn_trade_in' (sebelumnya cuma earn_transaksi), supaya kunjungan servis & trade-in ikut menyumbang ke perhitungan tier, bukan cuma transaksi beli.

24.6 Loop Retensi & Reaktivasi — Disesuaikan ke Siklus Panjang Jam Tangan

Karena siklus beli jam sangat panjang, loop retensi/reaktivasi ala Haidilao (yang berasumsi orang makan lagi minggu depan) perlu disesuaikan:

24.7 Rekomendasi Prioritas Eksekusi (Loyalitas Konsumen Secara Spesifik)

  1. Battery service log + reminder otomatis (§23.4, §24.6) — paling murah, paling langsung actionable, dan jadi fondasi buat semua loop lain di section ini.
  2. Tier 3-dimensi (§24.5) — begitu data servis & trade-in mulai terkumpul, hitung ulang tier bukan cuma dari spend.
  3. Queue Quest di momen servis baterai (§24.3) — infrastruktur ringan (WhatsApp link), langsung punya momen nyata untuk hidup.
  4. Personalisasi/gravir sebagai standar penawaran (§24.4) — dorong UGC organik tanpa biaya campaign.
  5. Movement reveal (§24.4) — paling murah (tidak butuh sistem apa pun, cuma SOP staf saat closing) tapi butuh training staf supaya konsisten dieksekusi sebagai momen, bukan cuma serah-terima biasa.

25. Sistem Gamifikasi & Loyalitas Pelanggan — Rancangan Final

Konsolidasi §11, §16, §19.1, §23, §24 jadi satu sistem konkret siap-eksekusi — bukan lagi daftar opsi terbuka. Angka di bawah ini starting point yang perlu dikalibrasi ke data harga/COGS riil sebelum diluncurkan (lihat §25.6), tapi strukturnya sudah final.

25.1 Ringkasan Sistem dalam 1 Alur

Konsumen daftar (WhatsApp-first, §16-B)
     │
     ▼
Poin didapat dari: pembelian, servis baterai, trade-in, referral,
personalisasi, misi tunggu (Queue Quest) — semua deterministik (§6)
     │
     ▼
Total poin 12-bulan-rolling → tentukan Tier (§25.3)
     │
     ▼
Poin ditukar reward (§25.4) ATAU tier buka privilese otomatis
     │
     ▼
Loop retensi: reminder servis baterai (§24.6) + occasion trigger
menjaga member tetap aktif walau siklus beli jam panjang

25.2 Aturan Perolehan Poin (Konkret)

Basis: 1 poin = Rp10.000 spend (mengikuti pola "Rasa Coins" di §11, gampang dijelaskan ke konsumen). Aktivitas non-spend dapat poin flat supaya semua touchpoint di §24.2 punya insentif jelas, bukan cuma pembelian.

Aktivitas Poin Tipe (points_ledger.event_type, §19.1/§24.5) Catatan
Pembelian jam baru 1 poin / Rp10.000 earn_transaksi Basis utama, proporsional nilai riil ke bisnis
Servis baterai 6-bulanan (gratis) +50 poin flat earn_battery_service (baru, §24.5) Menghargai kunjungan meski servisnya sendiri gratis — ini yang paling sering terjadi (2x/tahun), jadi kontributor poin paling konsisten
Streak servis tepat waktu +25 poin bonus / kunjungan ke-2 berturut-turut yang was_on_time = true (§23.4), naik +25 tiap streak berlanjut (cap wajar, mis. maks +100) earn_mission type on_time_streak Baru — hadiahi konsistensi datang sesuai jadwal 6-bulanan, bukan cuma datang sekali-sekali
Trade-in diproses +200 poin flat earn_trade_in (baru, §24.5) Terlepas dari nilai trade-in — mendorong partisipasi ke program §23.2
Personalisasi/gravir saat beli +30 poin earn_mission Dorong opt-in ke momen paling shareable (§24.4)
Queue Quest (kuis saat tunggu servis) +10–20 poin earn_mission Via mission_definition type queue_quest (§24.3) — otomatis terpicu tiap kunjungan servis 6-bulanan
Table/Counter Mission (coba ≥2 model) +25 poin earn_mission Via mission_definition type table_mission
Referral berhasil (referee transaksi pertama) +100 poin (referrer) / +50 poin (referee, welcome) referral_bonus Sesuai referral_log (§19.1)
Bonus ulang tahun +100 poin otomatis birthday_bonus Trigger dari birthday_broadcast_log (§19.1)

Streak sebagai mekanik utama (baru): karena jadwal servis sekarang tetap (tiap 6 bulan, §23.1), ini jadi kandidat kuat buat mekanik "streak" ala aplikasi kebiasaan (Duolingo, dst) tapi disesuaikan ke kadensi yang realistis untuk jam tangan — bukan harian, tapi tiap 6 bulan. battery_service_log.was_on_time (§23.4) jadi basis hitungnya: 2 kunjungan tepat waktu berturut-turut = 1 tahun penuh "on track", cukup jadi pencapaian yang terasa berarti tanpa perlu member mikir tiap hari.

Semua deterministik — tidak ada elemen acak (guardrail UGB, §6).

25.3 Sistem Tier — Final (Total Poin Earned, Rolling 12 Bulan)

Penyederhanaan dari formula 3-dimensi di §24.5: karena aturan poin di §25.2 sudah otomatis mencerminkan spend + kunjungan servis + trade-in, tier cukup dihitung dari total poin earned 12-bulan-rolling (bukan saldo — poin yang sudah ditukar tetap dihitung untuk status tier, supaya menukar reward tidak menghukum status member).

Tier Threshold (poin earned/12bln) Kira-kira setara Privilese
Dasar 0+ Member baru terdaftar Poin dasar semua aktivitas §25.2
Menengah 500+ ~2 servis 6-bulanan (1 tahun penuh on-time + streak bonus) + 1 pembelian kelas menengah Voucher ulang tahun lebih besar, prioritas booking servis
Utama 1.500+ Pembelian rutin + servis konsisten + minimal 1 trade-in Personalisasi gravir gratis, priority access rilisan baru
Kolektor 4.000+ Riwayat multi-pembelian/multi-trade-in Undangan private preview, dedicated service line

tier_history (§19.1) dihitung ulang tiap ada points_ledger baru — pakai efek "near-miss" (§18, gemini-1.md): tampilkan ke member berapa poin lagi menuju tier berikutnya di ringkasan WhatsApp (§18, chatgpt-1.md pola "kamu dapat X poin, tinggal Y lagi").

25.4 Katalog Redemption (Contoh Awal — Kalibrasi ke COGS Riil)

Poin Reward Kenapa Dipilih
100 Servis pembersihan/poles gratis Biaya marginal rendah, dorong kunjungan lagi
300 Penggantian tali/strap Item fisik bernilai rendah-menengah, COGS terkontrol
800 Voucher diskon 10% pembelian berikutnya Dorong upgrade/pembelian kedua
1.500 Personalisasi gravir premium + priority service Selaras §24.4, biaya marginal rendah (cuma jasa)
3.000 Akses private preview koleksi baru + voucher besar Reward tier atas, prioritaskan retensi Kolektor

Prinsip dari riset (§18, chatgpt-1.md): utamakan reward berupa item/jasa (biaya marginal rendah bagi perusahaan, nilai dirasakan tinggi bagi konsumen) dibanding voucher setara-cash — supaya biaya reward tetap di kisaran 0,8–1,5% dari sales member (§11) alih-alih tergerus diskon tunai.

25.5 Peta Perjalanan Pelanggan Lengkap dengan Titik Gamifikasi

Tahap Apa yang terjadi Mekanik gamifikasi yang aktif
Discovery Lihat konten/koleksi di toko atau medsos — (belum jadi member)
Kunjungan pertama Browsing di toko, video analytics jalan (§1) tapi anonim
Pembelian pertama Closing di kasir, tawarkan registrasi WhatsApp-first (§16-B) Poin pembelian (§25.2), opsi personalisasi (+30 poin), battery_service_log.next_due_at diset otomatis +6 bulan dari tanggal beli
Reminder H-7 (bulan ke-5,75) WhatsApp otomatis dari battery_reminder_log (§23.4) Ajak datang cek rutin + sneak-peek rilisan baru kalau ada
Servis 6-bulanan #1 Kunjungan wajib, gratis +50 poin, Queue Quest saat tunggu, info rilisan baru, next_due_at baru diset lagi +6 bulan
Ulang tahun Trigger otomatis (bisa jatuh di antara 2 siklus servis) +100 poin, voucher sesuai tier
Servis 6-bulanan #2 (tepat waktu) Loop berulang, jantung retensi (§24.1) +50 poin + streak bonus +25 poin (2 kunjungan on-time berturut-turut = 1 tahun penuh), naikkan tier bertahap
Servis #3, #4, dst Streak berlanjut kalau tetap tepat waktu Bonus streak naik (cap wajar), makin kuat alasan untuk tidak skip jadwal
Trade-in / upgrade Konsumen tukar jam lama (§23.2) +200 poin, kemungkinan naik tier langsung
Referral Konsumen ajak teman +100/+50 poin, referral_log
Reaktivasi (lewat jatuh tempo servis 6-bulanan tanpa respons) WhatsApp reminder susulan otomatis (§24.6) Trigger personalized_offer_log, streak ter-reset kalau kelewat jauh dari grace period

25.6 Sebelum Diluncurkan: Kalibrasi Wajib

Angka di §25.2–§25.4 adalah starting point terstruktur, bukan angka final — sebelum eksekusi, kalibrasi ke: - COGS riil tiap item reward (§25.4) — supaya biaya reward tetap dalam target 0,8–1,5% sales member (§11). - Rata-rata harga jual & margin riil (§23.3) — supaya nilai poin (Rp10.000 = 1 poin) proporsional dengan margin, bukan cuma ditiru dari referensi Haidilao. - Threshold tier (§25.3) — sesuaikan dengan distribusi spend & frekuensi servis aktual konsumen, bukan angka ilustratif ini. - Cek ulang guardrail UGB (§6) tiap kali ada mekanik baru ditambahkan — pastikan tetap deterministik.

26. Deteksi Presisi per Etalase — SKU-Level Tanpa VLM

Ide: pasang kamera/sensor kecil di tiap etalase yang mendeteksi slot mana yang diangkat, lalu dicocokkan ke track_id/loyalty_id lewat timestamp + zona — bukan lewat koneksi langsung antar kamera. Ini lebih murah dan lebih presisi dari pendekatan Layer 2 VLM yang sudah ada, bukan tambahan kompleksitas — karena reuse pola join yang sama persis dengan track_transaction_match (§5.1) dan zone_dwell_segment (§10).

26.1 Kenapa Ini Lebih Murah, Bukan Lebih Mahal

Layer 2 VLM (§4) saat ini menebak product_hint dari gambar — butuh model yang cukup pintar untuk "membaca" jam model apa yang kelihatan, dan bisa salah antara 2 model yang mirip. Pendekatan etalase ini membalik soalnya: karena slot → SKU sudah diketahui pasti dari data inventori, kamera per etalase cuma perlu jawab pertanyaan biner "slot ini kosong atau tidak?" — deteksi perubahan sederhana (frame differencing / background subtraction), bukan klasifikasi visual. Ini: - Tidak butuh resolusi tinggi (beda dari kebutuhan baca detail wajah/model produk). - Tidak butuh VLM sama sekali untuk identifikasi produk — product_hint jadi ground truth SKU-level, bukan tebakan. - Mengurangi panggilan VLM yang sebelumnya dipakai untuk produk (Layer 2 masih relevan untuk interaction_type — mencoba vs sekadar pegang — tapi bukan lagi untuk "produk apa").

26.2 Degradasi Wajar Kalau Item Tidak Diangkat

Kalau konsumen cuma lihat-lihat tanpa mengangkat barang, sinyalnya otomatis turun ke level yang sudah ada: zone_dwell_segment (dwell time di etalase itu, level zona) — bukan presisi SKU, tapi tetap tercatat sebagai minat. Sistem punya 2 tingkat presisi yang saling melengkapi, bukan 2 sistem terpisah:

Kondisi Presisi Sumber
Item diangkat dari slot SKU-level pasti slot_event (baru, §26.3)
Cuma dilihat/berdiri di depan etalase Level zona/etalase zone_dwell_segment (§10, sudah ada)

26.3 Data Model Tambahan

-- Mapping slot fisik di etalase → SKU, diupdate manual tiap rotasi barang (mirip zone_definition.product_category, lebih granular)
CREATE TABLE etalase_slot_definition (
    slot_id      TEXT PRIMARY KEY,
    zone_id      TEXT REFERENCES zone_definition(zone_id),
    store_id     TEXT NOT NULL,
    sku          TEXT NOT NULL,        -- kode model jam persis
    position_label TEXT,               -- mis. "baris 2, kolom 3" — buat memudahkan staf update manual
    active_from  DATE,
    active_to    DATE                  -- null = masih di slot itu
);

-- Event mentah angkat/taruh kembali, dari kamera/sensor per etalase — deteksi perubahan sederhana, BUKAN VLM
-- 1 baris per kejadian ('lifted' saat diangkat, 'returned' saat ditaruh kembali) — mentah, belum dipasangkan
CREATE TABLE slot_event (
    id           SERIAL PRIMARY KEY,
    slot_id      TEXT REFERENCES etalase_slot_definition(slot_id),
    event_type   TEXT NOT NULL,   -- 'lifted' | 'returned'
    detected_at  TIMESTAMP NOT NULL
);

-- Sesi pegang — pasangan (lifted, returned) yang sudah digabung jadi 1 baris dengan durasi terhitung,
-- konsisten dengan pola dwell_seconds/interaction_seconds di tabel lain (§10) — bukan dibiarkan mentah
CREATE TABLE slot_hold_session (
    id                SERIAL PRIMARY KEY,
    slot_id           TEXT REFERENCES etalase_slot_definition(slot_id),
    lifted_event_id   INT REFERENCES slot_event(id),
    returned_event_id INT REFERENCES slot_event(id),   -- null kalau belum ditaruh kembali (masih dipegang / sesi belum selesai)
    lifted_at         TIMESTAMP NOT NULL,
    returned_at       TIMESTAMP,
    hold_seconds      INT                                -- = returned_at - lifted_at, dihitung sekali saat returned_event masuk
);

-- Join layer: cocokkan 1 sesi pegang ke track_id yang paling mungkin (posisi + waktu terdekat di zona yang sama)
CREATE TABLE slot_track_match (
    id                    SERIAL PRIMARY KEY,
    slot_hold_session_id  INT REFERENCES slot_hold_session(id),
    track_id              TEXT REFERENCES track_event(track_id),
    match_confidence      TEXT,   -- 'single_person_in_zone' | 'closest_by_time' | 'ambiguous_multi_person' — transparansi soal keandalan matching
    matched_at            TIMESTAMP NOT NULL
);

Logika matching (batch job, sama pola dengan §5.1): tiap slot_event bertipe returned dipasangkan dengan lifted terdekat sebelumnya di slot_id yang sama → jadi 1 baris slot_hold_session dengan hold_seconds terhitung otomatis. Lalu untuk tiap sesi, cari track_event/zone_dwell_segment yang aktif di zone_id yang sama pada rentang lifted_atreturned_at — kalau cuma ada 1 orang di zona itu saat itu, match_confidence = 'single_person_in_zone' (paling andal, ini yang terjadi di contoh §26.2 barusan). Kalau ada lebih dari 1 orang, pakai jarak waktu terdekat sebagai fallback (closest_by_time), atau tandai ambiguous_multi_person kalau terlalu tidak pasti untuk dipakai sebagai basis keputusan bisnis (mis. jangan dipakai untuk atribusi skor staf individual, tapi tetap valid untuk agregat "SKU ini sering diangkat").

26.4 Yang Perlu Diperhatikan (bukan rumit, tapi perlu diputuskan)

26.5 Dampak ke Bagian Lain


Dokumen ini di-generate otomatis dari README.md — untuk versi paling mutakhir, rujuk file sumber di project. Dua ringkasan pendamping tersedia terpisah: presentasi eksekutif (non-teknis) dan referensi teknis (arsitektur & skema data).