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

/**
 * ============================================================
 *  SALES TRANSACTION CONTROLLER
 *  Aplikasi Laundry - Manajemen Transaksi Penjualan
 * ============================================================
 *  Fitur:
 *  - CRUD Transaksi (Add / Edit / Delete / History)
 *  - Invoice & Print (Bill, QR Baju, Marker, Bill Produksi)
 *  - Pembayaran (CASH / PANJAR / BELUM BAYAR)
 *  - Cek Stok (View & PDF)
 *  - Status Piutang
 *  - Analisa Pelayanan Per Jam
 *  - Barkot (Barang Kotor)
 *  - Barber (Bill Keluar Hari Ini / Besok / Pending)
 *  - Export Excel
 *  - Edit Tgl Selesai (Admin Only)
 * ============================================================
 */
class Sales_transaction extends CI_Controller
{
    // ----------------------------------------------------------
    //  PROPERTIES
    // ----------------------------------------------------------
    private $table      = 'transaction_group';
    private $controller = 'sales_transaction';

    // ----------------------------------------------------------
    //  CONSTRUCTOR
    // ----------------------------------------------------------
    public function __construct()
    {
        parent::__construct();
        $this->load->helper('az_auth');
        az_check_auth('transactions');
        $this->load->helper('az_crud');
        $this->load->helper('az_config');
    }

