# Blueprint Modul Invoice & Pembukuan Ringan

Dokumen ini merangkum rancangan implementasi modul invoice yang menyatu dengan sistem servis yang sudah ada.

## 1. Prinsip Arsitektur

- Tetap gunakan satu aplikasi Laravel yang sama.
- Pisahkan modul secara logis: `Customers`, `Service`, `Sales`, `Invoices`, `Payments`, `Accounting`.
- Invoice wajib terhubung ke customer yang sudah terdaftar.
- Invoice bisa dibuat dari tiga sumber:
  - `service`
  - `sale`
  - `manual`
- Data item invoice harus disimpan sebagai snapshot di tabel invoice item agar histori tidak berubah jika data sumber berubah.

## 2. Penyesuaian Sistem Yang Sudah Ada

Struktur sistem saat ini:

- Tabel customer sudah ada: `customers`
- Tiket servis sudah ada: `service_tickets`
- Role user saat ini: `admin`, `technician`, `courier`
- Paket QR code sudah tersedia: `simplesoftwareio/simple-qrcode`

Perubahan yang disarankan sebelum modul invoice dibuat:

- Tambahkan role baru: `cashier`
- Ganti akses kurir jika diperlukan nanti, tetapi untuk invoice tidak perlu dihubungkan
- Customer tetap satu sumber data untuk service, invoice, dan penjualan

## 3. Skema Tabel Final Yang Siap Di-Migrate

### 3.1 Tabel `customers`

Tabel ini sudah ada. Disarankan ditambah beberapa kolom agar kuat untuk invoice:

- `code` nullable unique
- `created_by` nullable foreign key users
- `updated_by` nullable foreign key users
- `is_active` boolean default true
- `tax_number` nullable string 50

Catatan:

- `name` tetap wajib
- `whatsapp` sebaiknya di-index
- `email` boleh tetap nullable unique

### 3.2 Tabel `invoices`

Header utama invoice.

Field yang disarankan:

- `id`
- `invoice_number` string unique nullable
- `customer_id` foreign key ke `customers`
- `source_type` enum: `service`, `sale`, `manual`
- `source_id` unsignedBigInteger nullable
- `status` enum: `draft`, `issued`, `cancelled`
- `payment_status` enum: `unpaid`, `partial`, `paid`
- `invoice_date` date nullable
- `due_date` date nullable
- `issued_at` timestamp nullable
- `paid_at` timestamp nullable
- `currency` char(3) default `IDR`
- `subtotal` decimal(15,2) default 0
- `discount_total` decimal(15,2) default 0
- `tax_total` decimal(15,2) default 0
- `grand_total` decimal(15,2) default 0
- `amount_paid` decimal(15,2) default 0
- `balance_due` decimal(15,2) default 0
- `public_note` text nullable
- `internal_note` text nullable
- `verification_code` string 100 unique nullable
- `signature_path` string nullable
- `barcode_path` string nullable
- `pdf_path` string nullable
- `issued_by` nullable foreign key users
- `created_by` nullable foreign key users
- `updated_by` nullable foreign key users
- `cancelled_by` nullable foreign key users
- `cancel_reason` text nullable
- timestamps

Indeks yang penting:

- index `customer_id`
- index `source_type, source_id`
- index `status`
- index `payment_status`
- index `invoice_date`

Aturan bisnis:

- `invoice_number` dibentuk hanya saat status berubah dari `draft` ke `issued`
- `verification_code` dibuat saat invoice diterbitkan
- `internal_note` tidak tampil di invoice customer
- `source_id` kosong jika invoice manual

### 3.3 Tabel `invoice_items`

Baris item invoice.

Field yang disarankan:

- `id`
- `invoice_id` foreign key ke `invoices`
- `item_type` enum: `service`, `product`, `diagnostic_fee`, `cancellation_fee`, `manual`, `other`
- `source_item_id` unsignedBigInteger nullable
- `description` string 255
- `qty` decimal(12,2) default 1
- `unit` string 30 nullable
- `unit_price` decimal(15,2) default 0
- `discount_amount` decimal(15,2) default 0
- `tax_amount` decimal(15,2) default 0
- `line_total` decimal(15,2) default 0
- `sort_order` integer default 0
- `meta` json nullable
- timestamps

