# Sales On Hand (ZFM_RF_GET_ONHAND) - Feature Documentation

## Overview

Fitur **Sales On Hand** menambahkan pull data **sales on hand** (diambil dari schedule line delivery order) dari SAP ke database lokal. Data disimpan pada tabel baru **`rpt_et_getonhand`** dan diproses bersama dengan data SAP lainnya (ZVBILLING, ZFFINANCE_YIELD, Z_GET_NEW_CUST, Z_GET_PAYMENT) melalui mekanisme `rpt_sync_status` + queue job yang sama. Sejak pemisahan report SAP (`rptsap`), sync dan preview data on-hand dilakukan dari menu **SAP Sync**, bukan dari `rptptr`.

### Endpoint SAP

```
GET /ZFM_RF_GET_ONHAND?sap-client={client}&sap-user={user}&sap-password={pass}&sap-language=EN&format=json&IP_YEAR={year}&IP_MONTH={month}
```

| Parameter | Keterangan |
|---|---|
| `IP_YEAR` | Tahun fiskal (4 digit) |
| `IP_MONTH` | Bulan fiskal (2 digit, `01`-`12`) |

### Struktur Response (`ET_DATA[]`)

| Field | Tipe (SAP) | Kolom DB | Keterangan |
|---|---|---|---|
| `MANDT` | char(3) | `MANDT` | Client |
| `DOCUMENTNUMBER` | char(20) | `DOCUMENTNUMBER` | Nomor dokumen (delivery order) |
| `CUSTOMERCODE` | char(20) | `CUSTOMERCODE` | Kode customer |
| `DELIVERYDATE` | date | `DELIVERYDATE` | Tanggal delivery |
| `QUANTITY` | decimal | `QUANTITY` | Quantity |
| `FISCALYEAR` | char(4) | `FISCALYEAR` | Tahun fiskal |
| `FISCALMONTH` | char(6) | `FISCALMONTH` | Bulan fiskal YYYYMM (dari filter API) |

Kolom tambahan `FISYR` (tahun sync) disimpan untuk keperluan filtering tahunan, mengikuti pola tabel SAP lain.

---

## File Structure

```
app/
  Jobs/
    SapOnHandSyncJob.php              # NEW: Sync job untuk Z_GET_ONHAND (bulanan, 12 bulan)
  Http/
    Controllers/
      RptsapController.php            # Updated: dispatch job ke-5, label, year list, data preview
      RptptrController.php            # Updated: hanya report viewer ZVBILLING

  Console/
    Commands/
      SapDailySync.php                # Updated: dispatch via RptsapController
      DispatchPendingSync.php         # Updated: tambah case Z_GET_ONHAND

resources/
  views/
    report/
      rptsap/
        filter.blade.php              # Updated: pilihan endpoint SAP terpisah
        status_list.blade.php         # Updated: label 'Sales On Hand' (PHP + JS)
        status.blade.php              # Updated: label + data preview kolom on-hand
      rptptr/
        result.blade.php              # Updated: menghapus tab Sales On Hand

database/
  migrations/
    2026_08_03_100000_create_rpt_et_getonhand_table.php   # tabel rpt_et_getonhand
    2026_08_03_105955_add_month_to_rpt_sync_status_table.php   # kolom sync_month untuk tracking bulan sync

docs/
  rptptr_onhand.md                    # NEW: dokumentasi ini
```

---

## Database Table: `rpt_et_getonhand`