    // ==========================================================
    //  1. HALAMAN UTAMA (HISTORY TRANSAKSI)
    // ==========================================================
    function index()
    {
        $this->load->library('AZApp');
        $app  = $this->azapp;
        $crud = $app->add_crud();

        // --- Kolom Tabel ---
        $crud->set_column(array(
            '#',
            azlang('Outlet'),
            azlang('Status'),
            azlang('Invoice Code'),
            azlang('Customer'),
            azlang('Date'),
            'Tgl Selesai',
            'Hal Pembayaran',
            azlang('Total'),
            'Posisi Stok',
            azlang('Action')
        ));
        $crud->set_id($this->controller);
        $crud->set_default_url(true);
        $crud->set_single_filter(false);

        // --- Filter Tanggal ---
        $datetime1 = $app->add_datetime();
        $datetime1->set_id("date_1");
        $datetime1->set_name("date_1");
        $datetime1->set_value(Date("d-m-Y", strtotime('-7 day')));
        $datetime1->set_format("DD-MM-YYYY");
        if ($this->session->userdata('role_name') == 'cashier') {
            $datetime1->set_min_date(Date('Y-m-d', strtotime('-7 day')));
        }
        $datetime1->add_class("con-element-top-filter");
        $datetime1 = $datetime1->render();

        $datetime2 = $app->add_datetime();
        $datetime2->set_id("date_2");
        $datetime2->set_name("date_2");
        $datetime2->set_value(Date("d-m-Y", strtotime("last day of this month")));
        $datetime2->set_format("DD-MM-YYYY");
        $datetime2->add_class("con-element-top-filter");
        $datetime2 = $datetime2->render();

        // --- Filter Outlet ---
        $outlet = az_select_outlet('outlet', 'transaction_group');

        $data['datetime1'] = $datetime1;
        $data['datetime2'] = $datetime2;
        $data['outlet']    = $outlet;

        // --- Callback Total ---
        $callback = "jQuery('#txt_total_price').text(json.total); if (typeof pu_paint_rows === 'function') { pu_paint_rows(); }";
        $crud->set_callback_table_complete($callback);

        $v_filter = $this->load->view("sales_transaction/v_sales_transaction_top_filter", $data, true);

        // --- Tombol Admin (Cek Stok, Barkot, Barber, Piutang, Jam) ---
        $btn_cek_stok = '';
        if ($this->session->userdata('role_name') == 'administrator') {
            $btn_cek_stok  = "&nbsp;<button type='button' class='btn btn-jam-open' style='background:#6f42c1;color:#fff;border:none;'><i class='fa fa-clock-o'></i> Analisa Per Jam</button>";
            $btn_cek_stok .= "&nbsp;<button type='button' class='btn btn-info btn-cek-stok-view'><i class='fa fa-eye'></i> View Cek Stok</button>";
            $btn_cek_stok .= "&nbsp;<button type='button' class='btn btn-barkot-open' style='background:#0f766e;color:#fff;border:none;'><i class='fa fa-recycle'></i> Barkot</button>";
            $btn_cek_stok .= "&nbsp;<button type='button' class='btn btn-barber-open' style='background:#0f766e;color:#fff;border:none;'><i class='fa fa-check-circle'></i> Barber</button>";
            $btn_cek_stok .= "&nbsp;<button type='button' class='btn btn-warning btn-cek-stok-pdf'><i class='fa fa-file-pdf-o'></i> Download PDF Cek Stok</button>";
            $btn_cek_stok .= "&nbsp;<button type='button' class='btn btn-danger btn-piutang-open'><i class='fa fa-money'></i> Status Piutang</button>";
        }

        if (aznav('role_export_transaction')) {
            $btn  = "&nbsp;<button class='btn btn-success btn-export-excel' type='button'><i class='fa fa-file-excel-o'></i> Export</button>";
            $btn .= $btn_cek_stok;
            $crud->set_top_filter_btn($btn);
        } elseif ($btn_cek_stok != '') {
            $crud->set_top_filter_btn($btn_cek_stok);
        }

        $crud->set_top_filter($v_filter);
        $crud = $crud->render();
        $app->add_content($crud);

// ==========================================================
// LEGENDA STATUS HISTORY TRANSAKSI
// ==========================================================
$legend_status = '
<div style="margin-top:12px;padding:10px;background:#f9f9f9;border:1px solid #e5e5e5;border-radius:8px;font-size:11px;line-height:2;">
    <b>Keterangan Status:</b><br>

    <span class="label" style="background:#2980b9;">
        <i class="fa fa-cogs"></i> DIPROSES
    </span>
    DSTB baru dibuat produksi &nbsp;&nbsp;

    <span class="label" style="background:#f39c12;">
        <i class="fa fa-truck"></i> DIANTAR
    </span>
    Sudah diverifikasi driver &nbsp;&nbsp;

    <span class="label" style="background:#27ae60;">
        <i class="fa fa-check"></i> END PROCESS
    </span>
    Sudah diverifikasi kasir &nbsp;&nbsp;

    <span class="label" style="background:#e67e22;">
        <i class="fa fa-exclamation-triangle"></i> ADA PU
    </span>
    Bill punya PU yang belum lengkap &nbsp;&nbsp;

    <span class="label" style="background:#16a085;">
        <i class="fa fa-check-circle"></i> END PROCESS LENGKAP
    </span>
    Semua PU sudah terkirim
</div>';

$app->add_content($legend_status);

        // --- Total Harga (bukan kasir) ---
        $idrole = $this->session->userdata('idrole');
        if ($idrole != '2') {
            $app->add_content("<div id='txt_total_price' style='text-align:right;font-size:20px;'>Total Rp 0</div>");
        }

        // --- Modal Detail + Tombol Print ---
        $btn_modal = array(
            'print-qr-baju'            => "<i class='fa fa-print'></i> Cetak QR Baju",
            'print-marker'             => "<i class='fa fa-print'></i> Marker kecil 50x30",
            'print-marker-old'         => "<i class='fa fa-print'></i> Marker Besar 70x30",
            'print-marker-sedang'      => "<i class='fa fa-print'></i> Marker Sedang 50x40",
            'print-marker-point'       => "<i class='fa fa-print'></i> Cetak Marker Point",
            'print-invoice'            => "<i class='fa fa-print'></i> Cetak Bill Pelanggan",
            'print-invoice-old'        => "<i class='fa fa-print'></i> Cetak Bill Pelanggan (Lama)",
            'print-invoice-production' => "<i class='fa fa-print'></i> Cetak Bill Produksi",
        );

        $modal_detail = $app->add_modal();
        $modal_detail->set_id('detail');
        $modal_detail->set_modal_title(azlang('Detail'));
        $modal_detail->set_modal('');
        $modal_detail->set_action_modal($btn_modal);
        $app->add_content($modal_detail->render());

        // --- Modal Process ---
        $pay_date = $app->add_datetime();
        $pay_date->set_id("pay_date");
        $pay_date->set_name("pay_date");
        $pay_date->add_class('x-focus');
        $pay_date = $pay_date->render();

        $data_process['pay_date'] = $pay_date;
        $v_modal_process = $this->load->view('sales_transaction/v_modal_process', $data_process, true);

        $modal_process = $app->add_modal();
        $modal_process->set_id('process');
        $modal_process->set_modal_title(azlang('Process'));
        $modal_process->set_modal($v_modal_process);
        $modal_process->set_action_modal(array('process' => azlang('Save')));
        $app->add_content($modal_process->render());

        // --- Modal Edit Tgl Selesai (Admin Only) ---
        $dt_selesai = $app->add_datetime();
        $dt_selesai->set_id('duedate_edit');
        $dt_selesai->set_name('duedate_edit');
        $dt_selesai->set_format('DD-MM-YYYY HH:mm:ss');
        $dt_selesai_render = $dt_selesai->render();

        $v_modal_edit_selesai = '
        <form id="form_edit_selesai" class="form-horizontal">
            <input type="hidden" id="id_edit_selesai" name="id_edit_selesai">
            <div class="form-group">
                <label class="control-label col-md-4">Kode Bill</label>
                <div class="col-md-5">
                    <input type="text" class="form-control" id="code_edit_selesai" readonly>
                </div>
            </div>
            <div class="form-group">
                <label class="control-label col-md-4">Tgl Selesai <red>*</red></label>
                <div class="col-md-5">' . $dt_selesai_render . '</div>
            </div>
            <div class="form-group">
                <div class="col-md-12" style="font-size:12px;color:#888;">
                    Perbaiki tanggal &amp; jam selesai bill ini, lalu klik <b>Simpan</b>.
                </div>
            </div>
        </form>';

        $modal_edit_selesai = $app->add_modal();
        $modal_edit_selesai->set_id('edit_selesai');
        $modal_edit_selesai->set_modal_title('Edit Tgl Selesai');
        $modal_edit_selesai->set_modal($v_modal_edit_selesai);
        $modal_edit_selesai->set_action_modal(array('edit-selesai' => 'Simpan'));
        $app->add_content($modal_edit_selesai->render());
    
        // --- Modal Detail NWS (untuk kasir) ---
$app->add_content('
<div class="modal fade" id="modal_nws" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header" style="background:#6f42c1;color:#fff;border-radius:6px 6px 0 0;">
<button type="button" class="close" data-dismiss="modal" style="color:#fff;">&times;</button>
<h4 class="modal-title">📝 Catatan NWS (Note We Are Sorry)</h4>
</div>
<div class="modal-body" id="nws_modal_body" style="max-height:60vh;overflow-y:auto;">Memuat…</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>');

        // --- JS ---
        $this->load->helper('az_core');
        $js = az_add_js('sales_transaction/vjs_list_sales_transaction');
        $app->add_js($js);

        // --- Header ---
        $data_header['title']      = 'History Transaksi';
        $data_header['breadcrumb'] = array('transactions', 'sales_transaction');
        $app->set_data_header($data_header);

        echo $app->render();
    }

    // ==========================================================
    //  2. GET DATA TABEL (AJAX - DataTables)
    // ==========================================================
    public function get()
    {
        $this->load->library('AZApp');
        $crud   = $this->azapp->add_crud();
        $idrole = $this->session->userdata('idrole');

        if ($idrole == '2') {
            $crud->add_where('transaction_group_status != "SELESAI"');
        }

        $crud->set_select("idtransaction_group, outlet_name, dstb_status, code, customer_name, date_format(date, '%d-%m-%Y %H:%i:%s') as date, date_format(duedate, '%d-%m-%Y %H:%i:%s') as duedate, payment_status, grand_total_final, stock_position");
        $crud->set_select_table("idtransaction_group, outlet_name, dstb_status, code, customer_name, date, duedate, payment_status, grand_total_final, stock_position");
        $crud->set_sorting('outlet_name, dstb_status, code, customer_name, date, duedate, payment_status, grand_total_final, stock_position');
        $crud->set_id($this->controller);

        $sess_idoutlet = $this->session->userdata('idoutlet');
        if (strlen($sess_idoutlet) > 0) {
            $crud->add_where('transaction_group.idoutlet = ' . $sess_idoutlet);
        }

        $crud->set_group_by('transaction_group.idtransaction_group');
        $crud->add_join('customer', 'left');
        $crud->add_join('outlet');
        $crud->set_select_align(',,,,,,,right');
        $crud->set_select_decimal('7');
        $crud->add_where("transaction_group.status > 0");
        $crud->set_table($this->table);
        $crud->set_order_by("transaction_group.has_pu_active desc, transaction_group.created desc");
        $crud->set_custom_style('custom_style');

        // --- Parse Top Filter ---
        parse_str($_SERVER['QUERY_STRING'], $str);
        $top_filter = azarr($str, 'topfilter');
        $arr_filter = array();
        foreach ($top_filter as $key => $value) {
            $decode = $this->encrypt->decode($key);
            $arr_filter[$decode] = $value;
        }

        $xdate      = explode('~az~', $arr_filter['date']);
        $start_date = azarr($xdate, 0);
        $end_date   = azarr($xdate, 1);
        $idoutlet   = azarr($arr_filter, 'transaction_group.idoutlet');
        $status     = $arr_filter['dstb_status'];
        $process_status = $arr_filter['process_status'];
        $filter_code    = $arr_filter['transaction_group.code'];
        $idcustomer     = $arr_filter['transaction_group.idcustomer'];

        $xstatus         = explode('~aztpwh~', $status);
        $xprocess_status = explode('~aztpwh~', $process_status);
        $xcustomer       = explode('~aztpwh~', $idcustomer);

        if ($idrole == '2') {
            $crud->add_where('transaction_group_status != "SELESAI"');
        }

        // --- Query Total ---
        $this->db->where('date(date) >=', Date('Y-m-d', strtotime($start_date)));
        $this->db->where('date(date) <=', Date('Y-m-d', strtotime($end_date)));

        if (strlen($idoutlet) > 0) {
            $this->db->where('transaction_group.idoutlet', $idoutlet);
        }
        if (strlen($filter_code) > 0) {
            $this->db->like('transaction_group.code', $filter_code);
        }
        if (strlen($sess_idoutlet) > 0) {
            $this->db->where('transaction_group.idoutlet', $sess_idoutlet);
        }
        if (count($xcustomer) > 1 && strlen(azarr($xcustomer, 1)) > 0) {
            $this->db->where('transaction_group.idcustomer', azarr($xcustomer, 1));
        }
        if (count($xstatus) > 1 && strlen(azarr($xstatus, 1)) > 0) {
            $this->db->where('dstb_status', azarr($xstatus, 1));
        }
        if (count($xprocess_status) > 1 && strlen(azarr($xprocess_status, 1)) > 0) {
            $this->db->where('process_status', azarr($xprocess_status, 1));
        }

        $this->db->select("sum(grand_total_final) as total");
        $this->db->where('transaction_group.status', 1);
        $data_transaction = $this->db->get('transaction_group');
        $total = $data_transaction->row()->total;

        $this->load->helper('az_core');
        $total = azlang('Total') . ': Rp ' . az_thousand_separator_decimal($total);
        if ($idrole == '2') {
            $total = '';
        }

        $table      = $crud->get_table();
        $table      = json_decode($table, true);
        $arr_total  = array('total' => $total);
        $arr_return = array_merge($table, $arr_total);

        echo json_encode($arr_return);
    }

// ==========================================================
//  CUSTOM STYLE (Status Badge & Action Buttons)
// ==========================================================
function custom_style($key, $value, $data)
{
// --- Status Badge ---
if ($key == 'dstb_status') {
$color_map = array(
'BARU'                => '#95a5a6',
'DIJEMPUT'            => '#3498db',
'DIPROSES'            => '#2980b9',
'DIANTAR'             => '#f39c12',
'END PROCESS'         => '#27ae60',
'ADA PU'              => '#e67e22',
'END PROCESS LENGKAP' => '#16a085',
);
$bg = isset($color_map[$value]) ? $color_map[$value] : '#95a5a6';
return "<label class='label' style='background:" . $bg . ";color:#fff;'>" . azlang($value) . "</label>";
}
// --- Action Buttons ---
if ($key == 'action') {
$idtransaction_group = azarr($data, 'idtransaction_group');
$code = azarr($data, 'code');
// ---- BADGE NWS (ungu = belum dibaca kasir) ----
$nws_btn = '';
$this->db->where('idtransaction_group', $idtransaction_group);
$this->db->where('status', 1);
$nws_count = $this->db->count_all_results('nws');
if ($nws_count > 0) {
// UNGU (belum dibaca) jika: belum pernah dibaca, ATAU ada NWS baru setelah terakhir dibaca
$this->db->select('MAX(created) AS last_nws');
$this->db->where('idtransaction_group', $idtransaction_group);
$this->db->where('status', 1);
$row_n = $this->db->get('nws')->row();
$last_nws = $row_n ? $row_n->last_nws : null;
$this->db->select('MAX(created) AS last_read');
$this->db->where('idtransaction_group', $idtransaction_group);
$this->db->where('status', 1);
$row_r = $this->db->get('nws_read')->row();
$last_read = $row_r ? $row_r->last_read : null;
$is_unread = empty($last_read) || (strtotime((string)$last_nws) > strtotime((string)$last_read));
$nws_cls = $is_unread ? 'nws-unread' : 'nws-read';
$nws_btn = '<button type="button" class="btn btn-xs btn-nws-view '.$nws_cls.'" data-id="'.$idtransaction_group.'"><i class="fa fa-sticky-note"></i> NWS ('.$nws_count.')</button> ';
}
// Tombol Detail
$btn = "<button data-code='" . $code . "' class='btn btn-info btn-detail btn-xs' type='button'><i class='fa fa-file-o'></i> " . azlang('Detail') . "</button>";
// Tombol Edit Tgl Selesai (Admin Only)
$btn_edit_selesai = '';
if ($this->session->userdata('role_name') == 'administrator') {
$btn_edit_selesai = '<button class="btn btn-warning btn-xs btn-edit-selesai" type="button" data_id="' . $idtransaction_group . '" data_code="' . $code . '"><span class="glyphicon glyphicon-calendar"></span> Edit Selesai</button>';
}
// Tombol Hapus (Role-based)
$btn_delete = '<button class="btn btn-danger btn-xs btn-delete-sales_transaction" data_id="' . $idtransaction_group . '"><span class="glyphicon glyphicon-remove"></span> ' . azlang('Delete') . '</button>';
if (!aznav('role_delete_transaction')) {
$btn_delete = '';
}
// ---- TOMBOL "DETAIL PU" (masih di DALAM blok action) ----
$btn_pu = '';
static $pu_cache = array();
if (array_key_exists($idtransaction_group, $pu_cache)) {
$pu_row = $pu_cache[$idtransaction_group];
} else {
$this->db->where('idtransaction_group', $idtransaction_group);
$this->db->order_by('est_finish_date asc');
$pu_row = $this->db->get('dstb_clean_pu')->row();
$pu_cache[$idtransaction_group] = $pu_row;
}
if ($pu_row) {
$btn_pu = '<button class="btn btn-warning btn-xs btn-lihat-pu" type="button" '
. 'data_id="' . $idtransaction_group . '" '
. 'data_est="' . htmlspecialchars((string)$pu_row->est_finish_date) . '">'
. '<span class="glyphicon glyphicon-repeat"></span> Detail PU</button>';
}
return $nws_btn . $btn . $btn_edit_selesai . $btn_pu . $btn_delete;
}
return $value;
}
// ==========================================================
//  4. ADD / EDIT TRANSAKSI
// ==========================================================
function add($type = '')
{
    $type = strtolower(trim((string)$type));
    // Tanpa tipe -> tampilkan halaman pilihan (Baju / Linen)
    if (!in_array($type, array('baju', 'linen'))) {
        $this->choose_transaction_type();
        return;
    }
    $this->edit('', $type);
}

// ----------------------------------------------------------
//  HALAMAN PILIHAN JENIS TRANSAKSI (Baju / Linen)
// ----------------------------------------------------------
function choose_transaction_type()
{
    $this->load->library('AZApp');
    $app = $this->azapp;

    $data_header['title']      = 'Transaksi';
    $data_header['breadcrumb'] = array('transaction', 'sales_transaction');
    $app->set_data_header($data_header);

    $view = $this->load->view('sales_transaction/v_choose', '', true);
    $app->add_content($view);

    echo $app->render();
}

function edit($id = '', $type = '')
{
    $this->load->helper('az_core');
    $this->load->library('AZApp');
    $app = $this->azapp;

    // =====================================================
    //  TENTUKAN KATEGORI BARANG (B = Baju, L = Linen)
    // =====================================================
    $b_l  = '';
    $type = strtolower(trim((string)$type));
    if ($type == 'baju')  { $b_l = 'B'; }
    if ($type == 'linen') { $b_l = 'L'; }

    // Edit transaksi lama: deteksi otomatis dari kategori
    // produk barang PERTAMA pada bill (B -> Baju, L -> Linen)
    if (strlen($id) > 0 && strlen($b_l) == 0) {
        $this->db->select('product.b_l');
        $this->db->from('transaction');
        $this->db->join('item', 'transaction.iditem = item.iditem', 'left');
        $this->db->join('product', 'item.idproduct = product.idproduct', 'left');
        $this->db->where('transaction.idtransaction_group', $id);
        $this->db->where('transaction.status', 1);
        $this->db->order_by('transaction.idtransaction', 'asc');
        $first_item = $this->db->get()->row();
        if ($first_item && in_array(strtoupper((string)$first_item->b_l), array('B', 'L'))) {
            $b_l = strtoupper((string)$first_item->b_l);
        }
    }

    $type_label  = ($b_l == 'L') ? 'Linen' : (($b_l == 'B') ? 'Baju' : '');
    $data['b_l'] = $b_l;

    // --- Tanggal Transaksi ---
    $date = $app->add_datetime();
    $date->set_id('date');
    $date->set_value(Date('d-m-Y H:i:s'));
    $date->set_name('date');
    $data['date'] = $date->render();

    // --- Tanggal Selesai (Due Date) ---
    // Baju  : mengikuti config duedate (H+2, perilaku lama)
    // Linen : H+3. Jam selesai keduanya tetap 17:00
    $cfg_due = az_get_config('duedate');
    if ($b_l == 'L') {
        $cfg_due = 3;
    }
    $due     = Date('d-m-Y', strtotime('+ ' . $cfg_due . ' day')) . ' 17:00:00';
    $duedate = $app->add_datetime();
    $duedate->set_id('duedate');
    $duedate->set_name('duedate');
    $duedate->set_value($due);
    $data['duedate'] = $duedate->render();

    // --- Pay Date ---
    $pay_date = $app->add_datetime();
    $pay_date->set_id("pay_date");
    $pay_date->set_name("pay_date");
    $pay_date->add_class('x-focus');
    $data['pay_date'] = $pay_date->render();

    // --- Modal Product (search dibatasi kategori B/L) ---
    $this->load->library('Laundry');
    $v_modal = $this->laundry->get_product($b_l);
    $modal   = $app->add_modal();
    $modal->set_id('product');
    $modal->set_modal_title(azlang('Product'));
    $modal->set_modal("<div class='table-responsive'>" . $v_modal . "</div>");
    $app->add_content($modal->render());

    // --- Modal Customer ---
    $this->db->where('status', 1);
    $data['customer_category'] = $this->db->get('customer_category');
    $birthday = $app->add_datetime();
    $birthday->set_id('birthday');
    $birthday->set_name('birthday');
    $birthday->set_format('DD-MM-YYYY');
    $data['birthday'] = $birthday->render();
    $v_customer = $this->load->view('sales_transaction/v_customer', $data, true);
    $modal_customer = $app->add_modal();
    $modal_customer->set_id('customer');
    $modal_customer->set_action_modal(array('save_customer' => azlang('Save')));
    $modal_customer->set_modal_title(azlang('Add Customer'));
    $modal_customer->set_modal($v_customer);
    $app->add_content($modal_customer->render());

    // --- Modal Payment ---
    $this->db->where('status', 1);
    $this->db->order_by('field(payment_type_name, "CASH")');
    $pdata['payment_type'] = $this->db->get('payment_type');
    $v_payment = $this->load->view('sales_transaction/v_payment', $pdata, true);
    $modal_payment = $app->add_modal();
    $modal_payment->set_id('payment');
    $modal_payment->set_action_modal(array('save-payment' => 'Simpan', 'save-payment-print' => 'Simpan dan Cetak'));
    $modal_payment->set_modal_title('Pembayaran');
    $modal_payment->set_modal($v_payment);
    $app->add_content($modal_payment->render());

    // --- Modal Create Item (jenis barang dibatasi kategori B/L) ---
    $this->db->where('status', 1);
    $data_tmp['colour'] = $this->db->get('colour');
    $this->db->where('status', 1);
    $data_tmp['hue'] = $this->db->get('hue');
    $this->db->where('status', 1);
    $data_tmp['spot'] = $this->db->get('spot');
    $data_tmp['b_l'] = $b_l;
    $this->db->where('status', 1);
    $data_tmp_add['additional_cost'] = $this->db->get('additional_cost');
    $data['tmp_transaction']     = $this->load->view('sales_transaction/v_tmp_transaction', $data_tmp, true);
    $data['tmp_additional_cost'] = $this->load->view('sales_transaction/v_tmp_additional_cost', $data_tmp_add, true);
    $v_item = $this->load->view('sales_transaction/v_item', $data_tmp, true);
    $modal  = $app->add_modal();
    $modal->set_id('create_item');
    $modal->set_action_modal(array('save_item' => 'Simpan'));
    $modal->set_modal_title('Buat Barang Baru');
    $modal->set_modal($v_item);
    $app->add_content($modal->render());

    // --- View Utama ---
    $view = $this->load->view('sales_transaction/v_sales_transaction', $data, true);
    $app->add_content($view);

    // --- JS ---
    $js = az_add_js('sales_transaction/vjs_sales_transaction');
    $app->add_js($js);

    // --- Scanner ---
    $app->add_content("<div class='container-scanner hide'><button class='btn btn-danger btn-sm btn-close-scanner' type='button'>" . azlang('Close') . "</button><br><video class='scanner' id='scanner'></video></div>");
    $js = az_add_js('sales_transaction/vjs_scanner');
    $app->add_js($js);

    // --- Header ---
    $title = azlang('Add Sales Transaction');
    if (strlen($type_label) > 0) {
        $title = 'Tambah Transaksi ' . $type_label;
    }
    if (strlen($id) > 0) {
        $title = azlang('Edit Sales Transaction');
        if (strlen($type_label) > 0) {
            $title = 'Edit Transaksi ' . $type_label;
        }
    }
    $data_header['title']      = $title;
    $data_header['breadcrumb'] = array('transaction', 'sales_transaction');
    $app->set_data_header($data_header);
    echo $app->render();
}
    // ==========================================================
    //  5. SAVE TRANSAKSI
    // ==========================================================
    public function save($type = '')
    {
        $data      = array();
        $data_post = $this->input->post();
        $idpost    = azarr($data_post, 'id' . $this->table);
        $data['sMessage'] = '';

        // --- Validasi ---
        $this->load->library('form_validation');
        $this->form_validation->set_rules('idoutlet', azlang('Outlet'), 'required|trim|max_length[200]');
        $this->form_validation->set_rules('idcustomer', azlang('Customer'), 'required|trim|max_length[200]');

        $payment_status = $this->input->post('payment_status');
        $idpayment      = $this->input->post("idpayment");

        if ($payment_status == 'CASH') {
            $this->form_validation->set_rules('idpayment', 'Jenis Pembayaran', 'required|trim|max_length[200]');
            if ($idpayment == '1') {
                $this->form_validation->set_rules('total_pay', 'Jumlah Uang', 'required|trim|max_length[200]');
            }
        } elseif ($payment_status == 'PANJAR') {
            $this->form_validation->set_rules('idpayment', 'Jenis Pembayaran', 'required|trim|max_length[200]');
            $this->form_validation->set_rules('total_panjar', 'Jumlah Panjar', 'required|trim|max_length[200]');
            if ($idpayment == '1') {
                $this->form_validation->set_rules('total_pay', 'Jumlah Uang', 'required|trim|max_length[200]');
            }
        }

        $err_code    = 0;
        $err_message = '';
        $invoice     = '';

        $total_pay    = $this->input->post('total_pay');
        $total_panjar = $this->input->post('total_panjar');
        $idproduct    = $this->input->post('idproduct');
        $trx_note     = $this->input->post('trx_note');
        $add_cost     = $this->input->post('add_cost');
        $price        = $this->input->post('price');
        $total        = $this->input->post('total');

        if ($this->form_validation->run() == FALSE) {
            $err_code++;
            $err_message = validation_errors();
        }

        // --- Hitung Total PCS ---
        $total_pcs = 0;
        if ($err_code == 0) {
            foreach ((array) $idproduct as $key => $value) {
                if (strlen($value) == 0) {
                    $err_code++;
                    $err_message = 'Harap lengkapi data transaksi';
                } else {
                    $total_pcs++;
                }
            }
        }

        // --- Simpan ke Database ---
        if ($err_code == 0) {
            $this->db->trans_begin();

            $idtransaction = $this->input->post('idtransaction');

            $info_discount         = az_crud_number($this->input->post('info_discount'));
            $info_discount_percent = az_crud_number($this->input->post('info_discount_percent'));
            $info_add_cost         = az_crud_number($this->input->post('info_add_cost'));
            $info_grand_total      = az_crud_number($this->input->post('info_grand_total'));
            $info_total_final      = az_crud_number($this->input->post('info_total_final'));
            $idcustomer            = $this->input->post('idcustomer');
            $idoutlet              = $this->input->post('idoutlet');

            if (strlen($this->session->userdata('idoutlet')) > 0) {
                $idoutlet = $this->session->userdata('idoutlet');
            }

            // --- Data Transaction Group ---
            $data_save = array(
                'idoutlet'               => $idoutlet,
                'idcustomer'             => $idcustomer,
                'duedate'                => az_crud_date($this->input->post('duedate')),
                'pay_date'               => Date('Y-m-d H:i:s'),
                'note'                   => $this->input->post('info_note'),
                'iduser'                 => $this->session->userdata('iduser'),
                'grand_total'            => $info_grand_total,
                'grand_discount'         => $info_discount,
                'grand_discount_percent' => $info_discount_percent,
                'grand_add_cost'         => $info_add_cost,
                'grand_total_final'      => $info_total_final,
                'transaction_group_status' => 'Baru',
                'payment_status'         => $this->input->post('payment_status'),
                'idpayment_type'         => NULL,
                'total_pay'              => 0,
                'total_panjar'           => 0,
                'total_pcs'              => $total_pcs,
                'add_cost_note'          => $this->input->post('add_cost_note'),
            );

            if ($payment_status == 'CASH' || $payment_status == 'PANJAR') {
                $data_save['total_pay']      = az_crud_number($total_pay);
                $data_save['total_panjar']   = az_crud_number($total_panjar);
                $data_save['idpayment_type'] = $this->input->post('idpayment');
            }

            if ($idpayment != '1') {
                $the_total = $info_total_final;
                if ($payment_status == 'PANJAR') {
                    $the_total = az_crud_number($total_panjar);
                }
                $data_save['total_pay'] = $the_total;
            }

            // --- Generate Code (Baru) / Ambil Code (Edit) ---
            if (strlen($idpost) == 0) {
                $data_code = $this->generate_code($idoutlet);
                $invoice   = azarr($data_code, 'code');
                $code_num  = azarr($data_code, 'code_num');
                $data_save['code']     = $invoice;
                $data_save['code_num'] = $code_num;
                $data_save['date']     = Date('Y-m-d H:i:s');
            } else {
                $this->db->where('idtransaction_group', $idpost);
                $invoice = $this->db->get('transaction_group')->row()->code;
            }

            $response_save = az_crud_save($idpost, $this->table, $data_save);
            $err_code      = azarr($response_save, 'err_code');
            $err_message   = azarr($response_save, 'err_message');
            $insert_id     = azarr($response_save, 'insert_id');

            // --- Simpan Detail Transaksi (Per Item) ---
            if ($err_code == 0) {
                foreach ((array) $idproduct as $key => $value) {
                    if (strlen($value) == 0) continue;

                    // Spot, Hue, Colour
                    $idspot = '';
                    $p_idspot = $this->input->post('idspot_' . $key);
                    if (count($p_idspot) > 0) $idspot = implode(',', $p_idspot);

                    $idhue = '';
                    $p_idhue = $this->input->post('idhue_' . $key);
                    if (count($p_idhue) > 0) $idhue = implode(',', $p_idhue);

                    $idcolour = '';
                    $p_idcolour = $this->input->post('idcolour_' . $key);
                    if (count($p_idcolour) > 0) $idcolour = implode(',', $p_idcolour);

                    // Update Item
                    az_crud_save($value, 'item', array('idhue' => $idhue, 'idcolour' => $idcolour));

                    // Ambil Point dari Product
                    $this->db->where('iditem', $value);
                    $this->db->join('product', 'item.idproduct = product.idproduct');
                    $db_item = $this->db->get("item");

                    // Simpan Transaction
                    $arr_transaction = array(
                        'idtransaction_group' => $insert_id,
                        'iditem'              => $value,
                        'qty'                 => 1,
                        'price'               => az_crud_number(azarr($price, $key)),
                        'add_cost'            => az_crud_number(azarr($add_cost, $key)),
                        'total'               => az_crud_number(azarr($total, $key)),
                        'idspot'              => $idspot,
                        'trx_note'            => azarr($trx_note, $key),
                        'point_a'             => $db_item->row()->point_a,
                        'point_b'             => $db_item->row()->point_b,
                        'point_c'             => $db_item->row()->point_c,
                        'point_d'             => $db_item->row()->point_d,
                    );

                    $response_save = az_crud_save(azarr($idtransaction, $key), 'transaction', $arr_transaction);
                    $err_code      = azarr($response_save, 'err_code');
                    $err_message   = azarr($response_save, 'err_message');
                    $idtransaction = azarr($response_save, 'insert_id');

                    // Simpan Additional Cost
                    $idadditional_item = $this->input->post('idadditional_item_' . $key);
                    $add_cost_price    = $this->input->post('add_cost_price_' . $key);

                    foreach ((array) $idadditional_item as $add_key => $add_value) {
                        if (strlen($add_value) == 0) continue;
                        az_crud_save('', 'transaction_additional_cost', array(
                            'idtransaction'     => $idtransaction,
                            'idadditional_cost' => $add_value,
                            'additional_price'  => az_crud_number(azarr($add_cost_price, $add_key)),
                        ));
                    }
                }
            }
        }

        // --- Commit / Rollback ---
        if ($this->db->trans_status() === FALSE || $err_code > 0) {
            $this->db->trans_rollback();
        } else {
            $this->db->trans_commit();
        }

        $data["err_message"] = $err_message;
        $data["err_code"]    = $err_code;
        $data['invoice']     = urlencode($invoice);

        echo json_encode($data);
    }

    // ==========================================================
    //  6. GENERATE KODE BILL
    // ==========================================================
    function generate_code($idoutlet)
    {
        $this->load->helper('az_core');

        $this->db->where('idoutlet', $idoutlet);
        $inv = $this->db->get('outlet')->row()->outlet_code;

        $code_num = 1;
        $this->db->where("idoutlet", $idoutlet);
        $this->db->order_by('code_num desc');
        $last = $this->db->get("transaction_group", 1);
        if ($last->num_rows() > 0) {
            $code_num = $last->row()->code_num + 1;
        }

        $db_num = sprintf('%05d', $code_num);
        $transaction_group_code = 'BILL' . $inv . '-' . $db_num;

        return array('code' => $transaction_group_code, 'code_num' => $code_num);
    }

    // ==========================================================
    //  7. INVOICE (View)
    // ==========================================================
    function invoice()
    {
        $this->load->library('Laundry');
        $code = urldecode($this->input->get('c'));
        $type = $this->input->get('t');

        $return   = $this->laundry->invoice($code);
        $err_code = azarr($return, 'err_code');

        if ($err_code == 0) {
            $this->load->helper('az_config');
            if ($type == 'small') {
                $this->load->view('sales_transaction/v_invoice_small', $return);
            } else {
                $this->load->view('sales_transaction/v_invoice', $return);
            }
        } else {
            redirect(app_url());
        }
    }

    // ==========================================================
    //  8. GET EDIT DATA (AJAX)
    // ==========================================================
    function get_edit()
    {
        $err_code    = 0;
        $err_message = "";
        $idoutlet    = $this->session->userdata('idoutlet');
        $id          = $this->input->post('id');

        $this->db->select('*, transaction_group.idoutlet, outlet_name as ajax_idoutlet, transaction_group.idcustomer, customer_name as ajax_idcustomer, grand_total as info_total, grand_add_cost as info_add_cost, grand_discount as info_discount, grand_discount_percent as info_discount_percent, grand_tax as info_tax, grand_tax_percent as info_tax_percent, grand_total_final as info_total_final, note as info_note');
        $this->db->join('outlet', 'transaction_group.idoutlet = outlet.idoutlet');
        $this->db->join('customer', 'transaction_group.idcustomer = customer.idcustomer');
        $this->db->where('idtransaction_group', $id);
        $this->db->where('transaction_group.status', 1);
        if (strlen($idoutlet) > 0) {
            $this->db->where('transaction_group.idoutlet', $idoutlet);
        }
        $data = $this->db->get('transaction_group');

        if ($data->num_rows() == 0) {
            $err_code++;
            $err_message = 'Data not found';
        }

        $data_group = array();
        $data_transaction_lang = array();
        $data_detail = array();

        if ($err_code == 0) {
            $data_group = azarr($data->result_array(), 0);

            $this->db->select('*, product_type, product_name');
            $this->db->join('product', 'transaction.idproduct = product.idproduct');
            $this->db->where('idtransaction_group', $id);
            $this->db->where('transaction.status', 1);
            $data_transaction = $this->db->get('transaction')->result_array();

            foreach ($data_transaction as $key => $value) {
                $arr_val = array();
                foreach ($value as $vkey => $vvalue) {
                    $arr_val[$vkey] = ($vkey == 'product_type') ? azlang($vvalue) : $vvalue;
                }
                $data_transaction_lang[$key] = $arr_val;
            }

            $this->db->where('idtransaction_group', $id);
            $this->db->where('transaction_detail.status', 1);
            $data_detail = $this->db->get('transaction_detail')->result_array();
        }

        echo json_encode(array(
            'err_code'           => $err_code,
            'err_message'        => $err_message,
            'data'               => $data_group,
            'data_transaction'   => $data_transaction_lang,
            'data_detail'        => $data_detail,
        ));
    }

    // ==========================================================
    //  9. GET INVOICE (AJAX - Detail Modal)
    // ==========================================================
    function get_invoice()
    {
        $this->load->helper('az_core');
        $code = $this->input->post('code');

        $this->load->library('Laundry');
        $data        = $this->laundry->invoice($code);
        $err_code    = azarr($data, 'err_code');
        $err_message = azarr($data, 'err_message');

        $this->load->helper('az_config');
        $view = $this->load->view('sales_transaction/v_invoice', $data, true);

        // Kasir tidak bisa edit bill > 2 hari
        $is_hide_btn = false;
        $trx_date    = azarr($data, 'data');
        $trx_date    = azarr($trx_date, 'date');
        $trx_date    = Date('Y-m-d', strtotime($trx_date));
        $two_day_ago = Date('Y-m-d', strtotime('-2 days'));

        if (strtotime($trx_date) <= strtotime($two_day_ago) && $this->session->userdata('role_name') == 'cashier') {
            $is_hide_btn = true;
        }

        echo json_encode(array(
            'err_code'    => $err_code,
            'err_message' => $err_message,
            'data'        => $view,
            'is_hide_btn' => $is_hide_btn,
        ));
    }

    // ==========================================================
    //  10. DELETE
    // ==========================================================
    public function delete()
    {
        $id = $this->input->post('id');
        az_crud_delete($this->table, $id);
    }

    // ==========================================================
    //  11. SAVE CUSTOMER (dari Modal Transaksi)
    // ==========================================================
    public function save_customer()
    {
        $data      = array();
        $data_post = $this->input->post();
        $idpost    = azarr($data_post, 'idcustomer');
        $data['sMessage'] = '';

        $this->load->library('form_validation');
        $this->form_validation->set_error_delimiters('', '');

        $idoutlet = $this->session->userdata('idoutlet');
        if (strlen($idoutlet) == 0) {
            $this->form_validation->set_rules('idcsoutlet', azlang('Outlet'), 'required|trim');
            $idoutlet = azarr($data_post, 'idcsoutlet');
        }

        $this->form_validation->set_rules('customer_name', azlang('Customer Name'), 'required|trim|max_length[200]');
        $this->form_validation->set_rules('idcustomer_category', 'Kategori Pelanggan', 'required|trim|max_length[200]');

        $err_code    = 0;
        $err_message = '';

        if ($this->form_validation->run() == TRUE) {
            $data_save = array(
                'idoutlet'            => $idoutlet,
                'idcustomer_category' => 11, // reguler
                'customer_name'       => $this->input->post('customer_name'),
                'address'             => $this->input->post('address'),
                'email'               => $this->input->post('email'),
                'phone'               => $this->input->post('phone'),
                'birthday'            => az_crud_date($this->input->post('birthday'), 'Y-m-d'),
            );

            if (strlen($idpost) == 0) {
                $data_code   = az_generate_customer_code($idoutlet);
                $data_save['customer_code']     = azarr($data_code, 'customer_code');
                $data_save['customer_code_num'] = azarr($data_code, 'customer_code_num');
            }

            $response_save = az_crud_save($idpost, 'customer', $data_save);
            $err_code      = azarr($response_save, 'err_code');
            $err_message   = azarr($response_save, 'err_message');
            $insert_id     = azarr($response_save, 'insert_id');

            $data["idcustomer"]    = $insert_id;
            $data["customer_name"] = $this->input->post('customer_name');
        } else {
            $err_code++;
            $err_message = validation_errors();
        }

        $data["sMessage"] = $err_message;
        echo json_encode($data);
    }

    // ==========================================================
    //  12. PROCESS (Update Status Pembayaran)
    // ==========================================================
    function process()
    {
        $idtransaction_group    = $this->input->post('idtransaction_group');
        $pay                    = $this->input->post('pay');
        $pay_date               = $this->input->post('pay_date');
        $transaction_group_status = $this->input->post('transaction_group_status');

        $err_code    = 0;
        $err_message = '';

        if ($pay == 'PAID' && strlen($pay_date) == 0) {
            $err_code++;
            $err_message = azlang("Pay Date Required");
        }

        if ($err_code == 0) {
            $arr_data = array(
                'pay'                      => $pay,
                'pay_date'                 => az_crud_date($pay_date),
                'transaction_group_status' => $transaction_group_status,
            );
            if ($pay == 'NOT PAID YET') {
                $arr_data['pay_date'] = NULL;
            }
            $this->db->where('idtransaction_group', $idtransaction_group);
            $this->db->update('transaction_group', $arr_data);
        }

        echo json_encode(array('err_code' => $err_code, 'err_message' => $err_message));
    }

    // ==========================================================
    //  13. HELPER AJAX (Detail, Customer, Additional, Product)
    // ==========================================================
    function get_detail()
    {
        $id = $this->input->post('id');
        $this->db->select('pay, date_format(pay_date, "%d-%m-%Y %H:%i:%s") as pay_date, transaction_group_status');
        $this->db->where('idtransaction_group', $id);
        $data = $this->db->get('transaction_group')->result_array();
        echo json_encode($data[0]);
    }

    function get_customer_detail()
    {
        $id = $this->input->post('id');
        $this->db->where('idcustomer', $id);
        $this->db->join('customer_category', 'customer.idcustomer_category = customer_category.idcustomer_category');
        $data = $this->db->get('customer');

        $phone = $address = $category = '';
        if ($data->num_rows() > 0) {
            $phone    = $data->row()->phone;
            $address  = $data->row()->address;
            $category = $data->row()->customer_category_name;
        }

        echo json_encode(array('phone' => $phone, 'address' => $address, 'category' => $category));
    }

    function get_additional_price()
    {
        $this->load->helper('az_core');
        $id    = $this->input->post('id');
        $price = '';
        $this->db->where('idadditional_cost', $id);
        $data = $this->db->get('additional_cost');
        if ($data->num_rows() > 0) {
            $price = $data->row()->price;
        }
        echo json_encode(array('price' => az_thousand_separator_decimal($price)));
    }

    function choose_product()
{
    $id  = $this->input->post('id');
    $b_l = strtoupper(trim((string)$this->input->post('b_l')));

    $this->db->where('item_code', $id);
    $this->db->join('customer', 'item.idcustomer = customer.idcustomer');
    $this->db->join('product', 'item.idproduct = product.idproduct');
    $data = $this->db->get('item');

    $result = array(
        'idproduct' => '', 'price' => '', 'name' => '',
        'idcolour' => '', 'idhue' => '', 'idspot' => '',
        'trx_note' => '', 'idcustomer' => '', 'customer_name' => '',
        'err_code' => 0, 'err_message' => '',
    );

    if ($data->num_rows() > 0) {
        $row     = $data->row();
        $item_bl = strtoupper((string)$row->b_l);

        // ---- Barang harus sesuai kategori form (B/L) ----
        if (in_array($b_l, array('B', 'L')) && $item_bl != $b_l) {
            $result['err_code']    = 1;
            $result['err_message'] = 'Barang ' . $row->item_code . ' berkategori "' . $item_bl . '" (' . ($item_bl == 'L' ? 'Linen' : 'Baju') . '). Silakan gunakan form Transaksi ' . ($item_bl == 'L' ? 'Linen' : 'Baju') . '.';
        } else {
            $result = array(
                'idproduct'     => $row->iditem,
                'price'         => $row->sell_price,
                'name'          => $row->item_code . ' - ' . $row->product_name,
                'idcolour'      => $row->idcolour,
                'idhue'         => $row->idhue,
                'idspot'        => $row->idspot_item,
                'trx_note'      => $row->trx_note_item,
                'idcustomer'    => $row->idcustomer,
                'customer_name' => $row->customer_code . ' - ' . $row->customer_name,
                'err_code'      => 0,
                'err_message'   => '',
            );
        }
    }

    echo json_encode($result);
}

 // ==========================================================
//  14. PRINT BILL (Semua Jenis)  +  toolbar Cetak manual
//      (fallback untuk WebView/web2app tablet yang memblokir
//       window.print() otomatis tanpa interaksi user)
// ==========================================================
function print_bill($id, $type = '')
{
    if ($type != 'trx_print') {
        $id = az_decode_url($id);
    }
    if (strlen($id) == 0) {
        show_404();
    }
    $this->load->helper('az_core');
    $this->load->library('Laundry');
    $data         = $this->laundry->invoice('x', $id);
    $data['type'] = $type;
    $view_map = array(
        ''              => 'sales_transaction/v_bill',
        'old'           => 'sales_transaction/v_bill',
        'qr_baju'       => 'sales_transaction/v_qr_baju',
        'marker'        => 'sales_transaction/v_marker',
        'marker_old'    => 'sales_transaction/v_marker_old',
        'marker_sedang' => 'sales_transaction/v_marker_sedang',
        'marker_point'  => 'sales_transaction/v_marker_point',
        'production'    => 'sales_transaction/v_bill_production',
    );

    // --- render view menjadi STRING (jangan langsung echo) ---
    if ($type == 'trx_print') {
        $data = $this->laundry->invoice($id);
        $html = $this->load->view('sales_transaction/v_bill', $data, true);
    } elseif (isset($view_map[$type])) {
        $html = $this->load->view($view_map[$type], $data, true);
    } else {
        $html = $this->load->view('sales_transaction/v_bill_production', $data, true);
    }

    // --- sisipkan toolbar Cetak manual sebelum </body> ---
    $html = $this->_inject_print_toolbar($html);
    echo $html;
}

// ----------------------------------------------------------
//  Toolbar "Cetak / Tutup" (hanya tampil di layar, hilang saat print)
// ----------------------------------------------------------
private function _print_toolbar_html()
{
    return ''
    . '<div id="azPrintBar" class="az-print-bar">'
    .   '<button type="button" class="az-pb-btn az-pb-print" onclick="window.print();">&#128424; Cetak / Print</button>'
    .   '<button type="button" class="az-pb-btn az-pb-close" onclick="window.close();">&#10005; Tutup</button>'
    .   '<span class="az-pb-hint">Ketuk “Cetak”, lalu pilih printer</span>'
    . '</div>'
    . '<style>'
    .   '.az-print-bar{position:fixed;top:0;left:0;right:0;z-index:2147483647;display:flex;align-items:center;gap:8px;padding:8px 10px;background:#0c3b36;color:#fff;font-family:Arial,Helvetica,sans-serif;box-shadow:0 2px 8px rgba(0,0,0,.3);}'
    .   '.az-print-bar .az-pb-btn{border:0;border-radius:8px;padding:10px 14px;font-size:14px;font-weight:700;cursor:pointer;color:#fff;line-height:1;}'
    .   '.az-print-bar .az-pb-print{background:#138074;}'
    .   '.az-print-bar .az-pb-close{background:#56657a;}'
    .   '.az-print-bar .az-pb-hint{font-size:11px;opacity:.85;margin-left:auto;}'
    .   '@media print{ .az-print-bar{display:none !important;} }'
    . '</style>';
}

private function _inject_print_toolbar($html)
{
    $tb = $this->_print_toolbar_html();
    if (stripos($html, '</body>') !== false) {
        $html = str_ireplace('</body>', $tb . '</body>', $html);
    } else {
        $html .= $tb; // jaga-jaga: tempel di akhir bila </body> tak ketemu
    }
    return $html;
}
    // ==========================================================
    //  15. CREATE ITEM (dari Modal Transaksi)
    // ==========================================================
    function create_item()
    {
        $data_post = $this->input->post();
        $this->load->library('form_validation');

        $idoutlet      = $this->input->post('idnoutlet');
        $sess_idoutlet = $this->session->userdata('idoutlet');

        if (strlen($sess_idoutlet) == 0) {
            $this->form_validation->set_rules('idnoutlet', azlang('Outlet'), 'required|trim|max_length[200]');
        } else {
            $idoutlet = $sess_idoutlet;
        }

        $this->form_validation->set_rules('idnproduct_type', 'Jenis Barang', 'required|trim|max_length[200]');
        $this->form_validation->set_rules('idncustomer', 'Pelanggan', 'required|trim|max_length[200]');
        $this->form_validation->set_rules('idcolour[]', 'Warna', 'required|trim|max_length[200]');
        $this->form_validation->set_rules('idhue[]', 'Corak', 'required|trim|max_length[200]');

        $err_code    = 0;
        $err_message = '';
        $code        = '';

        if ($this->form_validation->run() == FALSE) {
            $err_code++;
            $err_message = validation_errors();
        }

        if ($err_code == 0) {
            $idcolour = implode(',', $this->input->post('idcolour'));
            $idhue    = implode(',', $this->input->post('idhue'));
            $idspot   = $this->input->post('idspot');
            if (count($idspot) > 0) $idspot = implode(',', $idspot);

            $data_save = array(
                'idproduct'     => $this->input->post('idnproduct_type'),
                'idcustomer'    => $this->input->post('idncustomer'),
                'idcolour'      => $idcolour,
                'idhue'         => $idhue,
                'idspot_item'   => $idspot,
                'trx_note_item' => $this->input->post("trx_note"),
            );

            $this->load->helper('az_laundry');
            $data_code = az_generate_item_code($idoutlet);
            $code      = azarr($data_code, 'item_code');
            $data_save['item_code']     = $code;
            $data_save['item_code_num'] = azarr($data_code, 'item_code_num');

            $response_save = az_crud_save('', 'item', $data_save);
            $err_code      = azarr($response_save, 'err_code');
            $err_message   = azarr($response_save, 'err_message');
        }

        echo json_encode(array('err_code' => $err_code, 'err_message' => $err_message, 'code' => $code));
    }

    // ==========================================================
    //  16. PRINT REPORT
    // ==========================================================
    function print_report()
    {
        $date_1     = $this->input->post('date_1');
        $date_2     = $this->input->post('date_2');
        $idoutlet   = $this->input->post('idoutlet');
        $status     = $this->input->post('status');
        $idcustomer = $this->input->post('idcustomer');
        $pay        = $this->input->post('pay');

        $data['date_1'] = $date_1;
        $data['date_2'] = $date_2;

        $this->db->where('transaction_group.status', 1);
        $this->db->where('date >= ', Date('Y-m-d', strtotime($date_1)) . ' 00:00:00');
        $this->db->where('date <= ', Date('Y-m-d', strtotime($date_2)) . ' 23:59:59');

        if (strlen($idoutlet) > 0)   $this->db->where('transaction_group.idoutlet', $idoutlet);
        if (strlen($idcustomer) > 0) $this->db->where('transaction_group.idcustomer', $idcustomer);
        if (strlen($status) > 0)     $this->db->where('transaction_group_status', $status);
        if (strlen($pay) > 0)        $this->db->where('pay', $pay);

        $this->db->join('outlet', 'transaction_group.idoutlet = outlet.idoutlet');
        $this->db->join('customer', 'transaction_group.idcustomer = customer.idcustomer', 'left');
        $data['data'] = $this->db->get('transaction_group');

        $this->load->view('sales_transaction/v_print_sales_transaction', $data);
    }

    // ==========================================================
    //  17. EXPORT EXCEL
    // ==========================================================
    function export_excel()
    {
        $date_1         = $this->input->post('date_1');
        $date_2         = $this->input->post('date_2');
        $idoutlet       = $this->input->post('idoutlet');
        $status         = $this->input->post('status');
        $idcustomer     = $this->input->post('idcustomer');
        $filter_code    = $this->input->post('filter_code');
        $process_status = $this->input->post('process_status');

        $data['date_1'] = $date_1;
        $data['date_2'] = $date_2;

        $this->db->where('transaction_group.status', 1);
        $this->db->where('date >= ', Date('Y-m-d', strtotime($date_1)) . ' 00:00:00');
        $this->db->where('date <= ', Date('Y-m-d', strtotime($date_2)) . ' 23:59:59');

        if (strlen($idoutlet) > 0)       $this->db->where('transaction_group.idoutlet', $idoutlet);
        if (strlen($idcustomer) > 0)     $this->db->where('transaction_group.idcustomer', $idcustomer);
        if (strlen($status) > 0)         $this->db->where('dstb_status', $status);
        if (strlen($process_status) > 0) $this->db->where('process_status', $process_status);
        if (strlen($filter_code) > 0)    $this->db->like('code', $filter_code);

        $this->db->join('outlet', 'transaction_group.idoutlet = outlet.idoutlet');
        $this->db->join('customer', 'transaction_group.idcustomer = customer.idcustomer', 'left');
        $this->db->join('payment_type', 'transaction_group.idpayment_type = payment_type.idpayment_type', 'left');
        $this->db->join('payment_type payment_type_finish', 'transaction_group.idpayment_type_finish = payment_type_finish.idpayment_type', 'left');
        $this->db->select('*, payment_type.payment_type_name, payment_type_finish.payment_type_name as payment_type_name_finish');
        $data['data'] = $this->db->get('transaction_group');

        // --- PHPExcel ---
        $this->load->library('AZApp');
        $this->azapp->add_phpexcel();
        $phpexcel = PHPExcel_IOFactory::load(APPPATH . "assets/excel/sales_transaction.xlsx");
        $sheet0   = $phpexcel->setActiveSheetIndex(0);

        $i         = 0;
        $start_row = 5;
        $styleArray11 = array(
            'borders' => array('allborders' => array('style' => PHPExcel_Style_Border::BORDER_THIN))
        );

        $this->load->helper('az_core');
        $sheet0->setCellValue("A2", "Periode: " . $date_1 . ' s/d ' . $date_2);

        foreach ($data['data']->result() as $key => $value) {
            // DSTB Process
            $this->db->where('idtransaction_group', $value->idtransaction_group);
            $this->db->join('dstb_process', 'dstb_process_detail.iddstb_process = dstb_process.iddstb_process');
            $this->db->join('user cashier', 'dstb_process.idcashier = cashier.iduser');
            $this->db->join('user driver', 'dstb_process.iddriver = driver.iduser');
            $this->db->select('dstb_code, dstb_date, cashier.name as cashier_name, driver.name as driver_name, dstb_note');
            $dstb_process = $this->db->get('dstb_process_detail');

            $dp_code = $dp_date = $dp_cashier = $dp_driver = $dp_note = '';
            if ($dstb_process->num_rows() > 0) {
                $dp_code    = $dstb_process->row()->dstb_code;
                $dp_date    = $dstb_process->row()->dstb_date;
                $dp_cashier = $dstb_process->row()->cashier_name;
                $dp_driver  = $dstb_process->row()->driver_name;
                $dp_note    = $dstb_process->row()->dstb_note;
            }

            // DSTB Clean
            $this->db->where('idtransaction_group', $value->idtransaction_group);
            $this->db->join('dstb_clean', 'dstb_clean_detail.iddstb_clean = dstb_clean.iddstb_clean');
            $this->db->join('user cashier', 'dstb_clean.idcashier = cashier.iduser');
            $this->db->join('user driver', 'dstb_clean.iddriver = driver.iduser');
            $this->db->select('dstb_code, dstb_date, cashier.name as cashier_name, driver.name as driver_name, dstb_note');
            $dstb_clean = $this->db->get('dstb_clean_detail');

            $dc_code = $dc_date = $dc_cashier = $dc_driver = $dc_note = '';
            if ($dstb_clean->num_rows() > 0) {
                $dc_code    = $dstb_clean->row()->dstb_code;
                $dc_date    = $dstb_clean->row()->dstb_date;
                $dc_cashier = $dstb_clean->row()->cashier_name;
                $dc_driver  = $dstb_clean->row()->driver_name;
                $dc_note    = $dstb_clean->row()->dstb_note;
            }

            // Phone formatting
            $phone = preg_replace('/\s+/', '', $value->phone);
            if (substr($phone, 0, 1) === "0") {
                $phone = "62" . substr($phone, 1);
            }

            $r = $start_row + $i;
            $sheet0->setCellValue("A" . $r, ($i + 1));
            $sheet0->setCellValue("B" . $r, $value->code);
            $sheet0->setCellValue("C" . $r, $value->date);
            $sheet0->setCellValue("D" . $r, $value->dstb_status);
            $sheet0->setCellValue("E" . $r, $value->customer_name);
            $sheet0->setCellValue("F" . $r, $phone);
            $sheet0->setCellValue("G" . $r, $value->address);
            $sheet0->setCellValue("H" . $r, $value->duedate);
            $sheet0->setCellValue("I" . $r, $value->total_pcs);
            $sheet0->setCellValue("J" . $r, $value->grand_discount);
            $sheet0->setCellValue("K" . $r, $value->grand_add_cost);
            $sheet0->setCellValue("L" . $r, $value->grand_total);
            $sheet0->setCellValue("M" . $r, $value->payment_status);
            $sheet0->setCellValue("N" . $r, $value->total_panjar);
            $sheet0->setCellValue("O" . $r, $value->payment_type_name);
            $sheet0->setCellValue("P" . $r, $value->pickup_date);
            $sheet0->setCellValue("Q" . $r, $value->outlet_name);
            $sheet0->setCellValue("R" . $r, $dp_code);
            $sheet0->setCellValue("S" . $r, $dp_date);
            $sheet0->setCellValue("T" . $r, $dp_cashier);
            $sheet0->setCellValue("U" . $r, $dp_driver);
            $sheet0->setCellValue("V" . $r, $dp_note);
            $sheet0->setCellValue("W" . $r, $dc_code);
            $sheet0->setCellValue("X" . $r, $dc_date);
            $sheet0->setCellValue("Y" . $r, $dc_cashier);
            $sheet0->setCellValue("Z" . $r, $dc_driver);
            $sheet0->setCellValue("AA" . $r, $dc_note);
            $sheet0->setCellValue("AB" . $r, Date('d-m-Y'));
            $sheet0->setCellValue("AC" . $r, $value->stock_position);
            $sheet0->setCellValue("AD" . $r, $value->note);

            $i++;
        }

        $sheet0->getStyle("A" . $start_row . ":AD" . ($start_row + $i - 1))->applyFromArray($styleArray11);

        // Download
        $filename = 'Transaksi.xls';
        header('Content-Type: application/vnd.ms-excel');
        header('Content-Disposition: attachment;filename="' . $filename . '"');
        header('Cache-Control: max-age=0');
        $objWriter = PHPExcel_IOFactory::createWriter($phpexcel, 'Excel5');
        $objWriter->save('php://output');
    }

    // ==========================================================
    //  18. CEK STOK (Admin Only)
    // ==========================================================
    private function _cek_stok_guard()
    {
        if ($this->session->userdata('role_name') != 'administrator') {
            show_404();
        }
    }

    private function _get_cek_stok($date_1, $date_2, $idoutlet)
    {
        $this->db->select('transaction_group.idoutlet, transaction_group.code, transaction_group.date, transaction_group.total_pcs, transaction_group.stock_position, customer.customer_name, outlet.outlet_name');
        $this->db->from('transaction_group');
        $this->db->join('customer', 'transaction_group.idcustomer = customer.idcustomer', 'left');
        $this->db->join('outlet', 'transaction_group.idoutlet = outlet.idoutlet', 'left');
        $this->db->where('transaction_group.status', 1);
        $this->db->where('(transaction_group.pickup_date IS NULL OR transaction_group.pickup_date = "0000-00-00 00:00:00")', NULL, FALSE);

        if (strlen($date_1) > 0)   $this->db->where('date(transaction_group.date) >=', $date_1);
        if (strlen($date_2) > 0)   $this->db->where('date(transaction_group.date) <=', $date_2);
        if (strlen($idoutlet) > 0) $this->db->where('transaction_group.idoutlet', $idoutlet);

        $this->db->order_by('transaction_group.date asc, transaction_group.code asc');
        return $this->db->get()->result();
    }

    private function _prep_cek_stok()
    {
        $this->_cek_stok_guard();

        $date_1   = $this->input->get('date_1');
        $date_2   = $this->input->get('date_2');
        $idoutlet = $this->input->get('idoutlet');
        $q1 = (strlen($date_1) > 0) ? date('Y-m-d', strtotime($date_1)) : '';
        $q2 = (strlen($date_2) > 0) ? date('Y-m-d', strtotime($date_2)) : '';

        $rows = $this->_get_cek_stok($q1, $q2, $idoutlet);

        $groups = array();
        $order  = array();
        $total_bill_global = 0;
        $total_pcs_global  = 0;

        foreach ($rows as $r) {
            $key = (isset($r->idoutlet) && $r->idoutlet !== null && $r->idoutlet !== '') ? $r->idoutlet : '0';
            if (!isset($groups[$key])) {
                $groups[$key] = array(
                    'outlet_id'   => $key,
                    'outlet_name' => (isset($r->outlet_name) && strlen($r->outlet_name) > 0) ? $r->outlet_name : '(TANPA OUTLET)',
                    'rows'        => array(),
                    'total_bill'  => 0,
                    'total_pcs'   => 0,
                );
                $order[] = $key;
            }
            $groups[$key]['rows'][] = $r;
            $groups[$key]['total_bill']++;
            $groups[$key]['total_pcs'] += (int)$r->total_pcs;
            $total_bill_global++;
            $total_pcs_global += (int)$r->total_pcs;
        }

        $group_list = array();
        foreach ($order as $k) { $group_list[] = $groups[$k]; }

        $outlet_title = 'SEMUA OUTLET';
        if (strlen($idoutlet) > 0) {
            $o = $this->db->select('outlet_name')->where('idoutlet', $idoutlet)->get('outlet')->row();
            if ($o) $outlet_title = $o->outlet_name;
        }

        return array(
            'groups'        => $group_list,
            'is_all_outlet' => (strlen($idoutlet) == 0),
            'outlet_title'  => $outlet_title,
            'date_1'        => $date_1,
            'date_2'        => $date_2,
            'tanggal_cetak' => date('d F Y'),
            'total_bill'    => $total_bill_global,
            'total_pcs'     => $total_pcs_global,
        );
    }

    public function cek_stok_view()
    {
        $data = $this->_prep_cek_stok();
        $data['auto_print'] = false;
        $this->load->view('sales_transaction/v_cek_stok', $data);
    }

    public function cek_stok_pdf()
    {
        $data = $this->_prep_cek_stok();
        $data['auto_print'] = true;
        $this->load->view('sales_transaction/v_cek_stok', $data);
    }

    // ==========================================================
    //  19. STATUS PIUTANG (Admin Only)
    // ==========================================================
    private function _get_piutang($date_1, $date_2, $idoutlet)
    {
        $this->db->select('transaction_group.idtransaction_group, transaction_group.idoutlet, transaction_group.idcustomer, transaction_group.code, transaction_group.date, transaction_group.duedate, transaction_group.pickup_date, transaction_group.grand_total_final, transaction_group.total_panjar, transaction_group.total_pay, transaction_group.total_pay_finish, transaction_group.payment_status, transaction_group.transaction_group_status, transaction_group.stock_position, customer.customer_name, customer.phone, outlet.outlet_name');
        $this->db->from('transaction_group');
        $this->db->join('customer', 'transaction_group.idcustomer = customer.idcustomer', 'left');
        $this->db->join('outlet', 'transaction_group.idoutlet = outlet.idoutlet', 'left');
        $this->db->where('transaction_group.status', 1);

        if (strlen($date_1) > 0)   $this->db->where('date(transaction_group.date) >=', $date_1);
        if (strlen($date_2) > 0)   $this->db->where('date(transaction_group.date) <=', $date_2);
        if (strlen($idoutlet) > 0) $this->db->where('transaction_group.idoutlet', $idoutlet);

        $this->db->order_by('transaction_group.date asc, transaction_group.code asc');
        return $this->db->get()->result();
    }

    private function _piutang_calc($r)
    {
        $gt  = (float)$r->grand_total_final;
        $pan = (float)$r->total_panjar;
        $fin = (float)$r->total_pay_finish;
        $ps  = $r->payment_status;

        $sudah = ($ps == 'CASH') ? $gt : ($pan + $fin);
        $sisa  = max(0, $gt - $sudah);
        $is_piutang = ($ps != 'CASH') && ($sisa > 0.5);

        return array('sudah' => $sudah, 'sisa' => $sisa, 'is_piutang' => $is_piutang);
    }

    private function _empty_metrics()
    {
        return array(
            'n_piutang' => 0, 'sum_sisa' => 0, 'sum_tagihan_p' => 0,
            'n_lunas' => 0, 'sum_tagihan_l' => 0,
            'sum_umur' => 0, 'max_umur' => 0, 'n_lewat' => 0, 'sum_sisa_lewat' => 0,
            'n_total' => 0, 'sum_tagihan_total' => 0,
        );
    }

    private function _prep_piutang()
    {
        if ($this->session->userdata('role_name') != 'administrator') { show_404(); }

        $date_1   = $this->input->get('date_1');
        $date_2   = $this->input->get('date_2');
        $idoutlet = $this->input->get('idoutlet');
        $q1 = (strlen($date_1) > 0) ? date('Y-m-d', strtotime($date_1)) : '';
        $q2 = (strlen($date_2) > 0) ? date('Y-m-d', strtotime($date_2)) : '';

        $rows = $this->_get_piutang($q1, $q2, $idoutlet);
        $now  = time();

        $groups = array();
        $order  = array();
        $gm     = $this->_empty_metrics();

        $aging_labels = array('0-7 hari', '8-14 hari', '15-30 hari', '31-60 hari', '> 60 hari');
        $aging = array();
        for ($i = 0; $i < 5; $i++) {
            $aging[$i] = array('label' => $aging_labels[$i], 'n' => 0, 'sum' => 0);
        }

        $cust = array();
        $n_lunas_all = 0;

        foreach ($rows as $r) {
            $c    = $this->_piutang_calc($r);
            $tgl  = (strlen($r->date) && $r->date != '0000-00-00 00:00:00') ? strtotime($r->date) : 0;
            $umur = ($tgl > 0) ? max(0, (int)floor(($now - $tgl) / 86400)) : 0;
            $jt   = (strlen($r->duedate) && $r->duedate != '0000-00-00 00:00:00') ? strtotime($r->duedate) : 0;
            $lewat = ($jt > 0) ? max(0, (int)floor(($now - $jt) / 86400)) : 0;
            $tgl_fmt = ($tgl > 0) ? date('d/m/Y H.i', $tgl) : '';

            $baris = array(
                'code' => $r->code, 'tgl' => $tgl_fmt,
                'umur' => $c['is_piutang'] ? $umur : '-',
                'customer' => $r->customer_name, 'phone' => $r->phone,
                'tagihan' => (float)$r->grand_total_final, 'sudah' => $c['sudah'], 'sisa' => $c['sisa'],
                'stock' => $r->stock_position, 'pay_status' => $r->payment_status,
                'trx_status' => $r->transaction_group_status,
            );

            $key = (isset($r->idoutlet) && $r->idoutlet !== null && $r->idoutlet !== '') ? $r->idoutlet : '0';
            if (!isset($groups[$key])) {
                $groups[$key] = array(
                    'outlet_name' => (strlen($r->outlet_name) > 0 ? $r->outlet_name : '(TANPA OUTLET)'),
                    'piutang' => array(), 'lunas' => array(), 'm' => $this->_empty_metrics(),
                );
                $order[] = $key;
            }

            $M = &$groups[$key]['m'];
            $M['n_total']++; $M['sum_tagihan_total'] += $baris['tagihan'];
            $gm['n_total']++; $gm['sum_tagihan_total'] += $baris['tagihan'];

            if ($c['is_piutang']) {
                $groups[$key]['piutang'][] = $baris;
                $M['n_piutang']++; $M['sum_sisa'] += $c['sisa']; $M['sum_tagihan_p'] += $baris['tagihan'];
                $M['sum_umur'] += $umur; if ($umur > $M['max_umur']) $M['max_umur'] = $umur;
                if ($lewat > 0) { $M['n_lewat']++; $M['sum_sisa_lewat'] += $c['sisa']; }

                $gm['n_piutang']++; $gm['sum_sisa'] += $c['sisa']; $gm['sum_tagihan_p'] += $baris['tagihan'];
                $gm['sum_umur'] += $umur; if ($umur > $gm['max_umur']) $gm['max_umur'] = $umur;
                if ($lewat > 0) { $gm['n_lewat']++; $gm['sum_sisa_lewat'] += $c['sisa']; }

                $bi = ($umur <= 7) ? 0 : (($umur <= 14) ? 1 : (($umur <= 30) ? 2 : (($umur <= 60) ? 3 : 4)));
                $aging[$bi]['n']++; $aging[$bi]['sum'] += $c['sisa'];

                $ck = $r->idcustomer;
                if (!isset($cust[$ck])) { $cust[$ck] = array('customer' => $r->customer_name, 'phone' => $r->phone, 'n' => 0, 'sum' => 0); }
                $cust[$ck]['n']++; $cust[$ck]['sum'] += $c['sisa'];
            } else {
                $groups[$key]['lunas'][] = $baris;
                $M['n_lunas']++; $M['sum_tagihan_l'] += $baris['tagihan'];
                $gm['n_lunas']++; $gm['sum_tagihan_l'] += $baris['tagihan'];
                $n_lunas_all++;
            }
            unset($M);
        }

        // Susun group
        $group_list = array();
        foreach ($order as $k) {
            $g = &$groups[$k];
            $g['m']['avg_umur'] = ($g['m']['n_piutang'] > 0) ? round($g['m']['sum_umur'] / $g['m']['n_piutang'], 1) : 0;
            $g['m']['ratio']    = ($g['m']['sum_tagihan_total'] > 0) ? round($g['m']['sum_sisa'] / $g['m']['sum_tagihan_total'] * 100, 1) : 0;
            $group_list[] = $g;
            unset($g);
        }

        $gm['avg_umur'] = ($gm['n_piutang'] > 0) ? round($gm['sum_umur'] / $gm['n_piutang'], 1) : 0;
        $gm['ratio']    = ($gm['sum_tagihan_total'] > 0) ? round($gm['sum_sisa'] / $gm['sum_tagihan_total'] * 100, 1) : 0;

        // Top 10 penunggak
        usort($cust, function ($a, $b) { return $b['sum'] - $a['sum']; });
        $top = array_slice($cust, 0, 10);

        $outlet_title = 'SEMUA OUTLET';
        if (strlen($idoutlet) > 0) {
            $o = $this->db->select('outlet_name')->where('idoutlet', $idoutlet)->get('outlet')->row();
            if ($o) $outlet_title = $o->outlet_name;
        }

        return array(
            'groups' => $group_list, 'g' => $gm, 'aging' => $aging, 'top' => $top,
            'outlet_title' => $outlet_title, 'date_1' => $date_1, 'date_2' => $date_2,
            'tanggal_cetak' => date('d F Y'), 'n_lunas_all' => $n_lunas_all,
        );
    }

    public function piutang_view()
    {
        $data = $this->_prep_piutang();
        $this->load->view('sales_transaction/v_piutang', $data);
    }

    // ==========================================================
    //  20. EDIT TGL SELESAI (Admin Only)
    // ==========================================================
    function get_selesai()
    {
        if ($this->session->userdata('role_name') != 'administrator') {
            echo json_encode(array('err_code' => 1, 'err_message' => 'Akses ditolak. Hanya administrator.'));
            return;
        }

        $err_code = 0; $err_message = ''; $duedate = '';
        $id = $this->input->post('id');

        if (strlen($id) == 0) { $err_code++; $err_message = 'ID tidak valid'; }

        if ($err_code == 0) {
            $this->db->where('idtransaction_group', $id);
            $row = $this->db->get('transaction_group')->row();
            if (!$row) {
                $err_code++; $err_message = 'Data tidak ditemukan';
            } else {
                $sess_outlet = $this->session->userdata('idoutlet');
                if (strlen($sess_outlet) > 0 && $row->idoutlet != $sess_outlet) {
                    $err_code++; $err_message = 'Akses ditolak';
                } else {
                    $duedate = (strlen($row->duedate) > 0 && $row->duedate != '0000-00-00 00:00:00')
                        ? Date('d-m-Y H:i:s', strtotime($row->duedate)) : '';
                }
            }
        }

        echo json_encode(array('err_code' => $err_code, 'err_message' => $err_message, 'duedate' => $duedate));
    }

    function save_selesai()
    {
        if ($this->session->userdata('role_name') != 'administrator') {
            echo json_encode(array('err_code' => 1, 'err_message' => 'Akses ditolak. Hanya administrator.'));
            return;
        }

        $err_code = 0; $err_message = '';
        $id      = $this->input->post('id');
        $duedate = $this->input->post('duedate');

        if (strlen($id) == 0) { $err_code++; $err_message = 'ID tidak valid'; }
        if ($err_code == 0 && strlen($duedate) == 0) { $err_code++; $err_message = 'Tgl Selesai wajib diisi'; }

        if ($err_code == 0) {
            $sess_outlet = $this->session->userdata('idoutlet');
            if (strlen($sess_outlet) > 0) {
                $this->db->where('idtransaction_group', $id);
                $chk = $this->db->get('transaction_group')->row();
                if (!$chk || $chk->idoutlet != $sess_outlet) {
                    $err_code++; $err_message = 'Akses ditolak';
                }
            }
        }

        if ($err_code == 0) {
            $this->db->where('idtransaction_group', $id);
            $this->db->update('transaction_group', array('duedate' => az_crud_date($duedate)));
        }

        echo json_encode(array('err_code' => $err_code, 'err_message' => $err_message));
    }

// ==========================================================
//  20b. AJAX: DAFTAR BARANG PU (tombol Lihat Barang PU)
// ==========================================================
public function get_pu_list()
{
$this->load->helper('az_config');
$id = $this->input->post('id');
$err_code = 0; $err_message = '';
$items = array();
if (strlen($id) == 0) { $err_code = 1; $err_message = 'ID tidak valid'; }
if ($err_code == 0) {
$pu_url = base_url() . (defined('AZAPP') ? AZAPP : 'application/') . 'assets/pu_proof/';
$this->db->select('dstb_clean_pu.*, product.product_name, item.item_code, transaction.trx_note');
$this->db->from('dstb_clean_pu');
$this->db->join('transaction', 'dstb_clean_pu.idtransaction = transaction.idtransaction', 'left');
$this->db->join('item', 'transaction.iditem = item.iditem', 'left');
$this->db->join('product', 'item.idproduct = product.idproduct', 'left');
$this->db->where('dstb_clean_pu.idtransaction_group', $id);
$this->db->order_by('dstb_clean_pu.iddstb_clean_pu asc');
foreach ($this->db->get()->result() as $r) {
$items[] = array(
'product_name' => (string)$r->product_name,
'item_code' => (string)$r->item_code,
'trx_note' => (string)$r->trx_note,
'reason' => (string)$r->reason,
'photo_url' => (strlen((string)$r->photo_proof) > 0) ? $pu_url . $r->photo_proof : '',
'est' => (string)$r->est_finish_date,
'status' => (int)$r->status,
);
}
}
echo json_encode(array('err_code' => $err_code, 'err_message' => $err_message, 'items' => $items));
}

    // ==========================================================
    //  21. ANALISA PELAYANAN PER JAM (Admin Only)
    // ==========================================================
    private function _azj_tok($v)
    {
        $v = (string)$v;
        if (strpos($v, '~aztpwh~') !== false) {
            $x = explode('~aztpwh~', $v);
            return isset($x[1]) ? trim($x[1]) : '';
        }
        return trim($v);
    }

    private function _azj_cust($v)
    {
        $v = (string)$v;
        if (strpos($v, '~aztpwh~') !== false) {
            $x = explode('~aztpwh~', $v);
            $v = isset($x[1]) ? $x[1] : '';
        }
        $v = trim($v);
        return ctype_digit($v) ? $v : '';
    }

    private function _azj_where($q1, $q2, $idoutlet, $code, $idcustomer, $status, $process, $datecol, $with_sp)
    {
        $this->db->where('tg.status', 1);
        if (strlen($q1) > 0) $this->db->where('date(' . $datecol . ') >=', $q1);
        if (strlen($q2) > 0) $this->db->where('date(' . $datecol . ') <=', $q2);
        if (strlen($idoutlet) > 0) $this->db->where('tg.idoutlet', $idoutlet);
        if (strlen($code) > 0) $this->db->like('tg.code', $code);
        if (strlen($idcustomer) > 0) $this->db->where('tg.idcustomer', $idcustomer);
        if ($with_sp) {
            if (strlen($status) > 0) $this->db->where('tg.dstb_status', $status);
            if (strlen($process) > 0) $this->db->where('tg.process_status', $process);
        }
    }

    public function jam_view()
    {
        if ($this->session->userdata('role_name') != 'administrator') { show_404(); }

        $date_1      = $this->input->get('date_1');
        $date_2      = $this->input->get('date_2');
        $idoutlet    = trim((string)$this->input->get('idoutlet'));
        $filter_code = trim((string)$this->input->get('filter_code'));
        $status      = $this->_azj_tok($this->input->get('status'));
        $process     = $this->_azj_tok($this->input->get('process_status'));
        $idcustomer  = $this->_azj_cust($this->input->get('customer'));

        $q1 = (strlen($date_1) > 0) ? date('Y-m-d', strtotime($date_1)) : '';
        $q2 = (strlen($date_2) > 0) ? date('Y-m-d', strtotime($date_2)) : '';

        // Inisialisasi 15 slot (07..21)
        $hours = range(7, 21);
        $slots = array();
        foreach ($hours as $h) {
            $slots[$h] = array('masuk' => 0, 'ambil' => 0, 'pcs' => 0, 'omzet' => 0.0, 'kas_dpt' => 0.0, 'kas_pick' => 0.0, 'ci' => array(), 'cp' => array(), 'cn' => array());
        }
        $g = array('masuk' => 0, 'ambil' => 0, 'pcs' => 0, 'omzet' => 0.0, 'kas' => 0.0, 'ci' => array(), 'cp' => array(), 'cn' => array(), 'cash_bill' => 0);

        // 1) Transaksi Masuk
        $this->db->select('tg.idtransaction_group, tg.idcustomer, tg.grand_total_final, tg.total_panjar, tg.payment_status, tg.total_pcs, tg.date, customer.created AS c_created');
        $this->db->from('transaction_group tg');
        $this->db->join('customer', 'tg.idcustomer = customer.idcustomer', 'left');
        $this->_azj_where($q1, $q2, $idoutlet, $filter_code, $idcustomer, $status, $process, 'tg.date', true);

        foreach ($this->db->get()->result() as $r) {
            $hh  = (int)date('G', strtotime($r->date));
            $gt  = (float)$r->grand_total_final;
            $pan = (float)$r->total_panjar;
            $cid = $r->idcustomer;

            $g['masuk']++; $g['omzet'] += $gt; $g['pcs'] += (int)$r->total_pcs;
            $g['ci'][$cid] = 1;
            if ($r->payment_status == 'CASH')       { $g['kas'] += $gt; $g['cash_bill']++; }
            elseif ($r->payment_status == 'PANJAR') { $g['kas'] += $pan; }

            if (isset($slots[$hh])) {
                $slots[$hh]['masuk']++; $slots[$hh]['omzet'] += $gt; $slots[$hh]['pcs'] += (int)$r->total_pcs;
                $slots[$hh]['ci'][$cid] = 1;
                if ($r->payment_status == 'CASH')       { $slots[$hh]['kas_dpt'] += $gt; }
                elseif ($r->payment_status == 'PANJAR') { $slots[$hh]['kas_dpt'] += $pan; }

                $cd = (strlen($r->c_created) > 0 && $r->c_created != '0000-00-00 00:00:00') ? date('Y-m-d', strtotime($r->c_created)) : '';
                $td = date('Y-m-d', strtotime($r->date));
                if ($cd !== '' && $cd === $td) { $slots[$hh]['cn'][$cid] = 1; $g['cn'][$cid] = 1; }
            }
        }

        // 2) Pengambilan
        $this->db->select('tg.idtransaction_group, tg.idcustomer, tg.grand_total_final, tg.total_panjar, tg.payment_status, tg.pickup_date');
        $this->db->from('transaction_group tg');
        $this->_azj_where($q1, $q2, $idoutlet, $filter_code, $idcustomer, $status, $process, 'tg.pickup_date', false);
        $this->db->where('tg.pickup_date IS NOT NULL AND tg.pickup_date != "0000-00-00 00:00:00"', NULL, FALSE);

        foreach ($this->db->get()->result() as $r) {
            $hh  = (int)date('G', strtotime($r->pickup_date));
            $gt  = (float)$r->grand_total_final;
            $pan = (float)$r->total_panjar;
            $cid = $r->idcustomer;

            $g['ambil']++;
            $g['cp'][$cid] = 1;
            $pick = 0.0;
            if ($r->payment_status == 'PANJAR')          { $pick = $gt - $pan; }
            elseif ($r->payment_status == 'BELUM BAYAR') { $pick = $gt; }
            $g['kas'] += $pick;

            if (isset($slots[$hh])) {
                $slots[$hh]['ambil']++; $slots[$hh]['cp'][$cid] = 1; $slots[$hh]['kas_pick'] += $pick;
            }
        }

        // 3) Matriks Jenis Pakaian × Jam
        $this->db->select('p.product_name AS jenis, HOUR(tg.date) AS hh, COUNT(*) AS jml');
        $this->db->from('transaction t');
        $this->db->join('item i', 't.iditem = i.iditem', 'left');
        $this->db->join('product p', 'i.idproduct = p.idproduct', 'left');
        $this->db->join('transaction_group tg', 't.idtransaction_group = tg.idtransaction_group');
        $this->_azj_where($q1, $q2, $idoutlet, $filter_code, $idcustomer, $status, $process, 'tg.date', true);
        $this->db->group_by('p.product_name, HOUR(tg.date)');

        $matrix = array(); $jenis_total = array(); $max_cell = 0;
        foreach ($this->db->get()->result() as $r) {
            $nm  = strlen((string)$r->jenis) ? $r->jenis : '(Tanpa Jenis)';
            $hh  = (int)$r->hh;
            $jml = (int)$r->jml;
            if (!isset($matrix[$nm])) $matrix[$nm] = array();
            $matrix[$nm][$hh] = $jml;
            if (!isset($jenis_total[$nm])) $jenis_total[$nm] = 0;
            $jenis_total[$nm] += $jml;
            if ($jml > $max_cell) $max_cell = $jml;
        }
        arsort($jenis_total);

        // Rakit baris per jam
        $rows = array(); $busy = -1; $quiet = -1; $busy_h = -1; $quiet_h = -1;
        foreach ($hours as $h) {
            $s    = $slots[$h];
            $kunj = count($s['ci'] + $s['cp']);
            $cm   = count($s['ci']);
            $cp   = count($s['cp']);
            $kas  = $s['kas_dpt'] + $s['kas_pick'];
            $baru = count($s['cn']);
            $aov  = ($s['masuk'] > 0) ? round($s['omzet'] / $s['masuk']) : null;
            $ratio = ($s['masuk'] > 0) ? round($s['ambil'] / $s['masuk'] * 100) : null;

            $rows[$h] = array(
                'label' => str_pad($h, 2, '0', STR_PAD_LEFT) . ':00 – ' . str_pad($h + 1, 2, '0', STR_PAD_LEFT) . ':00',
                'masuk' => $s['masuk'], 'cm' => $cm, 'ambil' => $s['ambil'], 'cp' => $cp, 'kunj' => $kunj,
                'pcs' => $s['pcs'], 'omzet' => $s['omzet'], 'kas' => $kas, 'baru' => $baru, 'aov' => $aov, 'ratio' => $ratio,
            );

            if ($kunj > $busy) { $busy = $kunj; $busy_h = $h; }
            if ($kunj > 0 && ($quiet < 0 || $kunj < $quiet)) { $quiet = $kunj; $quiet_h = $h; }
        }

        foreach ($rows as $h => &$rw) {
            $rw['is_busy']  = ($busy_h >= 0 && $h == $busy_h && $busy > 0);
            $rw['is_quiet'] = ($quiet_h >= 0 && $h == $quiet_h && $busy > 0 && $quiet_h != $busy_h);
        }
        unset($rw);

        // KPI Global
        $total_kunj = count($g['ci'] + $g['cp']);
        $kpi = array(
            'kunjungan'   => $total_kunj,
            'masuk'       => $g['masuk'],
            'ambil'       => $g['ambil'],
            'pel_unik'    => $total_kunj,
            'pel_baru'    => count($g['cn']),
            'omzet'       => $g['omzet'],
            'kas'         => $g['kas'],
            'pcs'         => $g['pcs'],
            'aov'         => ($g['masuk'] > 0) ? round($g['omzet'] / $g['masuk']) : 0,
            'cash_pct'    => ($g['masuk'] > 0) ? round($g['cash_bill'] / $g['masuk'] * 100) : 0,
            'busy_label'  => ($busy_h >= 0 && $busy > 0) ? $rows[$busy_h]['label'] . ' (' . $busy . ' kunj.)' : '–',
            'quiet_label' => ($quiet_h >= 0 && $busy > 0) ? $rows[$quiet_h]['label'] . ' (' . $quiet . ' kunj.)' : '–',
        );

        $outlet_title = 'SEMUA OUTLET';
        if (strlen($idoutlet) > 0) {
            $o = $this->db->select('outlet_name')->where('idoutlet', $idoutlet)->get('outlet')->row();
            if ($o) $outlet_title = $o->outlet_name;
        }

        $this->load->view('sales_transaction/v_jam', array(
            'rows' => $rows, 'hours' => $hours, 'kpi' => $kpi,
            'matrix' => $matrix, 'jenis_total' => $jenis_total, 'max_cell' => $max_cell,
            'outlet_title' => $outlet_title, 'date_1' => $date_1, 'date_2' => $date_2,
            'tanggal_cetak' => date('d F Y'),
            'f_outlet' => $idoutlet, 'f_code' => $filter_code, 'f_status' => $status,
            'f_process' => $process, 'f_customer' => $idcustomer,
        ));
    }

    // ==========================================================
    //  22. BARKOT - BARANG KOTOR (Admin Only)
    // ==========================================================
    public function barkot_view()
    {
        $data = $this->_prep_barkot();
        $this->load->view('sales_transaction/v_barkot', $data);
    }

    private function _get_barkot($q1, $q2, $idoutlet)
    {
        $this->db->select("tg.idtransaction_group, tg.idoutlet, tg.code, tg.date, tg.duedate, tg.total_pcs, tg.note, customer.customer_name, outlet.outlet_name");
        $this->db->from('transaction_group tg');
        $this->db->join('customer', 'tg.idcustomer = customer.idcustomer', 'left');
        $this->db->join('outlet', 'tg.idoutlet = outlet.idoutlet', 'left');
        $this->db->where('tg.status', 1);
        $this->db->where('tg.dstb_status', 'BARU');
        $this->db->where('(tg.pickup_date IS NULL OR tg.pickup_date = "0000-00-00 00:00:00")', NULL, FALSE);

        if (strlen($q1) > 0) $this->db->where('date(tg.date) >=', $q1);
        if (strlen($q2) > 0) $this->db->where('date(tg.date) <=', $q2);
        if (strlen($idoutlet) > 0) $this->db->where('tg.idoutlet', $idoutlet);

        $this->db->order_by('tg.date asc, tg.code asc');
        return $this->db->get()->result();
    }

    private function _get_barkot_items($ids)
    {
        $map = array();
        if (empty($ids)) return $map;

        $this->db->query("SET SESSION group_concat_max_len = 100000");
        $this->db->select("transaction.idtransaction_group,
            GROUP_CONCAT(DISTINCT product.product_name ORDER BY product.product_name SEPARATOR ', ') AS jenis,
            GROUP_CONCAT(DISTINCT CASE WHEN IFNULL(transaction.trx_note,'') <> '' THEN transaction.trx_note END SEPARATOR ' | ') AS notes");
        $this->db->from('transaction');
        $this->db->join('item', 'transaction.iditem = item.iditem', 'left');
        $this->db->join('product', 'item.idproduct = product.idproduct', 'left');
        $this->db->where_in('transaction.idtransaction_group', $ids);
        $this->db->group_by('transaction.idtransaction_group');

        foreach ($this->db->get()->result() as $r) {
            $map[$r->idtransaction_group] = array('jenis' => (string)$r->jenis, 'notes' => (string)$r->notes);
        }
        return $map;
    }

    private function _empty_bm()
    {
        return array(
            'n_bill' => 0, 'n_pcs' => 0,
            'n_expres' => 0, 'pcs_expres' => 0,
            'n_endap' => 0, 'pcs_endap' => 0,
            'n_expres_endap' => 0, 'pcs_expres_endap' => 0,
            'n_catatan' => 0,
            'n_hari_ini' => 0, 'pcs_hari_ini' => 0,
            'sum_malam' => 0, 'max_malam' => 0, 'avg_malam' => 0,
        );
    }

    private function _prep_barkot()
    {
        if ($this->session->userdata('role_name') != 'administrator') { show_404(); }

        $date_1   = $this->input->get('date_1');
        $date_2   = $this->input->get('date_2');
        $idoutlet = trim((string)$this->input->get('idoutlet'));
        $q1 = (strlen($date_1) > 0) ? date('Y-m-d', strtotime($date_1)) : '';
        $q2 = (strlen($date_2) > 0) ? date('Y-m-d', strtotime($date_2)) : '';

        $rows  = $this->_get_barkot($q1, $q2, $idoutlet);
        $ids   = array();
        foreach ($rows as $r) { $ids[] = $r->idtransaction_group; }
        $items = $this->_get_barkot_items($ids);

        $hari_ini = strtotime(date('Y-m-d'));
        $g        = $this->_empty_bm();
        $aging    = array(
            array('label' => 'Masuk hari ini (0 malam)', 'n' => 0, 'pcs' => 0),
            array('label' => 'Mengendap 1 malam', 'n' => 0, 'pcs' => 0),
            array('label' => 'Mengendap 2 malam', 'n' => 0, 'pcs' => 0),
            array('label' => 'Mengendap ≥ 3 malam', 'n' => 0, 'pcs' => 0),
        );
        $endap = array(
            array('label' => '1 malam', 'n' => 0, 'pcs' => 0),
            array('label' => '2 malam', 'n' => 0, 'pcs' => 0),
            array('label' => '≥ 3 malam', 'n' => 0, 'pcs' => 0),
        );
        $groups = array();
        $order  = array();

        foreach ($rows as $r) {
            $masuk_ts = (strlen($r->date) && $r->date != '0000-00-00 00:00:00') ? strtotime(date('Y-m-d', strtotime($r->date))) : 0;
            $malam    = ($masuk_ts > 0) ? max(0, (int)floor(($hari_ini - $masuk_ts) / 86400)) : 0;
            $sel_ts   = (strlen($r->duedate) && $r->duedate != '0000-00-00 00:00:00') ? strtotime(date('Y-m-d', strtotime($r->duedate))) : 0;
            $diff_sel = ($sel_ts > 0 && $masuk_ts > 0) ? (int)floor(($sel_ts - $masuk_ts) / 86400) : 999;

            $is_expres   = ($diff_sel <= 1);
            $is_endap    = ($malam >= 1);
            $is_hari_ini = ($malam == 0);
            $pcs         = (int)$r->total_pcs;

            $it   = isset($items[$r->idtransaction_group]) ? $items[$r->idtransaction_group] : array('jenis' => '', 'notes' => '');
            $note = trim((string)$r->note);
            $inot = trim((string)$it['notes']);
            $ada_catatan = ($note !== '' || $inot !== '');

            $baris = array(
                'code' => $r->code, 'customer' => $r->customer_name,
                'tgl_masuk' => ($masuk_ts > 0 ? date('d/m/Y H.i', strtotime($r->date)) : '-'),
                'tgl_selesai' => ($sel_ts > 0 ? date('d/m/Y', $sel_ts) : '-'),
                'pcs' => $pcs, 'malam' => $malam,
                'is_expres' => $is_expres, 'is_endap' => $is_endap, 'is_hari_ini' => $is_hari_ini,
                'jenis' => (string)$it['jenis'], 'note' => $note, 'item_notes' => $inot, 'ada_catatan' => $ada_catatan,
            );

            // Global
            $g['n_bill']++; $g['n_pcs'] += $pcs;
            $g['sum_malam'] += $malam; if ($malam > $g['max_malam']) $g['max_malam'] = $malam;
            if ($is_expres) { $g['n_expres']++; $g['pcs_expres'] += $pcs; }
            if ($is_endap) { $g['n_endap']++; $g['pcs_endap'] += $pcs; }
            if ($is_expres && $is_endap) { $g['n_expres_endap']++; $g['pcs_expres_endap'] += $pcs; }
            if ($is_hari_ini) { $g['n_hari_ini']++; $g['pcs_hari_ini'] += $pcs; }
            if ($ada_catatan) { $g['n_catatan']++; }

            $ai = ($malam >= 3) ? 3 : $malam;
            $aging[$ai]['n']++; $aging[$ai]['pcs'] += $pcs;
            if ($is_endap) { $ei = ($malam >= 3) ? 2 : ($malam - 1); $endap[$ei]['n']++; $endap[$ei]['pcs'] += $pcs; }

            // Per Outlet
            $key = (isset($r->idoutlet) && $r->idoutlet !== null && $r->idoutlet !== '') ? $r->idoutlet : '0';
            if (!isset($groups[$key])) {
                $groups[$key] = array(
                    'outlet_name' => (strlen($r->outlet_name) > 0 ? $r->outlet_name : '(TANPA OUTLET)'),
                    'rows' => array(), 'm' => $this->_empty_bm(),
                    'aging' => array(
                        array('label' => '0 malam', 'n' => 0, 'pcs' => 0),
                        array('label' => '1 malam', 'n' => 0, 'pcs' => 0),
                        array('label' => '2 malam', 'n' => 0, 'pcs' => 0),
                        array('label' => '≥ 3 malam', 'n' => 0, 'pcs' => 0),
                    ),
                    'endap' => array(
                        array('label' => '1 malam', 'n' => 0, 'pcs' => 0),
                        array('label' => '2 malam', 'n' => 0, 'pcs' => 0),
                        array('label' => '≥ 3 malam', 'n' => 0, 'pcs' => 0),
                    ),
                );
                $order[] = $key;
            }

            $M = &$groups[$key];
            $M['rows'][] = $baris;
            $M['m']['n_bill']++; $M['m']['n_pcs'] += $pcs;
            $M['m']['sum_malam'] += $malam; if ($malam > $M['m']['max_malam']) $M['m']['max_malam'] = $malam;
            if ($is_expres) { $M['m']['n_expres']++; $M['m']['pcs_expres'] += $pcs; }
            if ($is_endap) { $M['m']['n_endap']++; $M['m']['pcs_endap'] += $pcs; }
            if ($is_expres && $is_endap) { $M['m']['n_expres_endap']++; $M['m']['pcs_expres_endap'] += $pcs; }
            if ($is_hari_ini) { $M['m']['n_hari_ini']++; $M['m']['pcs_hari_ini'] += $pcs; }
            if ($ada_catatan) { $M['m']['n_catatan']++; }
            $M['aging'][$ai]['n']++; $M['aging'][$ai]['pcs'] += $pcs;
            if ($is_endap) { $ei = ($malam >= 3) ? 2 : ($malam - 1); $M['endap'][$ei]['n']++; $M['endap'][$ei]['pcs'] += $pcs; }
            unset($M);
        }

        $g['avg_malam'] = ($g['n_bill'] > 0) ? round($g['sum_malam'] / $g['n_bill'], 1) : 0;

        $group_list = array();
        foreach ($order as $k) {
            $gr = &$groups[$k];
            $gr['m']['avg_malam'] = ($gr['m']['n_bill'] > 0) ? round($gr['m']['sum_malam'] / $gr['m']['n_bill'], 1) : 0;
            $group_list[] = $gr;
            unset($gr);
        }

        $outlet_title = 'SEMUA OUTLET';
        if (strlen($idoutlet) > 0) {
            $o = $this->db->select('outlet_name')->where('idoutlet', $idoutlet)->get('outlet')->row();
            if ($o) $outlet_title = $o->outlet_name;
        }

        return array(
            'groups' => $group_list, 'is_all_outlet' => (strlen($idoutlet) == 0),
            'outlet_title' => $outlet_title, 'date_1' => $date_1, 'date_2' => $date_2,
            'tanggal_cetak' => date('d F Y'), 'g' => $g, 'aging' => $aging, 'endap' => $endap,
        );
    }

// ==========================================================
//  23. BARBER — PANTAUAN PRODUKSI / PRC (Admin Only)
// ==========================================================
//  Logika masuk laporan:
//    BARU      → hanya jika EXPRESS (selesai−masuk ≤ 1 hari)
//    DIJEMPUT  → hanya jika EXPRESS
//    DIPROSES  → SEMUA (express maupun bukan)
//    DIANTAR / END PROCESS / SELESAI → TIDAK ikut
//
//  Warna baris:
//    🔴 Merah  = tgl selesai sudah lewat (terlambat)
//    🟠 Kuning = jatuh tempo hari ini
//    🔵 Biru   = jadwal keluar besok
//    ⚡ Ungu   = express (pill tambahan, lintas status)
// ==========================================================

public function barber_view2()
{
    // Teruskan ke controller Barber supaya logika selalu sama (termasuk besok efektif & libur)
    $qs = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
    redirect('barber/report' . (strlen($qs) ? '?' . $qs : ''));
}

// Alias supaya URL lama tetap jalan
public function barber_view()
{
    $this->barber_view2();
}

// ---------- Ambil jenis pakaian & catatan per item ----------
private function _brb_items($ids)
{
    $map = array();

    if (empty($ids)) {
        return $map;
    }

    $this->db->query("SET SESSION group_concat_max_len = 100000");

    $this->db->select("
        t.idtransaction_group,
        GROUP_CONCAT(DISTINCT p.product_name ORDER BY p.product_name SEPARATOR ', ') AS jenis,
        GROUP_CONCAT(DISTINCT CASE WHEN IFNULL(t.trx_note,'') <> '' THEN t.trx_note END SEPARATOR ' | ') AS item_notes
    ", FALSE);

    $this->db->from('transaction t');
    $this->db->join('item i', 't.iditem = i.iditem', 'left');
    $this->db->join('product p', 'i.idproduct = p.idproduct', 'left');
    $this->db->where_in('t.idtransaction_group', $ids);
    $this->db->group_by('t.idtransaction_group');

    foreach ($this->db->get()->result() as $r) {
        $map[$r->idtransaction_group] = array(
            'jenis' => (string)$r->jenis,
            'notes' => (string)$r->item_notes,
        );
    }

    return $map;
}

// ---------- Top 10 jenis pakaian terbanyak ----------
private function _brb_topjenis($ids)
{
    if (empty($ids)) {
        return array();
    }

    $this->db->select('p.product_name AS name, COUNT(*) AS pcs');
    $this->db->from('transaction t');
    $this->db->join('item i', 't.iditem = i.iditem', 'left');
    $this->db->join('product p', 'i.idproduct = p.idproduct', 'left');
    $this->db->where_in('t.idtransaction_group', $ids);
    $this->db->where('p.product_name IS NOT NULL', NULL, FALSE);
    $this->db->group_by('p.product_name');
    $this->db->order_by('pcs DESC');
    $this->db->limit(10);

    return $this->db->get()->result();
}

// ---------- QUERY: ambil bill BARU / DIJEMPUT / DIPROSES ----------
private function _brb_query($q1, $q2, $idoutlet)
{
    $this->db->select("
        tg.idtransaction_group, tg.idoutlet, tg.code, tg.date, tg.duedate,
        tg.total_pcs, tg.note, tg.dstb_status, tg.pending_description,
        tg.ready_to_deliver, tg.ready_to_deliver_at,
        rb.name AS ready_by_name,
        c.customer_name, c.phone AS customer_phone,
        o.outlet_name,
        (SELECT dc.dstb_code FROM dstb_clean_detail dcd
         JOIN dstb_clean dc ON dc.iddstb_clean = dcd.iddstb_clean AND dc.status = 1
         WHERE dcd.idtransaction_group = tg.idtransaction_group
         ORDER BY dc.iddstb_clean DESC LIMIT 1) AS dstb_clean_code,
        (SELECT dd.dstb_note FROM dstb_clean dd
         JOIN dstb_clean_detail ddd ON ddd.iddstb_clean = dd.iddstb_clean
         WHERE ddd.idtransaction_group = tg.idtransaction_group
         ORDER BY dd.iddstb_clean DESC LIMIT 1) AS dstb_clean_note,
        (SELECT dp.dstb_note FROM dstb_process dp
         JOIN dstb_process_detail dpd ON dpd.iddstb_process = dp.iddstb_process
         WHERE dpd.idtransaction_group = tg.idtransaction_group
         ORDER BY dp.iddstb_process DESC LIMIT 1) AS dstb_process_note
    ", FALSE);
    $this->db->from('transaction_group tg');
    $this->db->join('customer c', 'tg.idcustomer = c.idcustomer', 'left');
    $this->db->join('outlet o', 'tg.idoutlet = o.idoutlet', 'left');
    $this->db->join('user rb', 'rb.iduser = tg.ready_to_deliver_by', 'left');
    $this->db->where('tg.status', 1);
    $this->db->where('(tg.pickup_date IS NULL OR tg.pickup_date = "0000-00-00 00:00:00")', NULL, FALSE);
    $this->db->where_in('tg.dstb_status', array('BARU', 'DIJEMPUT', 'DIPROSES'));
    if (strlen($q1) > 0) $this->db->where('DATE(tg.duedate) >=', $q1);
    if (strlen($q2) > 0) $this->db->where('DATE(tg.duedate) <=', $q2);
    if (strlen($idoutlet) > 0) $this->db->where('tg.idoutlet', $idoutlet);
    $this->db->order_by('tg.duedate ASC, tg.code ASC');
    return $this->db->get()->result();
}

// ---------- PERSIAPAN DATA UNTUK VIEW ----------
private function _brb_prep()
{
    $date_1   = $this->input->get('date_1');
    $date_2   = $this->input->get('date_2');
    $idoutlet = trim((string)$this->input->get('idoutlet'));

    $q1 = (strlen($date_1) > 0) ? date('Y-m-d', strtotime($date_1)) : date('Y-m-d', strtotime('-30 day'));
    $q2 = (strlen($date_2) > 0) ? date('Y-m-d', strtotime($date_2)) : date('Y-m-d', strtotime('+1 day'));

    $today = date('Y-m-d');
    $besok = date('Y-m-d', strtotime('+1 day'));

    $rows  = $this->_brb_query($q1, $q2, $idoutlet);

    $all_ids = array();
    foreach ($rows as $r) { $all_ids[] = $r->idtransaction_group; }
    $items = $this->_brb_items($all_ids);

    // ---- Metrik global ----
    $g = array(
        'n_bill' => 0, 'n_pcs' => 0,
        'n_terlambat' => 0, 'pcs_terlambat' => 0,
        'n_hari_ini' => 0, 'pcs_hari_ini' => 0,
        'n_besok' => 0, 'pcs_besok' => 0,
        'n_express' => 0, 'pcs_express' => 0,
        'n_proses' => 0, 'pcs_proses' => 0,
        'n_menunggu' => 0, 'pcs_menunggu' => 0,
        'n_catatan' => 0, 'n_siap' => 0, 'n_dstb' => 0,
        'sum_telat' => 0, 'max_telat' => 0,
    );

    // ---- Aging ----
    $aging = array(
        array('label' => 'Telat 1 hari',   'n' => 0, 'pcs' => 0),
        array('label' => 'Telat 2-3 hari', 'n' => 0, 'pcs' => 0),
        array('label' => 'Telat 4+ hari',  'n' => 0, 'pcs' => 0),
    );

    $groups = array(); $order = array(); $valid_ids = array();

    foreach ($rows as $r) {
        $st  = (string)$r->dstb_status;
        $pcs = (int)$r->total_pcs;

        $masuk_raw = (strlen($r->date) && $r->date != '0000-00-00 00:00:00') ? $r->date : '';
        $masuk_d   = ($masuk_raw != '') ? date('Y-m-d', strtotime($masuk_raw)) : '';
        $seles_d   = (strlen($r->duedate) && $r->duedate != '0000-00-00 00:00:00')
                   ? date('Y-m-d', strtotime($r->duedate)) : '';

        // ---- Express ----
        $is_express = false;
        if ($masuk_d != '' && $seles_d != '') {
            $diff = (strtotime($seles_d) - strtotime($masuk_d)) / 86400;
            $is_express = ($diff <= 1);
        }

        // BARU/DIJEMPUT hanya jika express
        if (in_array($st, array('BARU', 'DIJEMPUT')) && !$is_express) {
            continue;
        }

        $valid_ids[] = $r->idtransaction_group;

        // ---- Flag kondisi ----
        $is_proses   = ($st == 'DIPROSES');
        $is_menunggu = in_array($st, array('BARU', 'DIJEMPUT'));
        $terlambat   = ($seles_d != '' && $seles_d < $today);
        $hari_ini    = ($seles_d == $today);
        $besok_flag  = ($seles_d == $besok);
        $telat_hari  = 0;
        if ($terlambat) {
            $telat_hari = max(1, (int)floor((strtotime($today) - strtotime($seles_d)) / 86400));
        }

        // ---- STATE AKSI PROSES (field yang hilang!) ----
        $dstb_code = (string)$r->dstb_clean_code;
        $has_dstb  = (strlen($dstb_code) > 0);
        $is_ready  = (!$has_dstb && (int)$r->ready_to_deliver === 1);
        $state     = $has_dstb ? 'C' : ($is_ready ? 'B' : 'A');
        $ready_at  = '';
        if (!empty($r->ready_to_deliver_at) && $r->ready_to_deliver_at != '0000-00-00 00:00:00') {
            $ready_at = date('d/m/Y H:i', strtotime($r->ready_to_deliver_at));
        }
        $ready_by = (string)$r->ready_by_name;

        // kategori warna
        $cat = $terlambat ? 'late' : ($hari_ini ? 'today' : ($besok_flag ? 'besok' : 'flat'));

        // ---- Catatan ----
        $it    = isset($items[$r->idtransaction_group])
               ? $items[$r->idtransaction_group]
               : array('jenis' => '', 'notes' => '');
        $note        = trim((string)$r->note);
        $inote       = trim((string)$it['notes']);
        $dnote_proc  = trim((string)$r->dstb_process_note);
        $dnote_clean = trim((string)$r->dstb_clean_note);
        $pnote       = trim((string)$r->pending_description);

        $notes = array();
        if ($note !== '')        $notes[] = array('i' => '📌', 't' => $note);
        if ($inote !== '')       $notes[] = array('i' => '👕', 't' => $inote);
        if ($dnote_proc !== '')  $notes[] = array('i' => '📋', 't' => 'DSTB Proses: ' . $dnote_proc);
        if ($dnote_clean !== '') $notes[] = array('i' => '📋', 't' => 'DSTB Bersih: ' . $dnote_clean);
        if ($pnote !== '')       $notes[] = array('i' => '⚠', 't' => $pnote);
        $ada_catatan = (count($notes) > 0);

        // ---- Satu baris data (LENGKAP) ----
        $baris = array(
            'id'            => $r->idtransaction_group,
            'code'          => $r->code,
            'customer'      => $r->customer_name,
            'phone'         => (string)$r->customer_phone,
            'tgl_masuk'     => ($masuk_d != '' ? date('d/m/Y', strtotime($masuk_d)) : '-'),
            'tgl_selesai'   => ($seles_d != '' ? date('d/m/Y', strtotime($seles_d)) : '-'),
            'pcs'           => $pcs,
            'dstb'          => $st,
            'terlambat'     => $terlambat,
            'hari_ini'      => $hari_ini,
            'besok'         => $besok_flag,
            'express'       => $is_express,
            'is_proses'     => $is_proses,
            'is_menunggu'   => $is_menunggu,
            'telat_hari'    => $telat_hari,
            'jenis'         => (string)$it['jenis'],
            'notes'         => $notes,
            'ada_catatan'   => $ada_catatan,
            'cat'           => $cat,
            'state'         => $state,
            'ready'         => $is_ready ? 1 : 0,
            'ready_at'      => $ready_at,
            'ready_by_name' => $ready_by,
            'dstb_code'     => $dstb_code,
            'has_dstb'      => $has_dstb,
        );

        // ---- Metrik global ----
        $g['n_bill']++;  $g['n_pcs'] += $pcs;
        if ($terlambat)   { $g['n_terlambat']++; $g['pcs_terlambat'] += $pcs;
                            $g['sum_telat'] += $telat_hari;
                            if ($telat_hari > $g['max_telat']) $g['max_telat'] = $telat_hari; }
        if ($hari_ini)    { $g['n_hari_ini']++;  $g['pcs_hari_ini']  += $pcs; }
        if ($besok_flag)  { $g['n_besok']++;     $g['pcs_besok']     += $pcs; }
        if ($is_express)  { $g['n_express']++;   $g['pcs_express']   += $pcs; }
        if ($is_proses)   { $g['n_proses']++;    $g['pcs_proses']    += $pcs; }
        if ($is_menunggu) { $g['n_menunggu']++;  $g['pcs_menunggu']  += $pcs; }
        if ($ada_catatan) { $g['n_catatan']++; }
        if ($state === 'B') { $g['n_siap']++; }
        if ($state === 'C') { $g['n_dstb']++; }

        // ---- Aging ----
        if ($terlambat) {
            if ($telat_hari == 1)     { $aging[0]['n']++; $aging[0]['pcs'] += $pcs; }
            elseif ($telat_hari <= 3) { $aging[1]['n']++; $aging[1]['pcs'] += $pcs; }
            else                      { $aging[2]['n']++; $aging[2]['pcs'] += $pcs; }
        }

        // ---- Per outlet ----
        $key = (isset($r->idoutlet) && $r->idoutlet !== null && $r->idoutlet !== '')
             ? $r->idoutlet : '0';
        if (!isset($groups[$key])) {
            $groups[$key] = array(
                'outlet_name' => (strlen($r->outlet_name) > 0 ? $r->outlet_name : '(TANPA OUTLET)'),
                'rows' => array(),
                'm' => array(
                    'n_bill' => 0, 'n_pcs' => 0,
                    'n_terlambat' => 0, 'pcs_terlambat' => 0,
                    'n_hari_ini' => 0, 'pcs_hari_ini' => 0,
                    'n_express' => 0, 'pcs_express' => 0,
                    'n_proses' => 0, 'pcs_proses' => 0,
                    'n_menunggu' => 0, 'pcs_menunggu' => 0,
                    'n_catatan' => 0, 'n_siap' => 0, 'n_dstb' => 0,
                ),
            );
            $order[] = $key;
        }
        $M = &$groups[$key];
        $M['rows'][] = $baris;
        $M['m']['n_bill']++;  $M['m']['n_pcs'] += $pcs;
        if ($terlambat)   { $M['m']['n_terlambat']++; $M['m']['pcs_terlambat'] += $pcs; }
        if ($hari_ini)    { $M['m']['n_hari_ini']++;  $M['m']['pcs_hari_ini']  += $pcs; }
        if ($is_express)  { $M['m']['n_express']++;   $M['m']['pcs_express']   += $pcs; }
        if ($is_proses)   { $M['m']['n_proses']++;    $M['m']['pcs_proses']    += $pcs; }
        if ($is_menunggu) { $M['m']['n_menunggu']++;  $M['m']['pcs_menunggu']  += $pcs; }
        if ($ada_catatan) { $M['m']['n_catatan']++; }
        if ($state === 'B') { $M['m']['n_siap']++; }
        if ($state === 'C') { $M['m']['n_dstb']++; }
        unset($M);
    }

    // ---- Sort baris per outlet ----
    $group_list = array();
    foreach ($order as $k) {
        $G = &$groups[$k];
        usort($G['rows'], function ($a, $b) {
            $sa = ($a['state'] === 'C') ? 9 : ($a['terlambat'] ? 0 : ($a['hari_ini'] ? 1 : ($a['besok'] ? 2 : ($a['express'] ? 3 : 4))));
            $sb = ($b['state'] === 'C') ? 9 : ($b['terlambat'] ? 0 : ($b['hari_ini'] ? 1 : ($b['besok'] ? 2 : ($b['express'] ? 3 : 4))));
            if ($sa != $sb) return $sa - $sb;
            if ($a['telat_hari'] != $b['telat_hari']) return $b['telat_hari'] - $a['telat_hari'];
            return strcmp($a['code'], $b['code']);
        });
        $group_list[] = $G;
        unset($G);
    }

    // ---- Sort outlet ----
    usort($group_list, function ($a, $b) {
        if ($b['m']['n_terlambat'] != $a['m']['n_terlambat'])
            return $b['m']['n_terlambat'] - $a['m']['n_terlambat'];
        if ($b['m']['n_hari_ini'] != $a['m']['n_hari_ini'])
            return $b['m']['n_hari_ini'] - $a['m']['n_hari_ini'];
        return strcmp($a['outlet_name'], $b['outlet_name']);
    });

    // ---- Judul outlet ----
    $outlet_title = 'SEMUA OUTLET';
    if (strlen($idoutlet) > 0) {
        $o = $this->db->select('outlet_name')->where('idoutlet', $idoutlet)->get('outlet')->row();
        if ($o) $outlet_title = $o->outlet_name;
    }

    return array(
        'groups'        => $group_list,
        'g'             => $g,
        'aging'         => $aging,
        'top_jenis'     => $this->_brb_topjenis($valid_ids),
        'outlet_title'  => $outlet_title,
        'is_all_outlet' => (strlen($idoutlet) == 0),
        'date_1'        => $date_1,
        'date_2'        => $date_2,
        'tanggal_cetak' => date('d F Y'),
    );
}
// ==========================================================
//  DETAIL NWS UNTUK MODAL HISTORY (kasir siap menjawab pelanggan)
// ==========================================================
public function get_nws_info()
{
    $this->load->helper('az_nws');
    $id = (int)$this->input->post('id');
    if ($id <= 0) { echo json_encode(array('err_code'=>1,'err_message'=>'ID tidak valid')); return; }

    $this->db->select('tg.code, c.customer_name');
    $this->db->from('transaction_group tg');
    $this->db->join('customer c', 'c.idcustomer = tg.idcustomer', 'left');
    $this->db->where('tg.idtransaction_group', $id);
    $tg = $this->db->get()->row();
    if (!$tg) { echo json_encode(array('err_code'=>1,'err_message'=>'Bill tidak ditemukan')); return; }

    // total pakaian dalam bill (untuk label No. x / y)
    $this->db->where('idtransaction_group', $id);
    $this->db->where('status', 1);
    $tot = $this->db->count_all_results('transaction');

    $map    = az_nws_group($id);          // idtransaction => catatan NWS
    $ids    = array_keys($map);
    $photos = az_nws_photos($ids);        // idtransaction => nama file foto  <-- BARU

    $items = array();
    if (count($ids) > 0) {
        // nama jenis barang per num
        $this->db->select('t.idtransaction, p.product_name');
        $this->db->from('transaction t');
        $this->db->join('item i', 'i.iditem = t.iditem', 'left');
        $this->db->join('product p', 'p.idproduct = i.idproduct', 'left');
        $this->db->where_in('t.idtransaction', $ids);
        $prod = array();
        foreach ($this->db->get()->result() as $r) { $prod[$r->idtransaction] = (string)$r->product_name; }

        foreach ($ids as $tid) {
            $notes = $map[$tid];
            $num   = (int)$notes[0]['num'];
            $ph    = isset($photos[$tid]) ? $photos[$tid] : '';   // <-- BARU
            $items[] = array(
                'num'       => $num,
                'tot'       => $tot,
                'product'   => isset($prod[$tid]) ? $prod[$tid] : '',
                'notes'     => $notes,
                'sentence'  => az_nws_sentence($notes),
                'photo'     => $ph,                                                        // <-- BARU
                'photo_url' => ($ph !== '') ? (base_url().AZAPP.'assets/nws/'.$ph) : '',   // <-- BARU
            );
        }
        // urutkan sesuai nomor pakaian
        usort($items, function($a,$b){ return $a['num'] - $b['num']; });
    }

    echo json_encode(array(
        'err_code' => 0,
        'code'     => $tg->code,
        'customer' => $tg->customer_name,
        'items'    => $items,
    ));
}

// ==========================================================
//  24. NWS READ TRACKING — BLOK UNGU PENGINGAT KASIR
// ==========================================================
private function _nws_ensure_read_table()
{
    $this->db->query("CREATE TABLE IF NOT EXISTS `nws_read` (
        `id` INT NOT NULL AUTO_INCREMENT,
        `idtransaction_group` INT NOT NULL,
        `iduser` INT NULL,
        `created` DATETIME NULL,
        `status` INT NOT NULL DEFAULT 1,
        PRIMARY KEY (`id`), KEY `idx_grp` (`idtransaction_group`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8");
}
// Daftar bill yang punya NWS tapi BELUM dibaca kasir
public function nws_unread_map()
{
    $this->_nws_ensure_read_table();
    $sql = "SELECT n.idtransaction_group AS gid
            FROM nws n
            LEFT JOIN nws_read r ON r.idtransaction_group = n.idtransaction_group AND r.status = 1
            WHERE n.status = 1
            GROUP BY n.idtransaction_group
            HAVING MAX(r.created) IS NULL OR MAX(n.created) > MAX(r.created)";
    $unread = array();
    foreach ($this->db->query($sql)->result() as $r) { $unread[] = (int)$r->gid; }
    echo json_encode(array('err_code' => 0, 'unread' => $unread));
}

// Tandai bill sudah dibaca (dipanggil saat tombol NWS ditekan)
public function nws_get()
{
$this->load->helper('az_nws');
$id = (int)$this->input->post('id');
if ($id <= 0) { echo json_encode(array('err_code'=>1,'err_message'=>'ID tidak valid')); return; }
$this->db->select('tg.code, c.customer_name');
$this->db->from('transaction_group tg');
$this->db->join('customer c','c.idcustomer = tg.idcustomer','left');
$this->db->where('tg.idtransaction_group',$id);
$tg = $this->db->get()->row();
if (!$tg) { echo json_encode(array('err_code'=>1,'err_message'=>'Bill tidak ditemukan')); return; }
$this->db->select('tr.idtransaction, pr.product_name');
$this->db->from('transaction tr');
$this->db->join('item i','i.iditem = tr.iditem','left');
$this->db->join('product pr','pr.idproduct = i.idproduct','left');
$this->db->where('tr.idtransaction_group',$id);
$this->db->where('tr.status',1);
$this->db->order_by('tr.idtransaction ASC');
$rows = $this->db->get()->result();
$ids = array(); foreach ($rows as $r) { $ids[] = $r->idtransaction; }
$map = az_nws_group($id); $photos = az_nws_photos($ids);
$h = '<div style="font-size:12px;"><p><b>'.htmlspecialchars($tg->code).'</b> · '.htmlspecialchars($tg->customer_name).'</p>';
$any = false; $n = 0;
foreach ($rows as $r) {
$n++;
if (!isset($map[$r->idtransaction])) continue;
$any = true;
$h .= '<div style="border:1px solid #e3e8ef;border-radius:10px;padding:10px;margin-bottom:8px;background:#fbfcfe;">';
$h .= '<b>No. '.$n.' — '.htmlspecialchars($r->product_name).'</b><ul style="margin:6px 0 6px 18px;">';
foreach ($map[$r->idtransaction] as $nn) {
$h .= '<li>'.htmlspecialchars($nn['kategori'])
.($nn['posisi'] ? ' · di bagian '.htmlspecialchars($nn['posisi']) : '')
.($nn['keterangan'] ? ' · '.htmlspecialchars($nn['keterangan']) : '')
.($nn['note_manual'] ? ' · "'.htmlspecialchars($nn['note_manual']).'"' : '').'</li>';
}
$h .= '</ul>';
if (isset($photos[$r->idtransaction])) {
$h .= '<img src="'.base_url().AZAPP.'assets/nws/'.$photos[$r->idtransaction].'" style="max-width:120px;border-radius:8px;border:1px solid #d9c9f2;">';
}
$h .= '</div>';
}
if (!$any) $h .= '<p>Tidak ada catatan NWS pada bill ini.</p>';
$h .= '</div>';
echo json_encode(array('err_code'=>0,'code'=>$tg->code,'html'=>$h));
}
public function nws_mark_read()
{
$this->db->query("CREATE TABLE IF NOT EXISTS `nws_read` (
`id` INT NOT NULL AUTO_INCREMENT, `idtransaction_group` INT NOT NULL,
`iduser` INT NULL, `created` DATETIME NULL, `status` INT NOT NULL DEFAULT 1,
PRIMARY KEY (`id`), KEY `idx_grp` (`idtransaction_group`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8");
$id = (int)$this->input->post('id');
if ($id <= 0) { echo json_encode(array('err_code'=>1)); return; }
$this->db->where('idtransaction_group',$id); $this->db->where('status',1);
if ($this->db->count_all_results('nws_read') == 0) {
$this->db->insert('nws_read', array('idtransaction_group'=>$id,'iduser'=>$this->session->userdata('iduser'),'created'=>date('Y-m-d H:i:s'),'status'=>1));
}
echo json_encode(array('err_code'=>0));
}
}