Aturan bisnis:

- Semua item dari service atau sale tetap dicopy ke sini
- `description`, `qty`, `unit_price`, dan `line_total` adalah snapshot final

### 3.4 Tabel `payments`

Pencatatan pembayaran invoice.

Field yang disarankan:

- `id`
- `invoice_id` foreign key ke `invoices`
- `payment_number` string unique nullable
- `payment_date` datetime
- `amount` decimal(15,2)
- `method` enum: `cash`, `transfer`, `qris`, `debit_card`, `credit_card`, `other`
- `reference_number` string 100 nullable
- `note` text nullable
- `received_by` nullable foreign key users
- `created_by` nullable foreign key users
- timestamps

Aturan bisnis:

- Total pembayaran mengubah `amount_paid`, `balance_due`, dan `payment_status`
- Invoice yang lunas otomatis set `paid_at`

### 3.5 Tabel `invoice_activity_logs`

Audit log internal invoice.

Field yang disarankan:

- `id`
- `invoice_id` foreign key ke `invoices`
- `user_id` nullable foreign key users
- `action` string 100
- `description` text
- `meta` json nullable
- timestamps

Gunakan untuk jejak berikut:

- draft dibuat
- invoice diterbitkan
- pembayaran ditambahkan
- invoice dibatalkan
- catatan internal diubah

### 3.6 Tabel Opsional `sales`

Jika penjualan barang belum ada, siapkan header transaksi penjualan terpisah:

- `id`
- `sale_number` string unique
- `customer_id` foreign key
- `status` enum: `draft`, `completed`, `cancelled`
- `sale_date` datetime
- `subtotal`, `discount_total`, `tax_total`, `grand_total`
- `note` text nullable
- `created_by`, `updated_by`
- timestamps

Lalu tabel `sale_items` berisi snapshot item jual.

### 3.7 Tabel Pembukuan Minimum

Jika ingin pembukuan langsung rapi, siapkan:

- `accounts`
- `journal_entries`
- `journal_lines`

MVP paling minimum:

- akun kas
- akun bank
- akun piutang usaha
- akun pendapatan jasa
- akun pendapatan penjualan
- akun diskon penjualan

## 4. Draft Struktur Migration

Urutan migration yang aman:

1. tambah role `cashier` ke users
2. alter customers untuk kolom audit tambahan
3. create `invoices`
4. create `invoice_items`
5. create `payments`
6. create `invoice_activity_logs`
7. create `accounts`
8. create `journal_entries`
9. create `journal_lines`

Catatan teknis:

- Untuk kolom `source_type` + `source_id`, cukup pakai pola manual, tidak perlu morph relation penuh jika ingin query sederhana
- Untuk invoice dari service, tetap cek bahwa satu tiket tidak memiliki invoice aktif ganda

Constraint yang disarankan:

- satu service ticket maksimal satu invoice aktif dengan `status != cancelled`
- invoice `paid` tidak boleh diedit nominalnya
- invoice `issued` tidak boleh dihapus, hanya dibatalkan dengan alasan

## 5. Matrix Role & Permission

Role yang disarankan:

- `admin`
- `cashier`
- `technician`
- `courier`

### 5.1 Admin

Hak akses admin:

- buat customer baru
- edit customer
- nonaktifkan customer
- lihat semua customer
- buat invoice manual
- buat invoice dari service
- buat invoice dari sale
- edit invoice draft
- terbitkan invoice
- input pembayaran
- batalkan invoice
- lihat dan edit internal note
- lihat audit log invoice
- lihat laporan invoice dan pembayaran
- kelola akun user

### 5.2 Cashier

Hak akses kasir:

- buat customer baru
- lihat customer
- tidak bisa edit penuh master customer
- buat invoice manual
- buat invoice dari service yang sudah layak ditagih
- buat invoice dari sale
- edit invoice draft yang dia buat atau yang diizinkan admin
- terbitkan invoice
- input pembayaran
- cetak PDF invoice
- lihat internal note
- tidak boleh hapus customer
- tidak boleh ubah user role

Catatan customer untuk kasir:

- boleh tambah data customer baru
- edit master customer hanya sebatas koreksi ringan bila diizinkan
- penghapusan, merge, dan nonaktifkan customer tetap admin

### 5.3 Technician

Hak akses teknisi:

- lihat daftar service
- tambah log teknis
- ubah status teknis sesuai aturan servis
- usulkan item biaya atau estimasi
- tidak bisa menerbitkan invoice
- tidak bisa input pembayaran
- tidak bisa kelola customer

### 5.4 Courier

Hak akses kurir:

- fokus ke handover dan pengambilan unit
- tidak terlibat invoice atau customer management

### 5.5 Permission Matrix Ringkas

| Fitur | Admin | Cashier | Technician | Courier |
|---|---|---|---|---|
| Tambah customer | Ya | Ya | Tidak | Tidak |
| Edit customer penuh | Ya | Tidak | Tidak | Tidak |
| Lihat daftar customer | Ya | Ya | Tidak | Tidak |
| Buat invoice manual | Ya | Ya | Tidak | Tidak |
| Buat invoice dari service | Ya | Ya | Tidak | Tidak |
| Edit draft invoice | Ya | Ya | Tidak | Tidak |
| Terbitkan invoice | Ya | Ya | Tidak | Tidak |
| Input pembayaran | Ya | Ya | Tidak | Tidak |
| Batalkan invoice | Ya | Opsional | Tidak | Tidak |
| Lihat internal note | Ya | Ya | Tidak | Tidak |
| Kelola user | Ya | Tidak | Tidak | Tidak |

## 6. Alur UI Modul Invoice

### 6.1 Navigasi Yang Disarankan

Tambahkan menu baru pada layout admin dan cashier:

- `Dashboard` jika nanti ada
- `Service`
- `Customers`
- `Invoices`
- `Payments`
- `Reports`

Submenu invoice:

- daftar invoice
- draft
- belum dibayar
- dibayar sebagian
- lunas
- dibatalkan
- buat invoice manual

### 6.2 Alur UI: Invoice Dari Service

Trigger:

- dari halaman detail tiket admin
- dari daftar service dengan tombol `Buat Invoice`

Syarat invoice dari service:

- status tiket `Ready`
- status tiket `Cancelled` hanya jika ada biaya diagnosa atau pembatalan

Langkah UI:

1. User klik `Buat Invoice` pada tiket service.
2. Sistem cek apakah tiket sudah punya invoice aktif.
3. Form invoice terbuka dengan data auto-fill:
   - customer
   - referensi ticket code
   - item jasa dasar atau item estimasi yang disetujui
4. User bisa tambah item lain seperti sparepart, biaya cleaning, biaya diagnosa.
5. User bisa isi `public_note` dan `internal_note`.
6. User pilih:
   - `Simpan Draft`
   - `Terbitkan Invoice`
7. Saat diterbitkan, sistem:
   - buat nomor invoice
   - buat verification code
   - generate QR/barcode
   - simpan tanda tangan penerbit
   - kunci nominal invoice

Elemen UI penting:

- badge sumber: `Dari Service`
- badge status bayar
- link balik ke nota servis
- ringkasan device dan ticket code

### 6.3 Alur UI: Invoice Manual

Trigger:

- menu `Invoices > Buat Manual`

Langkah UI:

1. User pilih customer dari dropdown searchable.
2. Jika customer belum ada, admin atau kasir bisa klik `Tambah Customer` lewat modal ringan.
3. User tambah baris item dinamis.
4. Masing-masing item berisi:
   - deskripsi
   - qty
   - satuan
   - harga
   - diskon
5. Sistem hitung subtotal, diskon, pajak, dan grand total.
6. User isi catatan publik dan internal bila perlu.
7. User simpan draft atau terbitkan.

