# Rencana Implementasi & Dokumentasi CLI Controller Murni (Tanpa $_SESSION)
**Modul Penjualan - san_sarana_7jul**

Dokumen ini mendokumentasikan hasil analisis struktur modul penjualan, penjelasan alur kerja pemrosesan order CRM, serta rekomendasi lengkap penulisan kode CLI Controller untuk memproses order dari CRM menjadi transaksi penjualan di database secara langsung tanpa menggunakan UI browser dan tanpa bergantung pada session PHP (`$_SESSION`) sama sekali.

---

## 1. Analisis Modul Penjualan

Modul penjualan berlokasi di: `z:\san_sarana_7jul\application\modules\penjualan`

### Struktur Folder Utama
* **[config](file:///z:/san_sarana_7jul/application/modules/penjualan/config)**: Konfigurasi inti UI, tata letak, dan nilai default transaksi (contoh: `coTransaksiCore.php`, `coTransaksiUi.php`).
* **[controllers](file:///z:/san_sarana_7jul/application/modules/penjualan/controllers)**: Alur logika transaksi utama.
* **[models](file:///z:/san_sarana_7jul/application/modules/penjualan/models)**: Logika interaksi database, termasuk `MdlPenjualanTransaksi.php` dan `MdlCrmDataBridge.php`.

---

## 2. Alur Pemrosesan CRM ke Order Murni via CLI

Untuk menjalankan proses pembuatan order dari CRM secara stateless (independen dari browser/web server), kita memotong ketergantungan pada controller `Create.php` dan session global. 

Semua parameter formulir detail (seperti *Term of Shipment*, *Payment Method*, dsb.) dikirim via argumen berformat JSON melalui console/CLI. Logika ini akan:
1. Mengambil data item order CRM dari database menggunakan `MdlCrmDataBridge`.
2. Mengambil data konsumen pembeli menggunakan `MdlCustomer`.
3. Menyusun seluruh data ke dalam satu struktur array lokal `$txData`.
4. Menggunakan model `MdlPenjualanTransaksi` secara langsung untuk melakukan operasi insert transaksi baru ke MySQL/MongoDB.

---

## 3. Implementasi CLI Controller Murni (`Cli.php`)

Buat file baru di: `application/modules/penjualan/controllers/Cli.php`

```php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Cli extends MX_Controller 
{
    private $jenisTr = "sales_pre_order"; // Jenis transaksi default

    public function __construct()
    {
        parent::__construct();
        
        // Proteksi keamanan: Hanya izinkan eksekusi via CLI/Console terminal
        if (!$this->input->is_cli_request()) {
            show_404();
            exit;
        }
    }

    /**
     * Memproses Order CRM menjadi Order Baru Penjualan Tanpa $_SESSION
     * 
     * Perintah CLI:
     * php index.php penjualan/cli/process_crm_to_order/[estimate_id]/[operator_user_id]/[json_options]
     * 
     * Contoh json_options:
     * '{"paymentMethod":"credit","tos":"LOCAL","shipping_date":"2026-07-07","due_date":"2026-08-06","capacity":"NON IMPORT","shipping_service":"NONE"}'
     */
    public function process_crm_to_order($estimate_id = NULL, $operator_id = NULL, $json_options = '{}')
    {
        if (empty($estimate_id) || empty($operator_id)) {
            echo "Error: Parameter tidak lengkap.\n";
            echo "Format: php index.php penjualan/cli/process_crm_to_order [estimate_id] [operator_id] [json_options]\n";
            exit(1);
        }

        $options = json_decode($json_options, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            echo "Warning: Format JSON Options tidak valid. Menggunakan opsi default.\n";
            $options = array();
        }

        echo "==================================================\n";
        echo "STARTING CLI CRM PROCESS FOR ESTIMATE ID: $estimate_id\n";
        echo "==================================================\n";

        // -------------------------------------------------------------------------
        // LANGKAH 1: Validasi Operator User (Pencatatan Audit Trail)
        // -------------------------------------------------------------------------
        echo "[1/5] Memeriksa Operator ID: $operator_id...\n";
        $this->db->where('id', $operator_id);
        $user = $this->db->get('user')->row_array();
        if (!$user) {
            echo "Error: Operator dengan ID $operator_id tidak ditemukan.\n";
            exit(1);
        }

        // -------------------------------------------------------------------------
        // LANGKAH 2: Mengambil Data Order & Item CRM
        // -------------------------------------------------------------------------
        echo "[2/5] Mengambil data dari CRM Data Bridge...\n";
        $this->load->model("Mdls/MdlCrmDataBridge");
        $this->load->model("Mdls/MdlCustomer");

        $m = new MdlCrmDataBridge();
        $m->addFilter("qty_saldo > '0'");
        $m->addFilter("estimate_id = '$estimate_id'");
        $crmItems = $m->lookUpAll()->result();

        if (empty($crmItems)) {
            echo "Error: Data CRM tidak ditemukan atau barang sudah di-followup.\n";
            exit(1);
        }

        $customer_id = $crmItems[0]->customer_id;
        $c = new MdlCustomer();
        $customerData = $c->lookupByID($customer_id)->row_array();
        if (!$customerData) {
            echo "Error: Konsumen dengan ID $customer_id tidak ditemukan.\n";
            exit(1);
        }

        // -------------------------------------------------------------------------
        // LANGKAH 3: Penentuan Opsi/Pilihan Form (Lokal Variabel)
        // -------------------------------------------------------------------------
        echo "[3/5] Mengatur opsi parameter transaksi...\n";

        $shipping_date = isset($options['shipping_date']) ? $options['shipping_date'] : date('Y-m-d');
        
        if (isset($options['due_date'])) {
            $due_date = $options['due_date'];
        } else {
            $due_days = isset($customerData['due_days']) ? (int)$customerData['due_days'] : 30;
            $due_date = date('Y-m-d', strtotime("+$due_days days"));
        }

        $payment_method = isset($options['paymentMethod']) ? $options['paymentMethod'] : (($customerData['kredit_limit'] > 0) ? "credit" : "cash");
        $tos = isset($options['tos']) ? $options['tos'] : "LOCAL"; 
        $capacity = isset($options['capacity']) ? $options['capacity'] : "NON IMPORT";
        $shipping_service = isset($options['shipping_service']) ? $options['shipping_service'] : "NONE";
        
        $cbu_cbm = isset($options['cbu_cbm']) ? (float)$options['cbu_cbm'] : 0.0;
        $cbu_kg  = isset($options['cbu_kg'])  ? (float)$options['cbu_kg']  : 0.0;
        $ckd_cbm = isset($options['ckd_cbm']) ? (float)$options['ckd_cbm'] : 0.0;
        $ckd_kg  = isset($options['ckd_kg'])  ? (float)$options['ckd_kg']  : 0.0;
        $utasan  = ($ckd_cbm > 0 || $ckd_kg > 0) ? "CKD" : "CBU";

        // Generate Nomer Booking Baru
        $dtime_now = dtimeNow();
        $dtime_now_ex = explode(" ", $dtime_now);
        $date_now = str_replace("-", "", $dtime_now_ex[0]);
        $time_now = str_replace(":", "", $dtime_now_ex[1]);
        $bookingNumber = $date_now . $time_now;

        // -------------------------------------------------------------------------
        // LANGKAH 4: Menyusun Array Data Transaksi ($txData)
        // -------------------------------------------------------------------------
        echo "[4/5] Menyusun struktur data transaksi lokal...\n";

        $txData = array();

        // 1. MASTER HEADER
        $txData['master'] = array(
            "nomer" => $bookingNumber,
            "pihak_id" => $customer_id,
            "pihak_nama" => $customerData['nama'],
            "pihak_level" => $customerData['level_id'],
            "jenis" => $this->jenisTr,
            "oleh_id" => $operator_id,
            "oleh_nama" => $user['nama'],
            "cabang_id" => $user['cabang_id'] ?? 1,
            "ref_order" => "crm",
            "ref_order_estimate_id" => $estimate_id,
            "payment_method" => $payment_method,
            "status" => 11,
            "trash" => 0,
            "created_at" => dtimeNow(),
        );

        // 2. MASTER VALUES (Detail Form/TOS/Shipping)
        $txData['master_values'] = array(
            "tos" => $tos,
            "tos_nama" => $tos,
            "shipping_date" => $shipping_date,
            "due_date" => $due_date,
            "shipping_service" => $shipping_service,
            "capacity" => $capacity,
            "utasan" => $utasan,
            "cbu_cbm" => $cbu_cbm,
            "cbu_kg" => $cbu_kg,
            "ckd_cbm" => $ckd_cbm,
            "ckd_kg" => $ckd_kg,
        );

        // 3. DETAIL ITEMS
        $txData['items'] = array();
        $i = 1;
        foreach ($crmItems as $item) {
            $effectiveHarga = ($item->produk_ord_hrg > 0) ? $item->produk_ord_hrg : 0;
            
            if ($effectiveHarga <= 0) {
                // Cari harga lokal dari database jika harga CRM kosong
                $this->db->select("nilai");
                $this->db->from("price");
                $this->db->where("produk_id", $item->produk_id);
                $this->db->where("jenis_value", "jual");
                $this->db->where("cabang_id", CB_ID_PUSAT);
                $this->db->where("trash", 0);
                $this->db->where("status", 1);
                $this->db->order_by("id", "desc");
                $localPriceObj = $this->db->get()->row();
                if ($localPriceObj) {
                    $effectiveHarga = (float)$localPriceObj->nilai;
                }
            }

            $txData['items'][] = array(
                "produk_id" => $item->produk_id,
                "produk_nama" => $item->produk_nama,
                "produk_kode" => $item->produk_kode,
                "qty" => $item->produk_ord_jml,
                "harga" => $effectiveHarga,
                "subtotal" => $item->produk_ord_jml * $effectiveHarga,
                "no" => $i
            );
            $i++;
        }

        // -------------------------------------------------------------------------
        // LANGKAH 5: Menyimpan Langsung ke Database via Model MdlPenjualanTransaksi
        // -------------------------------------------------------------------------
        echo "[5/5] Melakukan proses INSERT ke database secara transaksional...\n";

        $this->db->trans_start();
        try {
            $this->load->model("MdlPenjualanTransaksi");
            $tr = new MdlPenjualanTransaksi();
            
            // Simpan header utama
            $insertID = $tr->writeMainEntries($txData['master']);
            
            if ($insertID) {
                // Simpan atribut values
                foreach ($txData['master_values'] as $key => $val) {
                    $this->db->insert('transaksi_values', array(
                        'transaksi_id' => $insertID,
                        'key' => $key,
                        'value' => $val
                    ));
                }

                // Simpan detail item
                foreach ($txData['items'] as $item) {
                    $itemData = array(
                        'transaksi_id' => $insertID,
                        'produk_id' => $item['produk_id'],
                        'produk_nama' => $item['produk_nama'],
                        'qty' => $item['qty'],
                        'harga' => $item['harga'],
                        'subtotal' => $item['subtotal'],
                        'created_at' => dtimeNow(),
                    );
                    $this->db->insert('transaksi_data', $itemData);
                }
            }

            $this->db->trans_complete();

            if ($this->db->trans_status() === FALSE) {
                throw new Exception("Transaksi Database gagal dijalankan.");
            }

            echo "==================================================\n";
            echo "PROSES SELESAI DENGAN SUKSES\n";
            echo "Transaksi Baru Created ID: $insertID\n";
            echo "Nomor Booking: $bookingNumber\n";
            echo "==================================================\n";

        } catch (Exception $e) {
            $this->db->trans_rollback();
            echo "Error saat menyimpan transaksi: " . $e->getMessage() . "\n";
            exit(1);
        }
    }
}
```

---

## 4. Pemetaan Parameter CLI ke UI Form

| Elemen Form di UI | Kunci JSON (CLI) | Nilai Default (jika kosong) | Tujuan Penyimpanan database |
| :--- | :--- | :--- | :--- |
| **Term Of Shipment** | `tos` | `"LOCAL"` | Tabel `transaksi_values` (key: `tos`) |
| **Payment Method** | `paymentMethod` | `"credit"` / `"cash"` | Kolom `payment_method` di tabel `transaksi` |
| **Shipping Date** | `shipping_date` | Tanggal hari ini (`Y-m-d`) | Tabel `transaksi_values` (key: `shipping_date`) |
| **Due Date** | `due_date` | Hari ini + `due_days` customer | Tabel `transaksi_values` (key: `due_date`) |
| **Capacity** | `capacity` | `"NON IMPORT"` | Tabel `transaksi_values` (key: `capacity`) |
| **Shipping Service** | `shipping_service` | `"NONE"` | Tabel `transaksi_values` (key: `shipping_service`) |
| **CBU CBM / KG** | `cbu_cbm`, `cbu_kg` | `0.0` | Tabel `transaksi_values` (key: `cbu_cbm`/`cbu_kg`) |
| **CKD CBM / KG** | `ckd_cbm`, `ckd_kg` | `0.0` | Tabel `transaksi_values` (key: `ckd_cbm`/`ckd_kg`) |
| **Data Utasan** | Otomatis | `"CKD"` jika CKD > 0, else `"CBU"` | Tabel `transaksi_values` (key: `utasan`) |
| **Auto-Validation** | Otomatis | `"YES"` | Kolom `auto_validation` atau setup internal |