```sql
CREATE TABLE `rpt_et_getonhand` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `MANDT` varchar(3) DEFAULT NULL COMMENT 'Client',
  `DOCUMENTNUMBER` varchar(20) DEFAULT NULL COMMENT 'Document Number',
  `CUSTOMERCODE` varchar(20) DEFAULT NULL COMMENT 'Customer Code',
  `DELIVERYDATE` date DEFAULT NULL COMMENT 'Delivery Date',
  `QUANTITY` decimal(20,3) DEFAULT NULL COMMENT 'Quantity',
  `FISCALYEAR` varchar(4) DEFAULT NULL COMMENT 'Fiscal Year',
  `FISCALMONTH` varchar(6) DEFAULT NULL COMMENT 'Fiscal Month YYYYMM from API filter',
  `FISYR` varchar(4) DEFAULT NULL COMMENT 'Fiscal Year of Sync',
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_onhand_custcode` (`CUSTOMERCODE`),
  KEY `idx_onhand_fiscalmonth` (`FISCALMONTH`),
  KEY `idx_onhand_fisyr` (`FISYR`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
```

---

## Alur Sync

### 1. Sync Manual (RptsapController `store()`)

Saat user klik **Execute** pada halaman filter `rptsap`:

1. User memilih tahun, checklist **Sales On Hand (ZFM_RF_GET_ONHAND)**, lalu memilih bulan (01–12).
2. `RptsapController::runYearlySync()` membuat record `rpt_sync_status` baru dengan `report_type = 'Z_GET_ONHAND'` dan `sync_month` (status `pending`).
3. Dispatch `SapOnHandSyncJob` dengan parameter bulan ke queue `default`.
4. User diarahkan ke halaman status (`rptsap.status`).

### 2. Sync Harian (SapDailySync)

Command `sap:daily-sync` memanggil `RptsapController::runYearlySync()` dengan **bulan berjalan** sebagai parameter, sehingga `SapOnHandSyncJob` hanya mengambil data untuk bulan tersebut. Sync 12 bulan sekaligus tidak diperbolehkan.

### 3. Auto Dispatch (DispatchPendingSync)

Command `sap:dispatch-pending` kini mengenali `report_type = 'Z_GET_ONHAND'` dan me-redispatch `SapOnHandSyncJob` untuk sync yang stuck dalam status `pending`.

### 4. SapOnHandSyncJob

Mengikuti pola `SapFinanceYieldSyncJob` (bulanan):

1. Cek status sync masih `pending` (jika tidak, exit).
2. Update status menjadi `processing`.
3. **Delete semua data `rpt_et_getonhand` untuk `FISYR` tahun tersebut** (simple replace logic).
4. Loop 12 bulan (`01`-`12`), panggil endpoint `ZFM_RF_GET_ONHAND` dengan `IP_YEAR` + `IP_MONTH`, dengan retry 3x (backoff eksponensial 2s/4s).
5. Ekstrak baris dari key `ET_DATA` (fallback: `data`, atau array penuh).
6. Insert ber-batch (chunk 100) ke `rpt_et_getonhand`, dengan pembersihan memori tiap 10 chunk.
7. Update progres `rpt_sync_status` (total_records / processed_records).
8. Update status menjadi `completed` (atau `failed` jika semua bulan gagal).

Konfigurasi job: `timeout = 3600`, `tries = 3`, `backoff = 60`.

Normalisasi `DELIVERYDATE` pada `convertSapRowToDbFormat` menangani 3 format:
- `Y-m-d` (mis. `2021-09-03`) → dipakai langsung
- `d.m.Y` (format SAP umum) → dikonversi ke `Y-m-d`
- `YYYYMMDD` (8 digit) → dikonversi ke `Y-m-d`

---

## Perubahan UI

### `rptsap` Filter Page

Checklist endpoint terpisah. **Sales On Hand (ZFM_RF_GET_ONHAND)** dapat dipilih sendiri atau bersama endpoint lain.

### `rptsap` Status Page (`status.blade.php`)

- Label report type: `Sales On Hand (ZFM_RF_GET_ONHAND)`.
- Data preview menampilkan kolom on-hand (Document Number, Customer Code, Delivery Date, Quantity, Fiscal Year, Fiscal Month) ketika `report_type === 'Z_GET_ONHAND'`.

### `rptsap` Status List (`status_list.blade.php`)

Label `Sales On Hand` untuk `report_type = 'Z_GET_ONHAND'` (di PHP array dan JS object).

### `rptptr` Result Page

Tab **Sales On Hand** dihapus dari halaman Price Trend. `rptptr` kini menjadi report viewer ZVBILLING murni.

---

## Catatan Penting

- **Endpoint hanya tersedia di SAP dev** (fioridev, client 110 / INT-1) saat pengujian dilakukan. Server SAP produksi (client 300) mengembalikan `ERROR "Invalid Function. FL-E-046: ZFM_RF_GET_ONHAND"`. Job membaca kredensial dari `.env` (`SAP_BASE_URL`, `SAP_CLIENT`, `SAP_USER`, `SAP_PASSWORD`) — pastikan function sudah di-deploy ke server produksi sebelum sync produksi dijalankan.
- Pola penyimpanan memakai **simple replace** per tahun (data lama untuk `FISYR` yang sama dihapus dulu), konsisten dengan job SAP lain.
- `FISCALMONTH` di-response SAP berupa 2 digit (`09`), sedangkan kolom DB dikomentari sebagai YYYYMM — nilai disimpan apa adanya dari SAP.

---

## Penggunaan di Salesperson Dashboard

Sejak `prompt_051403aug2026`, data `rpt_et_getonhand` juga dipakai oleh **Salesperson Dashboard** (`/dashboard`):

- Baris dengan `DELIVERYDATE > current date` dianggap **belum terkirim** (sales on hand) dan **mengurangi Sales Actual** (`rpt_ET_ZVBILLING.SALESDOC` / `trs_header_data.SALESDOCUMENT`) yang mapping-nya cocok via `DOCUMENTNUMBER`.
- Implementasi: `DashboardController::getPendingOnHandDocuments()` (map `DOCUMENTNUMBER => SUM(QUANTITY)`) dan `getOnHandReduction()` (pemetaan per dokumen + prorata USD dari qty kg + pecahan per customer).
- Detail lengkap: `docs/sales_dashboard.md` section "Sales On Hand / Shipment Berjalan".