Elemen UI penting:

- `source_type = manual`
- customer wajib, tidak boleh free text
- tombol preview PDF sebelum terbit

### 6.4 Alur UI: Invoice Dari Penjualan

Trigger:

- dari halaman detail penjualan atau POS selesai

Langkah UI:

1. User klik `Buat Invoice` dari transaksi sale.
2. Data customer dan item penjualan diambil sebagai draft snapshot.
3. User review item, note, dan jatuh tempo.
4. User simpan draft atau terbitkan.

Elemen UI penting:

- badge sumber: `Dari Penjualan`
- invoice item tetap editable selama masih draft

### 6.5 Alur UI: Pembayaran Invoice

Di halaman detail invoice tambahkan card `Pembayaran`.

Isi card:

- total invoice
- total dibayar
- sisa tagihan
- status pembayaran
- tombol `Tambah Pembayaran`

Form pembayaran:

- tanggal bayar
- nominal
- metode
- nomor referensi
- catatan

Perubahan status otomatis:

- belum ada pembayaran: `unpaid`
- pembayaran sebagian: `partial`
- sisa 0: `paid`

### 6.6 Alur UI: Signature + Barcode Verifikasi

Di footer invoice PDF/web:

- nama penerbit invoice
- role penerbit
- waktu terbit
- gambar tanda tangan digital dari user penerbit atau canvas sign saat issue
- QR/barcode yang mengarah ke halaman verifikasi invoice

Halaman verifikasi invoice menampilkan:

- nomor invoice
- nama customer
- tanggal invoice
- total
- status invoice
- status pembayaran
- status valid / dibatalkan

Jika ingin ringan pada fase awal:

- simpan hanya `verification_code`
- generate QR saat render PDF
- `signature_path` diambil dari tanda tangan digital tersimpan atau canvas saat terbit

## 7. Catatan Internal & Legal

Pisahkan dua jenis catatan:

- `public_note`: tampil di invoice customer
- `internal_note`: hanya tampil di sistem

Legal minimum yang perlu dipastikan:

- invoice final menyimpan snapshot item dan nominal
- invoice final menyimpan siapa penerbitnya
- invoice final menyimpan waktu terbit
- invoice final punya verification code
- invoice final yang sudah lunas tidak boleh diubah nominalnya

## 8. Aturan Integrasi Dengan Service

Aturan yang disarankan:

- tiket `Ready` boleh dibuat invoice servis normal
- tiket `Cancelled` boleh dibuat invoice hanya jika ada biaya diagnosa atau pembatalan
- tiket `Pending`, `Checking`, `Repairing` belum boleh ditagih kecuali Anda memang ingin invoice DP
- satu tiket service maksimal satu invoice aktif

Tambahan yang berguna untuk teknisi:

- teknisi boleh mengusulkan item pekerjaan dan sparepart
- admin atau kasir yang mengubah usulan itu menjadi invoice final

## 9. Implementasi Bertahap Yang Paling Aman

### Fase 1

- tambah role `cashier`
- tambah modul `customers` dengan pembatasan akses
- buat tabel `invoices`, `invoice_items`, `payments`, `invoice_activity_logs`

### Fase 2

- buat halaman daftar invoice
- buat create manual invoice
- buat generate invoice dari service
- buat detail invoice + pembayaran
- buat PDF invoice + QR verifikasi

### Fase 3

- buat sale module jika diperlukan
- buat generate invoice dari sale
- tambah jurnal akuntansi otomatis

## 10. Keputusan Rekomendasi

Keputusan final yang paling tepat untuk sistem ini:

- tetap satu aplikasi
- customer menjadi master data tunggal
- invoice mendukung `manual`, `service`, dan `sale`
- admin dan cashier bisa akses invoice
- admin dan cashier bisa daftar customer
- hanya admin yang punya akses penuh untuk manage customer
- invoice punya `internal_note`, `payment_status`, `signature_path`, dan QR/barcode verifikasi
- invoice final selalu menyimpan snapshot item dan nominal