403Webshell
Server IP : 121.121.20.254  /  Your IP : 216.73.217.51
Web Server : Microsoft-IIS/10.0
System : Windows NT WEB-SERVER 10.0 build 20348 (Windows Server 2022) AMD64
User : IUSR ( 0)
PHP Version : 8.3.28
Disable Function : NONE
MySQL : ON  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  C:/Program Files/MariaDB 10.6/data/excellentvege/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : C:/Program Files/MariaDB 10.6/data/excellentvege/wp_xyz_ips_short_code.ibd
��������+���??@���������������������������&&������������������������������못�������+���W�����������Pl�?�Pl���f���������+���?���������������������������������i����������������������������������������������������������������������������������������������������������������������������� �������������������i�	
����������������������������������������������������������������������������������������+������������6��E�?���&�?�?2infimumsupremum�!�րpc6���������������s'�
??�<?php
/**
 * DELIVERY ORDER CREATE PAGE
 * Features:
 * - Select Debtor Code
 * - Choose Carton / Basket
 * - Choose UOM (KG / PCS)
 * - Dynamic label changes based on UOM selection
 * - Fill Qty
 * - Fill Kg / Pieces
 * - Select Item from Item Code
 * - Display Total = Qty * Kg
 */

if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">
        Please log in to create Delivery Order.
    </div>';
    return;
}

$rest_nonce = wp_create_nonce('wp_rest');

$list_url  = 'https://website.ipohserver.com/InventorySearch/index.php/delivery-orders/';
$edit_base = 'https://website.ipohserver.com/InventorySearch/index.php/delivery-order-edit/';

$REST_JOB_POST = rest_url('ac/v1/job');
$REST_JOB_BASE = rest_url('ac/v1/job/');

$ajax_url           = admin_url('admin-ajax.php');
$debtor_nonce       = wp_create_nonce('ac_cs_debtor_search');
$item_suggest_nonce = wp_create_nonce('ac_itemcode_suggest');
$default_location   = 'HQ';
?>

<script>
window.AC_SUGGEST = window.AC_SUGGEST || {};
window.AC_SUGGEST.ajaxUrl = "<?php echo esc_js($ajax_url); ?>";
window.AC_SUGGEST.nonce   = "<?php echo esc_js($item_suggest_nonce); ?>";
</script>

<div class="ac-so-wrap do-container"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-edit-base="<?php echo esc_attr($edit_base); ?>"
     data-rest-job-post="<?php echo esc_attr($REST_JOB_POST); ?>"
     data-rest-job-base="<?php echo esc_attr($REST_JOB_BASE); ?>">

  <div class="do-pagehead">
    <a class="back-btn" href="<?php echo esc_url($list_url); ?>">← Back to List</a>
    <h1>Create Delivery Order</h1>
  </div>

  <div class="do-card">
    <div class="do-label">Date</div>
    <input id="ac_do_date" type="date" class="do-input" />

    <input type="hidden" id="ac_do_customer_name" value="" />
    <input type="hidden" id="ac_do_sales_agent" value="" />
    <input type="hidden" id="ac_do_location" value="<?php echo esc_attr($default_location); ?>" />

    <div class="do-label">Debtor Code</div>
    <div class="ac-search-wrapper"
         id="acDoDebtorWrapper"
         data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
         data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
      <input type="text"
             id="acDoDebtorInput"
             class="ac-search-input do-input"
             placeholder="Search debtor code..."
             autocomplete="off" />
      <input type="hidden" id="ac_do_customer" value="" />
      <div class="ac-search-dropdown" id="acDoDebtorDropdown"></div>
    </div>

    <div id="ac_do_status" class="ac-so-status"></div>
  </div>

  <div class="do-card">
    <div class="do-label">Item Code</div>
    <div class="ac-item-search-wrap">
      <input id="ac_do_item_name" type="text" class="do-input" placeholder="Scan / type item" autocomplete="off">
      <input id="ac_do_item" type="hidden" value="">
      <input id="ac_do_item_display" type="hidden" value="">
      <div class="ac-dd" id="ac_do_item_dd" style="display:none;"></div>
    </div>

    <div class="do-label">Type</div>
    <div class="type-toggle" id="ac_do_pack_type_toggle">
      <button type="button" class="type-btn active" data-pack-type="CARTON">Carton</button>
      <button type="button" class="type-btn" data-pack-type="BASKET">Basket</button>
    </div>
    <select id="ac_do_pack_type" style="display:none;">
      <option value="CARTON" selected>Carton</option>
      <option value="BASKET">Basket</option>
    </select>

    <div class="do-label">Unit of Measure (UOM)</div>
    <div class="type-toggle" id="ac_do_uom_toggle">
      <button type="button" class="type-btn active" data-uom="KG">KG</button>
      <button type="button" class="type-btn" data-uom="PCS">PCS</button>
    </div>
    <select id="ac_do_uom" style="display:none;">
      <option value="KG" selected>KG</option>
      <option value="PCS">PCS</option>
    </select>

    <div class="do-label" id="ac_do_weight_label">Weight (KG)</div>
    <input id="ac_do_kg" type="number" min="0" step="1" value="0" class="do-input" placeholder="Enter weight in KG">

    <div class="do-label">Quantity (Qty)</div>
    <input id="ac_do_qty" type="number" min="0" step="1" value="1" class="do-input" placeholder="Enter quantity">

    <input id="ac_do_total" type="hidden" value="0">
    <div class="ac-line-preview" id="ac_do_line_preview" style="display:none;"></div>

    <button id="ac_do_addline" class="do-btn" type="button">+ Add Item</button>
  </div>

  <div class="do-card">
    <div id="ac_do_lines" class="do-list"><b>No items yet</b></div>
  </div>

  <div class="save-wrapper">
    <button class="do-btn" id="ac_do_submit" type="button">Save Delivery Order</button>
  </div>
</div>

<style>
  .do-container{
    max-width:560px;
    margin:0 auto;
    padding:16px;
    font-family:"Segoe UI",Arial,sans-serif;
    background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
    color:#1f2937;
    box-sizing:border-box;
    border-radius:16px;
  }
  .do-pagehead{
    display:flex;
    align-items:center;
    gap:12px;
    flex-wrap:wrap;
    margin-bottom:12px;
  }
  .do-pagehead h1{
    margin:0;
    font-size:clamp(24px,6vw,34px);
    font-weight:800;
    line-height:1.1;
    color:#0f172a;
  }
  .back-btn{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border:1px solid #cfd9d1;
    background:#fff;
    color:#0f172a;
    padding:10px 12px;
    border-radius:10px;
    text-decoration:none;
    font-weight:700;
    font-size:14px;
    min-height:44px;
  }
  .do-card{
    background:#fff;
    border-radius:14px;
    padding:16px;
    margin-bottom:14px;
    box-shadow:0 4px 16px rgba(15,23,42,.05);
    border:1px solid #e2ebe3;
  }
  .do-label{
    font-size:13px;
    color:#4b5563;
    margin-bottom:6px;
    font-weight:700;
  }
  .do-input{
    width:100%;
    min-height:48px;
    font-size:16px;
    padding:12px;
    border-radius:10px;
    border:1px solid #d1d5db;
    margin-bottom:14px;
    box-sizing:border-box;
    background:#fff;
    color:#111;
  }
  .do-input:focus,
  .type-btn:focus,
  .do-btn:focus,
  .del-btn:focus{
    outline:none;
    box-shadow:0 0 0 3px rgba(40,167,69,.18);
  }
  .do-input:focus{
    border-color:#28a745;
  }
  .type-toggle{
    display:flex;
    gap:10px;
    margin-bottom:14px;
  }
  .type-btn{
    flex:1;
    min-height:48px;
    padding:10px 12px;
    border-radius:10px;
    border:1px solid #28a745;
    background:#fff;
    color:#28a745;
    font-size:18px;
    font-weight:700;
    cursor:pointer;
  }
  .type-btn.active{
    background:linear-gradient(90deg,#28a745,#1e7e34);
    color:#fff;
  }
  .do-btn{
    width:100%;
    min-height:52px;
    background:linear-gradient(90deg,#28a745,#1e7e34);
    color:#fff;
    border:none;
    padding:12px 14px;
    border-radius:12px;
    font-size:20px;
    font-weight:800;
    cursor:pointer;
  }
  .do-btn:disabled{
    opacity:.7;
    cursor:not-allowed;
  }
  .save-wrapper{
    padding:0 2px 10px;
  }

  .ac-search-wrapper,
  .ac-item-search-wrap{ position:relative; margin-bottom:14px; }

  .ac-search-dropdown,
  .ac-dd{
    position:absolute;
    left:0;
    right:0;
    top:calc(100% - 10px);
    background:#fff;
    border:1px solid #dcdcdc;
    border-radius:10px;
    z-index:80;
    max-height:260px;
    overflow:auto;
    box-shadow:0 8px 24px rgba(0,0,0,.10);
  }
  .ac-search-dropdown{ display:none; }
  .ac-search-dropdown.active{ display:block; }

  .ac-dropdown-item,
  .ac-dd .item{
    padding:10px 12px;
    cursor:pointer;
    border-bottom:1px solid #f1f1f1;
    display:flex;
    flex-direction:column;
    gap:2px;
    font-size:15px;
  }
  .ac-dropdown-item:last-child,
  .ac-dd .item:last-child{ border-bottom:none; }
  .ac-dropdown-item:hover,
  .ac-dropdown-item.active,
  .ac-dd .item:hover{ background:#f3f9f4; }

  .ac-dropdown-empty,
  .ac-dropdown-error,
  .ac-dd .item small{
    color:#64748b;
    font-size:12px;
  }

  .ac-line-preview{
    display:none;
    background:#f8fafc;
    border:1px solid #e2e8f0;
    border-radius:12px;
    padding:10px 12px;
    margin-bottom:12px;
    color:#0f172a;
  }
  .preview-head,
  .line-top{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:10px;
    margin-bottom:10px;
  }
  .preview-item,
  .line-item{
    font-size:20px;
    font-weight:800;
    color:#0f5132;
    line-height:1.2;
    word-break:break-word;
  }
  .line-badge{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border-radius:999px;
    border:1px solid #bfe3c8;
    background:#eefaf1;
    color:#166534;
    font-size:12px;
    font-weight:800;
    letter-spacing:.04em;
    text-transform:uppercase;
    padding:6px 10px;
    white-space:nowrap;
  }
  .preview-grid,
  .metric-grid{
    display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));
    gap:8px;
  }
  .metric{
    background:#f8fafc;
    border:1px solid #e2e8f0;
    border-radius:10px;
    padding:8px 10px;
  }
  .metric-label{
    color:#64748b;
    font-size:11px;
    font-weight:700;
    letter-spacing:.05em;
    text-transform:uppercase;
    margin-bottom:4px;
  }
  .metric-value{
    color:#0f172a;
    font-size:18px;
    font-weight:800;
    line-height:1;
  }

  .do-list{
    display:grid;
    gap:10px;
  }
  .line-card{
    border:1px solid #e2e8f0;
    border-radius:12px;
    background:#fff;
    padding:12px;
  }
  .no-items{
    border:1px dashed #cbd5e1;
    border-radius:10px;
    background:#f8fafc;
    color:#64748b;
    padding:14px;
    text-align:center;
    font-weight:700;
  }
  .del-btn{
    margin-top:10px;
    min-height:42px;
    background:#ff4d4d;
    color:#fff;
    border:none;
    padding:8px 12px;
    border-radius:8px;
    font-size:14px;
    font-weight:700;
    cursor:pointer;
  }

  .ac-so-status{ display:none; }

  @media (max-width: 768px){
    .do-container{
      padding:12px;
      border-radius:0;
      margin-left:-8px;
      margin-right:-8px;
      max-width:none;
    }
    .do-card{
      padding:14px;
      margin-bottom:12px;
    }
    .preview-item,
    .line-item{ font-size:18px; }
    .metric-value{ font-size:17px; }
    .save-wrapper{
      position:sticky;
      bottom:0;
      z-index:30;
      padding:10px 2px calc(12px + env(safe-area-inset-bottom));
      background:linear-gradient(to top, rgba(239,247,241,.96), rgba(239,247,241,0));
      backdrop-filter:blur(1px);
    }
  }

  @media (max-width: 420px){
    .type-btn{ font-size:16px; }
    .metric-grid,
    .preview-grid{ grid-template-columns:1fr; }
    .line-badge{ font-size:11px; padding:5px 8px; }
  }
</style>


<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

<script>
(function(){
  const wrap = document.querySelector('.ac-so-wrap');
  if(!wrap) return;

  const REST_NONCE    = wrap.dataset.restNonce;
  const EDIT_BASE     = wrap.dataset.editBase;
  const REST_JOB_POST = wrap.dataset.restJobPost;
  const REST_JOB_BASE = wrap.dataset.restJobBase;

  const $ = (id) => document.getElementById(id);
  const state = { lines: [], jobFinished: false, isSubmitting: false };

  function escapeHtml(s){
    if(s===null || s===undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
  }

  function fmtNum(n){
    const x = parseFloat(n);
    return isNaN(x) ? '0.00' : x.toFixed(2);
  }

  function parseWhole(n){
    const x = Number(n);
    if(!isFinite(x) || x < 0) return 0;
    return Math.round(x);
  }

  function fmtWhole(n){
    return String(parseWhole(n));
  }

  function setStatus(msg, type='info'){
    const toastType =
      type === 'success' ? 'success' :
      type === 'error'   ? 'error' :
      type === 'warning' ? 'warning' :
      'info';
    if (window.Swal) {
      Swal.fire({
        toast: true,
        position: 'top-end',
        icon: toastType,
        title: msg.replace(/<[^>]*>/g, ''),
        showConfirmButton: false,
        timer: 1800,
        timerProgressBar: true
      });
    }
  }

  function showToast(icon, title, text=''){
    if (window.Swal) {
      Swal.fire({
        toast: true,
        position: 'top-end',
        icon,
        title,
        text,
        showConfirmButton: false,
        timer: 2600,
        timerProgressBar: true
      });
    }
  }

  function showModal(icon, title, html){
    if (window.Swal) {
      Swal.fire({
        icon,
        title,
        html,
        confirmButtonText: 'OK'
      });
    }
  }

  function calcTotal(qty, kg){
    return (parseFloat(qty) || 0) * (parseFloat(kg) || 0);
  }

  function updateEntryTotal(){
    const qty = parseFloat(($('ac_do_qty').value || '0')) || 0;
    const kg = parseWhole($('ac_do_kg').value || '0');
    $('ac_do_total').value = fmtNum(calcTotal(qty, kg));
    updateLinePreview();
  }

  function setPackType(type){
    const nextType = String(type || '').toUpperCase() === 'BASKET' ? 'BASKET' : 'CARTON';
    $('ac_do_pack_type').value = nextType;
    document.querySelectorAll('#ac_do_pack_type_toggle .type-btn').forEach((btn) => {
      const btnType = (btn.dataset.packType || '').toUpperCase();
      btn.classList.toggle('active', btnType === nextType);
    });
    updateLinePreview();
  }

  function setUom(uom){
    const selectedUom = String(uom || '').toUpperCase() === 'PCS' ? 'PCS' : 'KG';
    $('ac_do_uom').value = selectedUom;
    document.querySelectorAll('#ac_do_uom_toggle .type-btn').forEach((btn) => {
      btn.classList.toggle('active', btn.dataset.uom === selectedUom);
    });
    
    // Update weight label and placeholder based on UOM
    const weightLabel = $('ac_do_weight_label');
    const weightInput = $('ac_do_kg');
    
    if (selectedUom === 'PCS') {
      weightLabel.textContent = 'Pieces (PCS)';
      weightInput.placeholder = 'Enter number of pieces';
    } else {
      weightLabel.textContent = 'Weight (KG)';
      weightInput.placeholder = 'Enter weight in KG';
    }
    
    updateLinePreview();
  }

  function initPackTypeToggle(){
    document.querySelectorAll('#ac_do_pack_type_toggle .type-btn').forEach((btn) => {
      btn.addEventListener('click', () => {
        setPackType(btn.dataset.packType || 'CARTON');
      });
    });
    setPackType($('ac_do_pack_type').value || 'CARTON');
  }

  function initUomToggle(){
    document.querySelectorAll('#ac_do_uom_toggle .type-btn').forEach((btn) => {
      btn.addEventListener('click', () => {
        setUom(btn.dataset.uom || 'KG');
      });
    });
    setUom($('ac_do_uom').value || 'KG');
  }

  function updateLinePreview(){
    const pv = $('ac_do_line_preview');
    const itemCode = ($('ac_do_item').value || '').trim();
    const packType = ($('ac_do_pack_type').value || '').trim();
    const uom = ($('ac_do_uom').value || 'KG').trim();
    const qty  = parseFloat(($('ac_do_qty').value || '0')) || 0;
    const kg   = parseWhole($('ac_do_kg').value || '0');
    const total = calcTotal(qty, kg);

    if(!itemCode){
      pv.style.display = 'none';
      pv.innerHTML = '';
      return;
    }

    pv.style.display = 'block';
    
    // Use appropriate label based on UOM
    const weightLabel = uom === 'PCS' ? 'Pcs' : 'Kg';
    
    pv.innerHTML = `
      <div class="preview-head">
        <span class="preview-item">${escapeHtml(itemCode)}</span>
        <span class="line-badge">${escapeHtml(packType)}</span>
      </div>
      <div class="preview-grid">
        <div class="metric"><div class="metric-label">Qty</div><div class="metric-value">${fmtNum(qty)} ${escapeHtml(uom)}</div></div>
        <div class="metric"><div class="metric-label">${weightLabel}</div><div class="metric�s'�"������������s1�
??�	-value">${fmtWhole(kg)}</div></div>
        <div class="metric"><div class="metric-label">Total</div><div class="metric-value">${fmtNum(total)}</div></div>
      </div>
    `;
  }

  function renderLines(){
    const list = $('ac_do_lines');
    list.innerHTML = '';

    if(!state.lines.length){
      list.innerHTML = '<div class="no-items">No items yet</div>';
      return;
    }

    state.lines.forEach((l, idx) => {
      const row = document.createElement('div');
      row.className = 'line-card';
      
      // Use appropriate label based on UOM
      const weightLabel = l.uom === 'PCS' ? 'Pcs' : 'Kg';
      
      row.innerHTML = `
        <div class="line-top">
          <div class="line-item">${escapeHtml(l.itemCode)}</div>
          <span class="line-badge">${escapeHtml(l.packType)}</span>
        </div>
        <div class="metric-grid">
          <div class="metric"><div class="metric-label">Qty</div><div class="metric-value">${fmtNum(l.qty)} ${escapeHtml(l.uom || 'KG')}</div></div>
          <div class="metric"><div class="metric-label">${weightLabel}</div><div class="metric-value">${fmtWhole(l.kg)}</div></div>
          <div class="metric"><div class="metric-label">Total</div><div class="metric-value">${fmtNum(l.total)}</div></div>
        </div>
        <button type="button" class="del-btn" data-idx="${idx}">Delete</button>
      `;
      list.appendChild(row);
    });
  }

  async function apiGet(url){
    const res = await fetch(url, {
      method:'GET',
      credentials:'same-origin',
      headers:{
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      cache:'no-store'
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    const text = await res.text();
    let data = null;
    try {
      data = text ? JSON.parse(text) : null;
    } catch (e) {
      throw new Error('Invalid JSON from job status endpoint');
    }
    return data;
  }

  async function apiPost(url, body){
    const res = await fetch(url, {
      method:'POST',
      credentials:'same-origin',
      headers:{
        'Content-Type':'application/json',
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      body: JSON.stringify(body)
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }
    return await res.json();
  }

  async function pollJob(jobId){
    const max = 45;
    for (let i = 0; i < max; i++) {
      await new Promise(r => setTimeout(r, 2000));
      let r;
      try {
        r = await apiGet(REST_JOB_BASE + jobId + '?_t=' + Date.now());
      } catch (err) {
        setStatus('Polling error: ' + (err.message || String(err)), 'error');
        showToast('error', 'Polling failed', err.message || 'Unable to read job status');
        return false;
      }

      const job = r?.job || r?.data?.job || r;
      const st = String(job?.status || '').toUpperCase();
      if (!st) continue;

      setStatus(`Job #${jobId}: ${st}`, (
        st === 'SUCCESS' ? 'success' :
        (st === 'FAILED' || st === 'FAILED_FINAL') ? 'error' :
        st === 'CANCELLED' ? 'warning' :
        'info'
      ));

      if (st === 'SUCCESS') {
        if (state.jobFinished) return true;
        state.jobFinished = true;
        let result = job.result || {};
        if (typeof result === 'string') {
          try { result = JSON.parse(result); } catch(e) {}
        }
        const docNo = result.docNo || result.DocNo || job.docNo || '';
        if (docNo) {
          const editUrl = EDIT_BASE + '?docno=' + encodeURIComponent(docNo);
          showModal(
            'success',
            'Delivery Order Created',
            `Document <b>${escapeHtml(docNo)}</b> was saved successfully.<br><br>
             <a href="${editUrl}" target="_blank" rel="noopener">Open Delivery Order</a>`
          );
        } else {
          showToast('success', 'Delivery Order created');
        }
        return true;
      }

      if (st === 'FAILED' || st === 'FAILED_FINAL') {
        if (state.jobFinished) return false;
        state.jobFinished = true;
        let result = job.result || {};
        if (typeof result === 'string') {
          try { result = JSON.parse(result); } catch(e) {}
        }
        const err = job.error_message || job.error || result.error || 'Job failed';
        showModal('error', 'Delivery Order Failed', escapeHtml(err));
        return false;
      }

      if (st === 'CANCELLED') {
        if (state.jobFinished) return false;
        state.jobFinished = true;
        showToast('warning', 'Job cancelled');
        return false;
      }
    }

    showModal(
      'warning',
      'Queue Timeout',
      'The page did not receive a final status in time.<br><br>Check the Jobs table to confirm whether it already completed.'
    );
    return false;
  }

  function attachSearch(inputEl, ddEl, fetchFn, onPick){
    let t = null;
    function hide(){ ddEl.style.display='none'; ddEl.innerHTML=''; }
    function show(){ ddEl.style.display='block'; }

    inputEl.addEventListener('input', () => {
      clearTimeout(t);
      const q = inputEl.value.trim();
      if(q.length < 1){ hide(); return; }
      t = setTimeout(async () => {
        ddEl.innerHTML = '<div class="item"><small>Searching...</small></div>';
        show();
        try{
          const items = await fetchFn(q);
          if(!items.length){
            ddEl.innerHTML = '<div class="item"><small>No result</small></div>';
            return;
          }
          ddEl.innerHTML = items.map((it, idx) => `
            <div class="item" data-idx="${idx}">
              <strong>${escapeHtml(it.code)}</strong>
              <small>${escapeHtml(it.name || '')}</small>
            </div>
          `).join('');
          ddEl.querySelectorAll('.item[data-idx]').forEach(node => {
            node.addEventListener('click', () => {
              const i = parseInt(node.dataset.idx, 10);
              onPick(items[i]);
              hide();
            });
          });
        }catch(e){
          ddEl.innerHTML = '<div class="item"><small>Failed to load</small></div>';
        }
      }, 200);
    });
    document.addEventListener('click', (e) => {
      if(!ddEl.contains(e.target) && e.target !== inputEl) hide();
    });
  }

  async function searchItemsLive(q){
    const cfg = window.AC_SUGGEST || {};
    const ajaxUrl = cfg.ajaxUrl || "";
    const nonce = cfg.nonce || "";
    if(!ajaxUrl || !nonce) return [];

    const fd = new FormData();
    fd.append("action", "ac_itemcode_suggest");
    fd.append("nonce", nonce);
    fd.append("term", q);

    const res = await fetch(ajaxUrl, {
      method: "POST",
      credentials: "same-origin",
      body: fd
    });

    let data;
    try {
      data = await res.json();
    } catch(e) {
      console.error('Item search parse error', e);
      return [];
    }

    if (data && data.success === true && data.data && Array.isArray(data.data.items)) {
      return data.data.items.map(it => ({
        code: it.code || '',
        name: (it.desc || it.name || it.description || '').trim()
      }));
    }
    return [];
  }

  function makeClientRequestId(prefix='DO'){
    const rnd = Math.random().toString(36).slice(2,10);
    const ts  = Date.now().toString(36);
    return `${prefix}-${ts}-${rnd}`;
  }

  (function initCustomerDropdown(){
    const wrapper = document.getElementById('acDoDebtorWrapper');
    if (!wrapper) return;

    const AJAX_URL = wrapper.dataset.ajaxUrl;
    const NONCE = wrapper.dataset.nonce;

    const input = document.getElementById('acDoDebtorInput');
    const dropdown = document.getElementById('acDoDebtorDropdown');
    const hiddenCode = document.getElementById('ac_do_customer');

    let items = [];
    let activeIndex = -1;
    let abortController = null;
    let searchTimeout = null;

    const show = () => dropdown.classList.add('active');
    const hide = () => { dropdown.classList.remove('active'); activeIndex = -1; };

    const renderEmpty = (msg) => {
      dropdown.innerHTML = `<div class="ac-dropdown-empty">${escapeHtml(msg)}</div>`;
      show();
    };
    const renderError = (msg) => {
      dropdown.innerHTML = `<div class="ac-dropdown-error">⚠️ ${escapeHtml(msg)}</div>`;
      show();
    };
    const renderResults = (arr) => {
      if (!arr || arr.length === 0) return renderEmpty('No debtor found');
      dropdown.innerHTML = arr.map((it, idx) => {
        const name = it.name || it.debtorName || it.description || '';
        const code = it.code || it.debtorCode || it.id || '';
        const sa   = (it.salesAgent || it.sales_agent || it.sa || '').trim();
        return `
          <div class="ac-dropdown-item" data-index="${idx}">
            <strong>${escapeHtml(code)}</strong>
            <small>${escapeHtml(name)}${sa ? ' • SA: ' + escapeHtml(sa) : ''}</small>
          </div>
        `;
      }).join('');
      show();
      activeIndex = -1;
    };

    const selectItem = (idx) => {
      if (idx < 0 || idx >= items.length) return;
      const it = items[idx];
      const name = it.name || it.debtorName || it.description || '';
      const code = it.code || it.debtorCode || it.id || '';
      const salesAgent = (it.salesAgent || it.sales_agent || it.sa || '').trim();
      input.value = code;
      hiddenCode.value = code;
      $('ac_do_customer_name').value = name;
      $('ac_do_sales_agent').value = salesAgent;
      hide();
    };

    const performSearch = async (query) => {
      if (abortController) abortController.abort();
      abortController = new AbortController();
      const q = (query || '').trim();
      if (q.length < 1) { hide(); return; }
      renderEmpty('Searching...');
      try {
        const url = `${AJAX_URL}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(NONCE)}&q=${encodeURIComponent(q)}`;
        const response = await fetch(url, { signal: abortController.signal, credentials:'same-origin' });
        const text = await response.text();
        let data;
        try { data = JSON.parse(text); } catch (e) { throw new Error('Invalid JSON response'); }
        if (!data.success) throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');
        items = (data.data && data.data.items) ? data.data.items : [];
        renderResults(items);
      } catch (err) {
        if (err.name === 'AbortError') return;
        renderError('Failed to load results');
      }
    };

    const debounceSearch = () => {
      clearTimeout(searchTimeout);
      searchTimeout = setTimeout(() => performSearch(input.value), 300);
    };

    input.addEventListener('input', () => {
      hiddenCode.value = '';
      $('ac_do_customer_name').value = '';
      $('ac_do_sales_agent').value = '';
      debounceSearch();
    });
    input.addEventListener('focus', () => {
      if (input.value.trim() !== '') debounceSearch();
    });
    input.addEventListener('keydown', (e) => {
      if (!dropdown.classList.contains('active')) return;
      const nodes = dropdown.querySelectorAll('.ac-dropdown-item');
      if (!nodes.length) return;
      if (e.key === 'ArrowDown') {
        e.preventDefault();
        activeIndex = (activeIndex + 1) % nodes.length;
        nodes.forEach((n, i) => n.classList.toggle('active', i === activeIndex));
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        activeIndex = activeIndex > 0 ? activeIndex - 1 : nodes.length - 1;
        nodes.forEach((n, i) => n.classList.toggle('active', i === activeIndex));
      } else if (e.key === 'Enter' && activeIndex >= 0) {
        e.preventDefault();
        selectItem(activeIndex);
      } else if (e.key === 'Escape') {
        hide();
      }
    });
    dropdown.addEventListener('click', (e) => {
      const node = e.target.closest('.ac-dropdown-item');
      if (!node) return;
      selectItem(parseInt(node.dataset.index, 10));
    });
    document.addEventListener('click', (e) => {
      if (!wrapper.contains(e.target)) hide();
    });
  })();

  (function init(){
    const d = new Date();
    $('ac_do_date').value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
    
    attachSearch($('ac_do_item_name'), $('ac_do_item_dd'), searchItemsLive, (it) => {
      $('ac_do_item_name').value = it.code || '';
      $('ac_do_item').value = it.code || '';
      $('ac_do_item_display').value = it.name || '';
      updateLinePreview();
    });
    
    $('ac_do_qty').addEventListener('input', updateEntryTotal);
    $('ac_do_kg').addEventListener('input', updateEntryTotal);
    $('ac_do_pack_type').addEventListener('change', () => setPackType($('ac_do_pack_type').value));
    $('ac_do_uom').addEventListener('change', () => {
      setUom($('ac_do_uom').value);
      updateLinePreview();
    });
    
    $('ac_do_item_name').addEventListener('input', () => {
      $('ac_do_item').value = '';
      $('ac_do_item_display').value = '';
      updateLinePreview();
    });

    $('ac_do_item_name').addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        $('ac_do_kg').focus();
      }
    });
    $('ac_do_kg').addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        $('ac_do_qty').focus();
      }
    });
    $('ac_do_qty').addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        $('ac_do_addline').click();
      }
    });

    initPackTypeToggle();
    initUomToggle();
    updateEntryTotal();
    renderLines();
  })();

  $('ac_do_addline').addEventListener('click', () => {
    const itemCode = ($('ac_do_item').value || '').trim();
    const packType = ($('ac_do_pack_type').value || '').trim();
    const uom = ($('ac_do_uom').value || 'KG').trim();
    const qty = parseFloat(($('ac_do_qty').value || '0')) || 0;
    const kg = parseWhole($('ac_do_kg').value || '0');
    const total = calcTotal(qty, kg);
    
    if(!itemCode){
      setStatus('Please select Item Code from dropdown.', 'error');
      return;
    }
    if(qty <= 0){
      setStatus('Qty must be greater than 0.', 'error');
      return;
    }
    if(kg <= 0){
      setStatus(uom === 'PCS' ? 'Pieces must be greater than 0.' : 'Kg must be greater than 0.', 'error');
      return;
    }
    
    state.lines.push({ itemCode, packType, qty, kg, total, uom });
    
    setPackType('CARTON');
    setUom('KG');
    $('ac_do_qty').value = '1';
    $('ac_do_kg').value = '0';
    $('ac_do_total').value = '0.00';
    $('ac_do_item_name').value = '';
    $('ac_do_item').value = '';
    $('ac_do_item_display').value = '';
    
    renderLines();
    updateLinePreview();
    $('ac_do_item_name').focus();
    setStatus('Line added.', 'success');
  });

  $('ac_do_lines').addEventListener('click', (e) => {
    const btn = e.target.closest('button[data-idx]');
    if(!btn) return;
    const idx = parseInt(btn.dataset.idx, 10);
    if(isNaN(idx)) return;
    state.lines.splice(idx, 1);
    renderLines();
    setStatus('Line removed.', 'warning');
  });

  $('ac_do_submit').addEventListener('click', async () => {
    if (state.isSubmitting) {
      setStatus('Already submitting, please wait...', 'warning');
      return;
    }
    const submitBtn = $('ac_do_submit');
    state.jobFinished = false;
    state.isSubmitting = true;
    try {
      submitBtn.disabled = true;
      submitBtn.textContent = 'Saving...';
      const customerCode = ($�s1�0�����������"�
??�<?php
/**
 * DELIVERY ORDER CREATE PAGE
 * Required flow:
 * - Select Debtor Code
 * - Choose Carton / Basket
 * - Fill Qty
 * - Fill Kg
 * - Select Item from Item Code
 * - Display Total = Qty * Kg
 */

if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">
        Please log in to create Delivery Order.
    </div>';
    return;
}

$rest_nonce = wp_create_nonce('wp_rest');

$list_url  = 'https://website.ipohserver.com/InventorySearch/index.php/delivery-orders/';
$edit_base = 'https://website.ipohserver.com/InventorySearch/index.php/delivery-order-edit/';

$REST_JOB_POST = rest_url('ac/v1/job');
$REST_JOB_BASE = rest_url('ac/v1/job/');

$ajax_url           = admin_url('admin-ajax.php');
$debtor_nonce       = wp_create_nonce('ac_cs_debtor_search');
$item_suggest_nonce = wp_create_nonce('ac_itemcode_suggest');
$default_location   = 'HQ';
?>

<script>
window.AC_SUGGEST = window.AC_SUGGEST || {};
window.AC_SUGGEST.ajaxUrl = "<?php echo esc_js($ajax_url); ?>";
window.AC_SUGGEST.nonce   = "<?php echo esc_js($item_suggest_nonce); ?>";
</script>

<div class="ac-so-wrap do-container"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-edit-base="<?php echo esc_attr($edit_base); ?>"
     data-rest-job-post="<?php echo esc_attr($REST_JOB_POST); ?>"
     data-rest-job-base="<?php echo esc_attr($REST_JOB_BASE); ?>">

  <div class="do-pagehead">
    <a class="back-btn" href="<?php echo esc_url($list_url); ?>">← Back to List</a>
    <h1>Create Delivery Order</h1>
  </div>

  <div class="do-card">
    <div class="do-label">Date</div>
    <input id="ac_do_date" type="date" class="do-input" />

    <input type="hidden" id="ac_do_customer_name" value="" />
    <input type="hidden" id="ac_do_sales_agent" value="" />
    <input type="hidden" id="ac_do_location" value="<?php echo esc_attr($default_location); ?>" />

    <div class="do-label">Debtor Code</div>
    <div class="ac-search-wrapper"
         id="acDoDebtorWrapper"
         data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
         data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
      <input type="text"
             id="acDoDebtorInput"
             class="ac-search-input do-input"
             placeholder="Search debtor code..."
             autocomplete="off" />
      <input type="hidden" id="ac_do_customer" value="" />
      <div class="ac-search-dropdown" id="acDoDebtorDropdown"></div>
    </div>

    <div id="ac_do_status" class="ac-so-status"></div>
  </div>

  <div class="do-card">
    <div class="do-label">Item Code</div>
    <div class="ac-item-search-wrap">
      <input id="ac_do_item_name" type="text" class="do-input" placeholder="Scan / type item" autocomplete="off">
      <input id="ac_do_item" type="hidden" value="">
      <input id="ac_do_item_display" type="hidden" value="">
      <div class="ac-dd" id="ac_do_item_dd" style="display:none;"></div>
    </div>

    <div class="do-label">Type</div>
    <div class="type-toggle" id="ac_do_pack_type_toggle">
      <button type="button" class="type-btn active" data-pack-type="CARTON">Carton</button>
      <button type="button" class="type-btn" data-pack-type="BASKET">Basket</button>
    </div>
    <select id="ac_do_pack_type" style="display:none;">
      <option value="CARTON" selected>Carton</option>
      <option value="BASKET">Basket</option>
    </select>

    <div class="do-label">Quantity (Qty)</div>
    <input id="ac_do_qty" type="number" min="0" step="1" value="1" class="do-input" placeholder="Enter quantity">

    <div class="do-label">Weight (KG)</div>
    <input id="ac_do_kg" type="number" min="0" step="1" value="0" class="do-input" placeholder="Enter weight">

    <input id="ac_do_total" type="hidden" value="0">
    <div class="ac-line-preview" id="ac_do_line_preview" style="display:none;"></div>

    <button id="ac_do_addline" class="do-btn" type="button">+ Add Item</button>
  </div>

  <div class="do-card">
    <div id="ac_do_lines" class="do-list"><b>No items yet</b></div>
  </div>

  <div class="save-wrapper">
    <button class="do-btn" id="ac_do_submit" type="button">Save Delivery Order</button>
  </div>
</div>

<style>
  .do-container{
    max-width:560px;
    margin:0 auto;
    padding:16px;
    font-family:"Segoe UI",Arial,sans-serif;
    background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
    color:#1f2937;
    box-sizing:border-box;
    border-radius:16px;
  }
  .do-pagehead{
    display:flex;
    align-items:center;
    gap:12px;
    flex-wrap:wrap;
    margin-bottom:12px;
  }
  .do-pagehead h1{
    margin:0;
    font-size:clamp(24px,6vw,34px);
    font-weight:800;
    line-height:1.1;
    color:#0f172a;
  }
  .back-btn{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border:1px solid #cfd9d1;
    background:#fff;
    color:#0f172a;
    padding:10px 12px;
    border-radius:10px;
    text-decoration:none;
    font-weight:700;
    font-size:14px;
    min-height:44px;
  }
  .do-card{
    background:#fff;
    border-radius:14px;
    padding:16px;
    margin-bottom:14px;
    box-shadow:0 4px 16px rgba(15,23,42,.05);
    border:1px solid #e2ebe3;
  }
  .do-label{
    font-size:13px;
    color:#4b5563;
    margin-bottom:6px;
    font-weight:700;
  }
  .do-input{
    width:100%;
    min-height:48px;
    font-size:16px;
    padding:12px;
    border-radius:10px;
    border:1px solid #d1d5db;
    margin-bottom:14px;
    box-sizing:border-box;
    background:#fff;
    color:#111;
  }
  .do-input:focus,
  .type-btn:focus,
  .do-btn:focus,
  .del-btn:focus{
    outline:none;
    box-shadow:0 0 0 3px rgba(40,167,69,.18);
  }
  .do-input:focus{
    border-color:#28a745;
  }
  .type-toggle{
    display:flex;
    gap:10px;
    margin-bottom:14px;
  }
  .type-btn{
    flex:1;
    min-height:48px;
    padding:10px 12px;
    border-radius:10px;
    border:1px solid #28a745;
    background:#fff;
    color:#28a745;
    font-size:18px;
    font-weight:700;
    cursor:pointer;
  }
  .type-btn.active{
    background:linear-gradient(90deg,#28a745,#1e7e34);
    color:#fff;
  }
  .do-btn{
    width:100%;
    min-height:52px;
    background:linear-gradient(90deg,#28a745,#1e7e34);
    color:#fff;
    border:none;
    padding:12px 14px;
    border-radius:12px;
    font-size:20px;
    font-weight:800;
    cursor:pointer;
  }
  .do-btn:disabled{
    opacity:.7;
    cursor:not-allowed;
  }
  .save-wrapper{
    padding:0 2px 10px;
  }

  .ac-search-wrapper,
  .ac-item-search-wrap{ position:relative; margin-bottom:14px; }

  .ac-search-dropdown,
  .ac-dd{
    position:absolute;
    left:0;
    right:0;
    top:calc(100% - 10px);
    background:#fff;
    border:1px solid #dcdcdc;
    border-radius:10px;
    z-index:80;
    max-height:260px;
    overflow:auto;
    box-shadow:0 8px 24px rgba(0,0,0,.10);
  }
  .ac-search-dropdown{ display:none; }
  .ac-search-dropdown.active{ display:block; }

  .ac-dropdown-item,
  .ac-dd .item{
    padding:10px 12px;
    cursor:pointer;
    border-bottom:1px solid #f1f1f1;
    display:flex;
    flex-direction:column;
    gap:2px;
    font-size:15px;
  }
  .ac-dropdown-item:last-child,
  .ac-dd .item:last-child{ border-bottom:none; }
  .ac-dropdown-item:hover,
  .ac-dropdown-item.active,
  .ac-dd .item:hover{ background:#f3f9f4; }

  .ac-dropdown-empty,
  .ac-dropdown-error,
  .ac-dd .item small{
    color:#64748b;
    font-size:12px;
  }

  .ac-line-preview{
    display:none;
    background:#f8fafc;
    border:1px solid #e2e8f0;
    border-radius:12px;
    padding:10px 12px;
    margin-bottom:12px;
    color:#0f172a;
  }
  .preview-head,
  .line-top{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:10px;
    margin-bottom:10px;
  }
  .preview-item,
  .line-item{
    font-size:20px;
    font-weight:800;
    color:#0f5132;
    line-height:1.2;
    word-break:break-word;
  }
  .line-badge{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border-radius:999px;
    border:1px solid #bfe3c8;
    background:#eefaf1;
    color:#166534;
    font-size:12px;
    font-weight:800;
    letter-spacing:.04em;
    text-transform:uppercase;
    padding:6px 10px;
    white-space:nowrap;
  }
  .preview-grid,
  .metric-grid{
    display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));
    gap:8px;
  }
  .metric{
    background:#f8fafc;
    border:1px solid #e2e8f0;
    border-radius:10px;
    padding:8px 10px;
  }
  .metric-label{
    color:#64748b;
    font-size:11px;
    font-weight:700;
    letter-spacing:.05em;
    text-transform:uppercase;
    margin-bottom:4px;
  }
  .metric-value{
    color:#0f172a;
    font-size:18px;
    font-weight:800;
    line-height:1;
  }

  .do-list{
    display:grid;
    gap:10px;
  }
  .line-card{
    border:1px solid #e2e8f0;
    border-radius:12px;
    background:#fff;
    padding:12px;
  }
  .no-items{
    border:1px dashed #cbd5e1;
    border-radius:10px;
    background:#f8fafc;
    color:#64748b;
    padding:14px;
    text-align:center;
    font-weight:700;
  }
  .del-btn{
    margin-top:10px;
    min-height:42px;
    background:#ff4d4d;
    color:#fff;
    border:none;
    padding:8px 12px;
    border-radius:8px;
    font-size:14px;
    font-weight:700;
    cursor:pointer;
  }

  .ac-so-status{ display:none; }

  @media (max-width: 768px){
    .do-container{
      padding:12px;
      border-radius:0;
      margin-left:-8px;
      margin-right:-8px;
      max-width:none;
    }
    .do-card{
      padding:14px;
      margin-bottom:12px;
    }
    .preview-item,
    .line-item{ font-size:18px; }
    .metric-value{ font-size:17px; }
    .save-wrapper{
      position:sticky;
      bottom:0;
      z-index:30;
      padding:10px 2px calc(12px + env(safe-area-inset-bottom));
      background:linear-gradient(to top, rgba(239,247,241,.96), rgba(239,247,241,0));
      backdrop-filter:blur(1px);
    }
  }

  @media (max-width: 420px){
    .type-btn{ font-size:16px; }
    .metric-grid,
    .preview-grid{ grid-template-columns:1fr; }
    .line-badge{ font-size:11px; padding:5px 8px; }
  }
</style>


<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

<script>
(function(){
  const wrap = document.querySelector('.ac-so-wrap');
  if(!wrap) return;

  const REST_NONCE    = wrap.dataset.restNonce;
  const EDIT_BASE     = wrap.dataset.editBase;
  const REST_JOB_POST = wrap.dataset.restJobPost;
  const REST_JOB_BASE = wrap.dataset.restJobBase;

  const $ = (id) => document.getElementById(id);
  const state = { lines: [], jobFinished: false, isSubmitting: false };

  function escapeHtml(s){
    if(s===null || s===undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
  }

  function fmtNum(n){
    const x = parseFloat(n);
    return isNaN(x) ? '0.00' : x.toFixed(2);
  }

  function parseWhole(n){
    const x = Number(n);
    if(!isFinite(x) || x < 0) return 0;
    return Math.round(x);
  }

  function fmtWhole(n){
    return String(parseWhole(n));
  }

  function setStatus(msg, type='info'){
    const toastType =
      type === 'success' ? 'success' :
      type === 'error'   ? 'error' :
      type === 'warning' ? 'warning' :
      'info';
    if (window.Swal) {
      Swal.fire({
        toast: true,
        position: 'top-end',
        icon: toastType,
        title: msg.replace(/<[^>]*>/g, ''),
        showConfirmButton: false,
        timer: 1800,
        timerProgressBar: true
      });
    }
  }

  function showToast(icon, title, text=''){
    if (window.Swal) {
      Swal.fire({
        toast: true,
        position: 'top-end',
        icon,
        title,
        text,
        showConfirmButton: false,
        timer: 2600,
        timerProgressBar: true
      });
    }
  }

  function showModal(icon, title, html){
    if (window.Swal) {
      Swal.fire({
        icon,
        title,
        html,
        confirmButtonText: 'OK'
      });
    }
  }

  function calcTotal(qty, kg){
    return (parseFloat(qty) || 0) * (parseFloat(kg) || 0);
  }

  function updateEntryTotal(){
    const qty = parseFloat(($('ac_do_qty').value || '0')) || 0;
    const kg = parseWhole($('ac_do_kg').value || '0');
    $('ac_do_total').value = fmtNum(calcTotal(qty, kg));
    updateLinePreview();
  }

  function setPackType(type){
    const nextType = String(type || '').toUpperCase() === 'BASKET' ? 'BASKET' : 'CARTON';
    $('ac_do_pack_type').value = nextType;
    document.querySelectorAll('#ac_do_pack_type_toggle .type-btn').forEach((btn) => {
      const btnType = (btn.dataset.packType || '').toUpperCase();
      btn.classList.toggle('active', btnType === nextType);
    });
    updateLinePreview();
  }

  function initPackTypeToggle(){
    document.querySelectorAll('#ac_do_pack_type_toggle .type-btn').forEach((btn) => {
      btn.addEventListener('click', () => {
        setPackType(btn.dataset.packType || 'CARTON');
      });
    });
    setPackType($('ac_do_pack_type').value || 'CARTON');
  }

  function updateLinePreview(){
    const pv = $('ac_do_line_preview');
    const itemCode = ($('ac_do_item').value || '').trim();
    const packType = ($('ac_do_pack_type').value || '').trim();
    const qty  = parseFloat(($('ac_do_qty').value || '0')) || 0;
    const kg   = parseWhole($('ac_do_kg').value || '0');
    const total = calcTotal(qty, kg);

    if(!itemCode){
      pv.style.display = 'none';
      pv.innerHTML = '';
      return;
    }

    pv.style.display = 'block';
    pv.innerHTML = `
      <div class="preview-head">
        <span class="preview-item">${escapeHtml(itemCode)}</span>
        <span class="line-badge">${escapeHtml(packType)}</span>
      </div>
      <div class="preview-grid">
        <div class="metric"><div class="metric-label">Qty</div><div class="metric-value">${fmtNum(qty)}</div></div>
        <div class="metric"><div class="metric-label">Kg</div><div class="metric-value">${fmtWhole(kg)}</div></div>
        <div class="metric"><div class="metric-label">Total</div><div class="metric-value">${fmtNum(total)}</div></div>
      </div>
    `;
  }

  function renderLines(){
    const list = $('ac_do_lines');
    list.innerHTML = '';

    if(!state.lines.length){
      list.innerHTML = '<div class="no-items">No items yet</div>';
      return;
    }

    state.lines.forEach((l, idx) => {
      const row = document.createElement('div');
      row.className = 'line-card';
      row.innerHTML = `
        <div class="line-top">
          <div class="line-item">${escapeHtml(l.itemCode)}</div>
          <span class="line-badge">${escapeHtml(l.packType)}</span>
        </div>
        <div class="metric-grid">
          <div class="metric"><div class="metric-label">Qty</div><div class="metric-value">${fmtNum(l.qty)}</div></div>
          <div class="metric"><div class="metric-label">Kg</div><div class="metric-value">${fmtWhole(l.kg)}</div></div>
          <div class="metric"><div class="metric-label">Total</div><div class="metric-value">${fmtNum(l.total)}</div></div>
        </div>
        <button type="button" class="del-btn" data-idx="${idx}">Delete</button>
      `;
      list.appendChild(row);
    });
  }

  async function apiGet(url){
    const res = await fetch(url, {
      method:'GET',
      credentials:'same-origin',
      headers:{
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      cache:'no-store'
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
   �"�Q澶���������#}
??� }

    const text = await res.text();
    let data = null;
    try {
      data = text ? JSON.parse(text) : null;
    } catch (e) {
      throw new Error('Invalid JSON from job status endpoint');
    }
    return data;
  }

  async function apiPost(url, body){
    const res = await fetch(url, {
      method:'POST',
      credentials:'same-origin',
      headers:{
        'Content-Type':'application/json',
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      body: JSON.stringify(body)
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }
    return await res.json();
  }

  async function pollJob(jobId){
    const max = 45;
    for (let i = 0; i < max; i++) {
      await new Promise(r => setTimeout(r, 2000));
      let r;
      try {
        r = await apiGet(REST_JOB_BASE + jobId + '?_t=' + Date.now());
      } catch (err) {
        setStatus('Polling error: ' + (err.message || String(err)), 'error');
        showToast('error', 'Polling failed', err.message || 'Unable to read job status');
        return false;
      }

      const job = r?.job || r?.data?.job || r;
      const st = String(job?.status || '').toUpperCase();
      if (!st) continue;

      setStatus(`Job #${jobId}: ${st}`, (
        st === 'SUCCESS' ? 'success' :
        (st === 'FAILED' || st === 'FAILED_FINAL') ? 'error' :
        st === 'CANCELLED' ? 'warning' :
        'info'
      ));

      if (st === 'SUCCESS') {
        if (state.jobFinished) return true;
        state.jobFinished = true;
        let result = job.result || {};
        if (typeof result === 'string') {
          try { result = JSON.parse(result); } catch(e) {}
        }
        const docNo = result.docNo || result.DocNo || job.docNo || '';
        if (docNo) {
          const editUrl = EDIT_BASE + '?docno=' + encodeURIComponent(docNo);
          showModal(
            'success',
            'Delivery Order Created',
            `Document <b>${escapeHtml(docNo)}</b> was saved successfully.<br><br>
             <a href="${editUrl}" target="_blank" rel="noopener">Open Delivery Order</a>`
          );
        } else {
          showToast('success', 'Delivery Order created');
        }
        return true;
      }

      if (st === 'FAILED' || st === 'FAILED_FINAL') {
        if (state.jobFinished) return false;
        state.jobFinished = true;
        let result = job.result || {};
        if (typeof result === 'string') {
          try { result = JSON.parse(result); } catch(e) {}
        }
        const err = job.error_message || job.error || result.error || 'Job failed';
        showModal('error', 'Delivery Order Failed', escapeHtml(err));
        return false;
      }

      if (st === 'CANCELLED') {
        if (state.jobFinished) return false;
        state.jobFinished = true;
        showToast('warning', 'Job cancelled');
        return false;
      }
    }

    showModal(
      'warning',
      'Queue Timeout',
      'The page did not receive a final status in time.<br><br>Check the Jobs table to confirm whether it already completed.'
    );
    return false;
  }

  function attachSearch(inputEl, ddEl, fetchFn, onPick){
    let t = null;
    function hide(){ ddEl.style.display='none'; ddEl.innerHTML=''; }
    function show(){ ddEl.style.display='block'; }

    inputEl.addEventListener('input', () => {
      clearTimeout(t);
      const q = inputEl.value.trim();
      if(q.length < 1){ hide(); return; }
      t = setTimeout(async () => {
        ddEl.innerHTML = '<div class="item"><small>Searching...</small></div>';
        show();
        try{
          const items = await fetchFn(q);
          if(!items.length){
            ddEl.innerHTML = '<div class="item"><small>No result</small></div>';
            return;
          }
          ddEl.innerHTML = items.map((it, idx) => `
            <div class="item" data-idx="${idx}">
              <strong>${escapeHtml(it.code)}</strong>
              <small>${escapeHtml(it.name || '')}</small>
            </div>
          `).join('');
          ddEl.querySelectorAll('.item[data-idx]').forEach(node => {
            node.addEventListener('click', () => {
              const i = parseInt(node.dataset.idx, 10);
              onPick(items[i]);
              hide();
            });
          });
        }catch(e){
          ddEl.innerHTML = '<div class="item"><small>Failed to load</small></div>';
        }
      }, 200);
    });
    document.addEventListener('click', (e) => {
      if(!ddEl.contains(e.target) && e.target !== inputEl) hide();
    });
  }

  async function searchItemsLive(q){
    const cfg = window.AC_SUGGEST || {};
    const ajaxUrl = cfg.ajaxUrl || "";
    const nonce = cfg.nonce || "";
    if(!ajaxUrl || !nonce) return [];

    const fd = new FormData();
    fd.append("action", "ac_itemcode_suggest");
    fd.append("nonce", nonce);
    fd.append("term", q);

    const res = await fetch(ajaxUrl, {
      method: "POST",
      credentials: "same-origin",
      body: fd
    });

    let data;
    try {
      data = await res.json();
    } catch(e) {
      console.error('Item search parse error', e);
      return [];
    }

    // WordPress standard response: { success: true, data: { items: [...] } }
    if (data && data.success === true && data.data && Array.isArray(data.data.items)) {
      return data.data.items.map(it => ({
        code: it.code || '',
        name: (it.desc || it.name || it.description || '').trim()
      }));
    }
    return [];
  }

  function makeClientRequestId(prefix='DO'){
    const rnd = Math.random().toString(36).slice(2,10);
    const ts  = Date.now().toString(36);
    return `${prefix}-${ts}-${rnd}`;
  }

  (function initCustomerDropdown(){
    const wrapper = document.getElementById('acDoDebtorWrapper');
    if (!wrapper) return;

    const AJAX_URL = wrapper.dataset.ajaxUrl;
    const NONCE = wrapper.dataset.nonce;

    const input = document.getElementById('acDoDebtorInput');
    const dropdown = document.getElementById('acDoDebtorDropdown');
    const hiddenCode = document.getElementById('ac_do_customer');

    let items = [];
    let activeIndex = -1;
    let abortController = null;
    let searchTimeout = null;

    const show = () => dropdown.classList.add('active');
    const hide = () => { dropdown.classList.remove('active'); activeIndex = -1; };

    const renderEmpty = (msg) => {
      dropdown.innerHTML = `<div class="ac-dropdown-empty">${escapeHtml(msg)}</div>`;
      show();
    };
    const renderError = (msg) => {
      dropdown.innerHTML = `<div class="ac-dropdown-error">⚠️ ${escapeHtml(msg)}</div>`;
      show();
    };
    const renderResults = (arr) => {
      if (!arr || arr.length === 0) return renderEmpty('No debtor found');
      dropdown.innerHTML = arr.map((it, idx) => {
        const name = it.name || it.debtorName || it.description || '';
        const code = it.code || it.debtorCode || it.id || '';
        const sa   = (it.salesAgent || it.sales_agent || it.sa || '').trim();
        return `
          <div class="ac-dropdown-item" data-index="${idx}">
            <strong>${escapeHtml(code)}</strong>
            <small>${escapeHtml(name)}${sa ? ' • SA: ' + escapeHtml(sa) : ''}</small>
          </div>
        `;
      }).join('');
      show();
      activeIndex = -1;
    };

    const selectItem = (idx) => {
      if (idx < 0 || idx >= items.length) return;
      const it = items[idx];
      const name = it.name || it.debtorName || it.description || '';
      const code = it.code || it.debtorCode || it.id || '';
      const salesAgent = (it.salesAgent || it.sales_agent || it.sa || '').trim();
      input.value = code;
      hiddenCode.value = code;
      $('ac_do_customer_name').value = name;
      $('ac_do_sales_agent').value = salesAgent;
      hide();
    };

    const performSearch = async (query) => {
      if (abortController) abortController.abort();
      abortController = new AbortController();
      const q = (query || '').trim();
      if (q.length < 1) { hide(); return; }
      renderEmpty('Searching...');
      try {
        const url = `${AJAX_URL}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(NONCE)}&q=${encodeURIComponent(q)}`;
        const response = await fetch(url, { signal: abortController.signal, credentials:'same-origin' });
        const text = await response.text();
        let data;
        try { data = JSON.parse(text); } catch (e) { throw new Error('Invalid JSON response'); }
        if (!data.success) throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');
        items = (data.data && data.data.items) ? data.data.items : [];
        renderResults(items);
      } catch (err) {
        if (err.name === 'AbortError') return;
        renderError('Failed to load results');
      }
    };

    const debounceSearch = () => {
      clearTimeout(searchTimeout);
      searchTimeout = setTimeout(() => performSearch(input.value), 300);
    };

    input.addEventListener('input', () => {
      hiddenCode.value = '';
      $('ac_do_customer_name').value = '';
      $('ac_do_sales_agent').value = '';
      debounceSearch();
    });
    input.addEventListener('focus', () => {
      if (input.value.trim() !== '') debounceSearch();
    });
    input.addEventListener('keydown', (e) => {
      if (!dropdown.classList.contains('active')) return;
      const nodes = dropdown.querySelectorAll('.ac-dropdown-item');
      if (!nodes.length) return;
      if (e.key === 'ArrowDown') {
        e.preventDefault();
        activeIndex = (activeIndex + 1) % nodes.length;
        nodes.forEach((n, i) => n.classList.toggle('active', i === activeIndex));
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        activeIndex = activeIndex > 0 ? activeIndex - 1 : nodes.length - 1;
        nodes.forEach((n, i) => n.classList.toggle('active', i === activeIndex));
      } else if (e.key === 'Enter' && activeIndex >= 0) {
        e.preventDefault();
        selectItem(activeIndex);
      } else if (e.key === 'Escape') {
        hide();
      }
    });
    dropdown.addEventListener('click', (e) => {
      const node = e.target.closest('.ac-dropdown-item');
      if (!node) return;
      selectItem(parseInt(node.dataset.index, 10));
    });
    document.addEventListener('click', (e) => {
      if (!wrapper.contains(e.target)) hide();
    });
  })();
  (function init(){
    const d = new Date();
    $('ac_do_date').value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
    attachSearch($('ac_do_item_name'), $('ac_do_item_dd'), searchItemsLive, (it) => {
      $('ac_do_item_name').value = it.code || '';
      $('ac_do_item').value = it.code || '';
      $('ac_do_item_display').value = it.name || '';
      updateLinePreview();
    });
    $('ac_do_qty').addEventListener('input', updateEntryTotal);
    $('ac_do_kg').addEventListener('input', updateEntryTotal);
    $('ac_do_pack_type').addEventListener('change', () => setPackType($('ac_do_pack_type').value));
    $('ac_do_item_name').addEventListener('input', () => {
      $('ac_do_item').value = '';
      $('ac_do_item_display').value = '';
      updateLinePreview();
    });

    $('ac_do_item_name').addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        $('ac_do_qty').focus();
      }
    });
    $('ac_do_qty').addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        $('ac_do_kg').focus();
      }
    });
    $('ac_do_kg').addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        $('ac_do_addline').click();
      }
    });

    initPackTypeToggle();
    updateEntryTotal();
    renderLines();
  })();

  $('ac_do_addline').addEventListener('click', () => {
    const itemCode = ($('ac_do_item').value || '').trim();
    const packType = ($('ac_do_pack_type').value || '').trim();
    const qty = parseFloat(($('ac_do_qty').value || '0')) || 0;
    const kg = parseWhole($('ac_do_kg').value || '0');
    const total = calcTotal(qty, kg);
    if(!itemCode){
      setStatus('Please select Item Code from dropdown.', 'error');
      return;
    }
    if(qty <= 0){
      setStatus('Qty must be greater than 0.', 'error');
      return;
    }
    if(kg <= 0){
      setStatus('Kg must be greater than 0.', 'error');
      return;
    }
    state.lines.push({ itemCode, packType, qty, kg, total });
    setPackType('CARTON');
    $('ac_do_qty').value = '1';
    $('ac_do_kg').value = '0';
    $('ac_do_total').value = '0.00';
    $('ac_do_item_name').value = '';
    $('ac_do_item').value = '';
    $('ac_do_item_display').value = '';
    renderLines();
    updateLinePreview();
    $('ac_do_item_name').focus();
    setStatus('Line added.', 'success');
  });

  $('ac_do_lines').addEventListener('click', (e) => {
    const btn = e.target.closest('button[data-idx]');
    if(!btn) return;
    const idx = parseInt(btn.dataset.idx, 10);
    if(isNaN(idx)) return;
    state.lines.splice(idx, 1);
    renderLines();
    setStatus('Line removed.', 'warning');
  });

  $('ac_do_submit').addEventListener('click', async () => {
    if (state.isSubmitting) {
      setStatus('Already submitting, please wait...', 'warning');
      return;
    }
    const submitBtn = $('ac_do_submit');
    state.jobFinished = false;
    state.isSubmitting = true;
    try {
      submitBtn.disabled = true;
      submitBtn.textContent = 'Saving...';
      const customerCode = ($('ac_do_customer').value || '').trim();
      const customerName = ($('ac_do_customer_name').value || '').trim();
      const salesAgent = ($('ac_do_sales_agent').value || '').trim();
      const location = ($('ac_do_location').value || '').trim();
      const docDate = ($('ac_do_date').value || '').trim();
      if(!customerCode){ setStatus('Please select Debtor Code.', 'error'); return; }
      if(!docDate){ setStatus('Please enter Date.', 'error'); return; }
      if(!location){ setStatus('Location is missing.', 'error'); return; }
      if(!state.lines.length){ setStatus('Please add at least 1 line item.', 'error'); return; }
      const payload = {
        customerCode, customerName, salesAgent,
        debtorCode: customerCode, DebtorCode: customerCode,
        debtorName: customerName, DebtorName: customerName,
        SalesAgent: salesAgent,
        location, Location: location,
        docDate, remark: '',
        lines: state.lines.map(l => ({
          itemCode: l.itemCode,
          qty: l.total,
          uom: 'KG',
          unitPrice: 0,
          amount: 0,
          taxCode: 'SR-0',
          taxRate: 0,
          packType: l.packType,
          cartonQty: l.qty,
          kg: l.kg,
          totalKg: l.total,
          location
        }))
      };
      const body = {
        type: 'DELIVERY_ORDER',
        client_request_id: makeClientRequestId('DO'),
        source: 'wp-ui',
        payload
      };
      const r = await apiPost(REST_JOB_POST, body);
      const jobId = r.jobId || r.id || (r.job && r.job.id);
      if (!jobId) {
        setStatus('Job created but jobId missing in response.', 'warning');
        showToast('warning', 'Job created, but jobId missing');
        return;
      }
      setStatus(`Job queued: #${jobId}. Waiting BridgeWorker...`, 'info');
      showToast('info', 'Job queued', `Job #${jobId}`);
      await pollJob(jobId);
    } catch(err) {
      setStatus('Submit error: ' + (err.message || String(err)), 'error');
      showModal('error', 'Submit error', escapeHtml(err.message || String(err)));
    } finally {
      state.isSubmitting = false;
 �#}�f����������#}
?v����     submitBtn.disabled = false;
      submitBtn.textContent = 'Save Delivery Order';
    }
  });
})();
</script>�#}f���	���������s1�
?	����('ac_do_customer').value || '').trim();
      const customerName = ($('ac_do_customer_name').value || '').trim();
      const salesAgent = ($('ac_do_sales_agent').value || '').trim();
      const location = ($('ac_do_location').value || '').trim();
      const docDate = ($('ac_do_date').value || '').trim();
      if(!customerCode){ setStatus('Please select Debtor Code.', 'error'); return; }
      if(!docDate){ setStatus('Please enter Date.', 'error'); return; }
      if(!location){ setStatus('Location is missing.', 'error'); return; }
      if(!state.lines.length){ setStatus('Please add at least 1 line item.', 'error'); return; }
      
      const payload = {
        customerCode, customerName, salesAgent,
        debtorCode: customerCode, DebtorCode: customerCode,
        debtorName: customerName, DebtorName: customerName,
        SalesAgent: salesAgent,
        location, Location: location,
        docDate, remark: '',
        lines: state.lines.map(l => ({
          itemCode: l.itemCode,
          qty: l.total,
          uom: l.uom || 'KG',
          unitPrice: 0,
          amount: 0,
          taxCode: 'SR-0',
          taxRate: 0,
          packType: l.packType,
          cartonQty: l.qty,
          kg: l.kg,
          totalKg: l.total,
          location
        }))
      };
      
      const body = {
        type: 'DELIVERY_ORDER',
        client_request_id: makeClientRequestId('DO'),
        source: 'wp-ui',
        payload
      };
      
      const r = await apiPost(REST_JOB_POST, body);
      const jobId = r.jobId || r.id || (r.job && r.job.id);
      if (!jobId) {
        setStatus('Job created but jobId missing in response.', 'warning');
        showToast('warning', 'Job created, but jobId missing');
        return;
      }
      setStatus(`Job queued: #${jobId}. Waiting BridgeWorker...`, 'info');
      showToast('info', 'Job queued', `Job #${jobId}`);
      await pollJob(jobId);
    } catch(err) {
      setStatus('Submit error: ' + (err.message || String(err)), 'error');
      showModal('error', 'Submit error', escapeHtml(err.message || String(err)));
    } finally {
      state.isSubmitting = false;
      submitBtn.disabled = false;
      submitBtn.textContent = 'Save Delivery Order';
    }
  });
})();
</script>�s1�~�Ɔ
���������}Y�
?=K����<?php
/**
 * BASKET RETURN PAGE
 * Fields:
 * - Date
 * - Debtor Code
 * - Basket Return Qty
 * - Remark (optional)
 * - Back to List button removed
 */

if (!defined('ABSPATH')) exit;

if (!function_exists('ac_used_is_mobile_or_tablet')) {
    function ac_used_is_mobile_or_tablet() {
        if (wp_is_mobile()) {
            return true;
        }

        $ua = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');
        foreach (array('ipad', 'tablet', 'kindle', 'silk', 'playbook') as $needle) {
            if ($needle !== '' && strpos($ua, $needle) !== false) {
                return true;
            }
        }

        return false;
    }
}

if (ac_used_is_mobile_or_tablet()) {
    return;
}

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to record Basket Return.</div>';
    return;
}

$rest_nonce = wp_create_nonce('wp_rest');
$list_url   = home_url('/delivery-order-records/');
$ajax_url   = admin_url('admin-ajax.php');
$debtor_nonce = wp_create_nonce('ac_cs_debtor_search');
$rest_return_url = rest_url('ac/v1/basket/return');
?>

<div id="ac-basket-return-desktop-root" class="ac-br-wrap br-container"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-rest-return-url="<?php echo esc_attr($rest_return_url); ?>">
  <div class="br-head">
    <h1>Basket Return</h1>
  </div>

  <div class="br-card">
    <label class="br-label" for="ac_br_date">Date</label>
    <input id="ac_br_date" type="date" class="br-input" />

    <label class="br-label" for="acBrDebtorInput">Debtor Code</label>
    <div class="br-search-wrap" id="acBrDebtorWrapper"
         data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
         data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
      <input type="text" id="acBrDebtorInput" class="br-input" placeholder="Search debtor code..." autocomplete="off" />
      <input type="hidden" id="ac_br_debtor_code" value="" />
      <input type="hidden" id="ac_br_debtor_name" value="" />
      <div class="br-dropdown" id="acBrDebtorDropdown"></div>
    </div>

    <label class="br-label" for="ac_br_qty">Basket Return Qty</label>
    <input id="ac_br_qty" type="number" min="1" step="1" class="br-input" value="1" placeholder="Enter basket return quantity" />

    <label class="br-label" for="ac_br_remark">Remark (optional)</label>
    <textarea id="ac_br_remark" class="br-input br-textarea" placeholder="Optional remark"></textarea>

    <button id="ac_br_submit" class="br-btn" type="button">Save Basket Return</button>
    <div id="ac_br_status" class="br-status"></div>
  </div>
</div>

<style>
  .br-container{
    max-width:560px;
    margin:0 auto;
    padding:16px;
    font-family:"Segoe UI",Arial,sans-serif;
    background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
    border-radius:16px;
    color:#0f172a;
  }
  .br-head{
    display:flex;
    align-items:center;
    gap:12px;
    flex-wrap:wrap;
    margin-bottom:12px;
  }
  .br-head h1{
    margin:0;
    font-size:clamp(24px,6vw,34px);
    font-weight:800;
    line-height:1.1;
  }
  .br-card{
    background:#fff;
    border:1px solid #e2ebe3;
    border-radius:14px;
    box-shadow:0 4px 16px rgba(15,23,42,.05);
    padding:16px;
  }
  .br-label{
    display:block;
    font-size:13px;
    color:#4b5563;
    margin-bottom:6px;
    font-weight:700;
  }
  .br-input{
    width:100%;
    min-height:48px;
    font-size:16px;
    padding:12px;
    border-radius:10px;
    border:1px solid #d1d5db;
    margin-bottom:14px;
    box-sizing:border-box;
    background:#fff;
    color:#111;
  }
  .br-textarea{ min-height:100px; resize:vertical; }
  .br-input:focus,
  .br-btn:focus{
    outline:none;
    border-color:#28a745;
    box-shadow:0 0 0 3px rgba(40,167,69,.18);
  }
  .br-search-wrap{ position:relative; }
  .br-dropdown{
    position:absolute;
    left:0;
    right:0;
    top:calc(100% - 10px);
    background:#fff;
    border:1px solid #dcdcdc;
    border-radius:10px;
    z-index:80;
    max-height:260px;
    overflow:auto;
    box-shadow:0 8px 24px rgba(0,0,0,.10);
    display:none;
  }
  .br-dropdown.active{ display:block; }
  .br-item{
    padding:10px 12px;
    cursor:pointer;
    border-bottom:1px solid #f1f1f1;
    display:flex;
    flex-direction:column;
    gap:2px;
    font-size:15px;
  }
  .br-item:last-child{ border-bottom:none; }
  .br-item:hover,
  .br-item.active{ background:#f3f9f4; }
  .br-btn{
    width:100%;
    min-height:52px;
    border:none;
    border-radius:12px;
    background:linear-gradient(90deg,#28a745,#1e7e34);
    color:#fff;
    font-size:20px;
    font-weight:800;
    padding:12px 14px;
    cursor:pointer;
  }
  .br-btn[disabled]{
    opacity:.7;
    cursor:not-allowed;
  }
  .br-status{
    margin-top:10px;
    font-size:14px;
    color:#334155;
    min-height:20px;
  }

  @media (max-width: 768px){
    .br-container{
      max-width:none;
      margin-left:-8px;
      margin-right:-8px;
      border-radius:0;
      padding:12px;
    }
    .br-card{ padding:14px; }
  }
</style>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
(function(){
  const wrap = document.getElementById('ac-basket-return-desktop-root');
  if (!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE = wrap.dataset.restNonce || '';
  const REST_RETURN_URL = wrap.dataset.restReturnUrl || '';

  const $ = (id) => wrap.querySelector('#' + id);
  let isSubmitting = false;

  function esc(s){
    if (s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
  }

  function toast(icon, title, text=''){
    if (!window.Swal) return;
    Swal.fire({
      toast:true,
      position:'top-end',
      icon, title, text,
      showConfirmButton:false,
      timer:2200,
      timerProgressBar:true
    });
  }

  function setStatus(text){
    const el = $('ac_br_status');
    if (el) el.textContent = text || '';
  }

  function whole(n){
    const x = Number(n);
    if (!isFinite(x) || x < 0) return 0;
    return Math.round(x);
  }

  async function apiPost(url, body){
    const res = await fetch(url, {
      method:'POST',
      credentials:'same-origin',
      headers:{
        'Content-Type':'application/json',
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      body: JSON.stringify(body)
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  (function initDate(){
    const d = new Date();
    $('ac_br_date').value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
  })();

  (function initDebtorDropdown(){
    const wrapper = $('acBrDebtorWrapper');
    if (!wrapper) return;

    const AJAX_URL = wrapper.dataset.ajaxUrl;
    const NONCE = wrapper.dataset.nonce;
    const input = $('acBrDebtorInput');
    const hiddenCode = $('ac_br_debtor_code');
    const hiddenName = $('ac_br_debtor_name');
    const dropdown = $('acBrDebtorDropdown');

    let items = [];
    let activeIndex = -1;
    let abortController = null;
    let timer = null;

    const show = () => dropdown.classList.add('active');
    const hide = () => { dropdown.classList.remove('active'); activeIndex = -1; };

    const render = () => {
      if (!items.length) {
        dropdown.innerHTML = '<div class="br-item"><small>No debtor found</small></div>';
        show();
        return;
      }
      dropdown.innerHTML = items.map((it, idx) => {
        const code = it.code || it.debtorCode || it.id || '';
        const name = it.name || it.debtorName || it.description || '';
        return `<div class="br-item" data-index="${idx}"><strong>${esc(code)}</strong><small>${esc(name)}</small></div>`;
      }).join('');
      show();
    };

    const pick = (idx) => {
      if (idx < 0 || idx >= items.length) return;
      const it = items[idx];
      const code = it.code || it.debtorCode || it.id || '';
      const name = it.name || it.debtorName || it.description || '';
      input.value = code;
      hiddenCode.value = code;
      hiddenName.value = name;
      hide();
    };

    const search = async (q) => {
      if (abortController) abortController.abort();
      abortController = new AbortController();
      const query = (q || '').trim();
      if (query.length < 1) { hide(); return; }

      dropdown.innerHTML = '<div class="br-item"><small>Searching...</small></div>';
      show();

      try {
        const url = `${AJAX_URL}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(NONCE)}&q=${encodeURIComponent(query)}`;
        const res = await fetch(url, { signal: abortController.signal, credentials:'same-origin' });
        const data = await res.json();
        if (!data.success) throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');
        items = (data.data && data.data.items) ? data.data.items : [];
        render();
      } catch (err) {
        if (err.name === 'AbortError') return;
        dropdown.innerHTML = '<div class="br-item"><small>Failed to load</small></div>';
        show();
      }
    };

    input.addEventListener('input', () => {
      hiddenCode.value = '';
      hiddenName.value = '';
      clearTimeout(timer);
      timer = setTimeout(() => search(input.value), 300);
    });

    input.addEventListener('keydown', (e) => {
      if (!dropdown.classList.contains('active')) return;
      const nodes = Array.from(dropdown.querySelectorAll('.br-item[data-index]'));
      if (!nodes.length) return;

      if (e.key === 'ArrowDown') {
        e.preventDefault();
        activeIndex = Math.min(activeIndex + 1, nodes.length - 1);
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        activeIndex = Math.max(activeIndex - 1, 0);
      } else if (e.key === 'Enter') {
        if (activeIndex >= 0) {
          e.preventDefault();
          pick(activeIndex);
        }
      } else if (e.key === 'Escape') {
        hide();
      }

      nodes.forEach((n, i) => n.classList.toggle('active', i === activeIndex));
    });

    dropdown.addEventListener('click', (e) => {
      const node = e.target.closest('.br-item[data-index]');
      if (!node) return;
      pick(parseInt(node.dataset.index, 10));
    });

    document.addEventListener('click', (e) => {
      if (!wrapper.contains(e.target)) hide();
    });
  })();

  $('ac_br_submit').addEventListener('click', async () => {
    if (isSubmitting) return;

    const debtorCode = ($('ac_br_debtor_code').value || '').trim();
    const debtorName = ($('ac_br_debtor_name').value || '').trim();
    const docDate = ($('ac_br_date').value || '').trim();
    const basketQty = whole(($('ac_br_qty').value || '0'));
    const remark = ($('ac_br_remark').value || '').trim();

    if (!debtorCode) { toast('error', 'Debtor required'); setStatus('Please select Debtor Code.'); return; }
    if (!docDate) { toast('error', 'Date required'); setStatus('Please enter Date.'); return; }
    if (basketQty <= 0) { toast('error', 'Invalid quantity'); setStatus('Basket Return Qty must be greater than 0.'); return; }
    if (!REST_RETURN_URL) { toast('error', 'Config error'); setStatus('Basket return endpoint is missing.'); return; }

    const submitBtn = $('ac_br_submit');

    try {
      isSubmitting = true;
      submitBtn.disabled = true;
      submitBtn.textContent = 'Saving...';
      setStatus('Saving basket return...');

      const payload = {
        debtorCode,
        debtorName,
        docDate,
        basketQty,
        remark
      };

      const res = await apiPost(REST_RETURN_URL, payload);
      if (!res || !res.ok) {
        throw new Error((res && (res.message || res.error)) ? (res.message || res.error) : 'Failed to save basket return');
      }

      const suffix = res.duplicate ? ' (duplicate ignored)' : '';
      setStatus(`Saved basket return for ${debtorCode}: ${basketQty}${suffix}`);
      toast('success', 'Basket return saved', `${debtorCode} | Qty ${basketQty}${suffix}`);

      $('ac_br_qty').value = '1';
      $('ac_br_remark').value = '';
      $('ac_br_qty').focus();
    } catch (err) {
      const msg = err && err.message ? err.message : 'Failed to save basket return';
      setStatus(msg);
      toast('error', 'Save failed', msg);
    } finally {
      isSubmitting = false;
      submitBtn.disabled = false;
      submitBtn.textContent = 'Save Basket Return';
    }
  });
})();
</script>

<!-- BasketReturn.desktop full copy variant -->
<style id="BasketReturn-desktop-full-copy-variant-style">
  .br-container{max-width:1100px !important;padding:24px !important;border-radius:18px !important;background:linear-gradient(180deg,#f0f7f2 0%,#eaf4ee 100%) !important;}
  .br-head h1{font-size:36px !important;}
  .br-card{padding:20px !important;margin-bottom:0 !important;}
  .br-desktop-grid{display:grid;grid-template-columns:1fr 320px;gap:14px;margin-top:12px;}
  .br-info{background:#fff;border:1px solid #dbe8de;border-radius:14px;padding:14px;box-shadow:0 4px 12px rgba(15,23,42,.04);}
</style>
<script>
(function(){
  const container = document.getElementById('ac-basket-return-desktop-root');
  const card = container ? container.querySelector('.br-card') : null;
  if (!container || !card || container.querySelector('.br-desktop-grid')) return;

  const grid = document.createElement('div');
  grid.className = 'br-desktop-grid';

  const info = document.createElement('aside');
  info.className = 'br-info';
  info.innerHTML = `
    <h3 style="margin:0 0 8px;font-size:18px;color:#0f172a;">Return Summary</h3>
    <p style="margin:0;font-size:14px;line-height:1.45;color:#475569;">Desktop view keeps more context visible while recording returns.</p>
    <div style="font-size:12px;color:#64748b;font-weight:700;margin-top:10px;">Current Qty</div>
    <div style="font-size:30px;font-weight:800;color:#0f172a;line-height:1.1;" id="acBrDesktopQty">0</div>
    <div style="font-size:12px;color:#64748b;font-weight:700;margin-top:10px;">Selected Debtor</div>
    <div style="font-size:20px;font-weight:800;color:#0f172a;line-height:1.1;" id="acBrDesktopDebtor">-</div>
  `;

  card.parentNode.insertBefore(grid, card);
  grid.appendChild(card);
  grid.appendChild(info);

  const qtyInput = container.querySelector('#ac_br_qty');
  const debtorInput = container.querySelector('#acBrDebtorInput');

  function update(){
    const qty = Math.max(0, Math.round(Number(qtyInput ? qtyInput.value : 0) || 0));
    const debtor = ((debtorInput ? debtorInput.value : '') || '').trim();
    const q = container.querySelector('#acBrDesktopQty');
    const d = container.querySelector('#acBrDesktopDebtor');
    if (q) q.textContent = String(qty);
    if (d) d.textContent = debtor || '-';
  }

  if (qtyInput) qtyInput.addEventListener('input', update);
  if (debtorInput) debtorInput.addEventListener('input', update);
  update();
})();
</script>�}Y��ެ�����������N�
??�<?php
/**
 * COMBINED: DELIVERY ORDER + BASKET RETURN (Mobile Tabs)
 * - Both features work exactly as original standalone versions
 * - Tabs switch without breaking JavaScript state
 */

if (!defined('ABSPATH')) exit;

// -------------------------------------------------------------------
// Shared mobile detection & login guard
// -------------------------------------------------------------------


if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to continue.</div>';
    return;
}

// -------------------------------------------------------------------
// REST & AJAX nonces (shared)
// -------------------------------------------------------------------
$rest_nonce          = wp_create_nonce('wp_rest');
$ajax_url            = admin_url('admin-ajax.php');
$debtor_nonce        = wp_create_nonce('ac_cs_debtor_search');
$item_suggest_nonce  = wp_create_nonce('ac_itemcode_suggest');
$default_location    = 'HQ';
$today_date          = current_time('Y-m-d');

// Delivery Order endpoints
$edit_base           = home_url('/index.php/delivery-order-edit/');
$REST_JOB_POST       = rest_url('ac/v1/job');
$REST_JOB_BASE       = rest_url('ac/v1/job/');

// Basket Return endpoint
$rest_return_url     = rest_url('ac/v1/basket/return');
$show_debtor_code    = false;   // display debtor code in customer field
?>
<div id="ac-combined-mobile-tabs" class="ac-combined-root">
  
  <!-- TAB BAR -->
  <div class="ac-tab-bar">
    <button type="button" class="ac-tab-btn active" data-tab="delivery">📦 Delivery Order</button>
    <button type="button" class="ac-tab-btn" data-tab="basket">🧺 Basket Return</button>
  </div>

  <!-- ========== TAB 1: DELIVERY ORDER (exact original HTML) ========== -->
  <div id="ac-tab-delivery" class="ac-tab-pane active" data-tab="delivery">
    <div id="ac-do-create-mobile-root"
         class="ac-do-fast-wrap"
         data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
         data-edit-base="<?php echo esc_attr($edit_base); ?>"
         data-rest-job-post="<?php echo esc_attr($REST_JOB_POST); ?>"
         data-rest-job-base="<?php echo esc_attr($REST_JOB_BASE); ?>"
         data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
         data-item-suggest-nonce="<?php echo esc_attr($item_suggest_nonce); ?>">

      <div class="ac-do-fast-shell">
        <div class="ac-do-fast-head">
          <h1>Create Delivery Order</h1>
          <div class="ac-do-fast-sub">Fast key in for driver</div>
        </div>

        <div class="ac-do-fast-panel">
          <div class="ac-do-section">
            <div class="ac-do-section-title">Delivery Info</div>
            <input id="ac_do_date" type="hidden" value="<?php echo esc_attr($today_date); ?>" />
            <input type="hidden" id="ac_do_customer_name" value="" />
            <input type="hidden" id="ac_do_sales_agent" value="" />
            <input type="hidden" id="ac_do_location" value="<?php echo esc_attr($default_location); ?>" />

            <label class="ac-do-label" for="acDoDebtorInput">Debtor Name</label>
            <div class="ac-do-search-wrap" id="acDoDebtorWrapper"
                 data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
                 data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
              <input type="text" id="acDoDebtorInput" class="ac-do-input" placeholder="Tap to search debtor..." autocomplete="off" readonly />
              <input type="hidden" id="ac_do_customer" value="" />
            </div>
            <div id="ac_do_status" class="ac-so-status"></div>
          </div>

          <div class="ac-do-section">
            <div class="ac-do-section-title">Add Item</div>
            <label class="ac-do-label" for="ac_do_item_name">Item Name</label>
            <div class="ac-do-search-wrap">
              <input id="ac_do_item_name" type="text" class="ac-do-input" placeholder="Tap to search item..." autocomplete="off" readonly>
              <input id="ac_do_item" type="hidden" value="">
              <input id="ac_do_item_display" type="hidden" value="">
            </div>

            <label class="ac-do-label">Type</label>
            <div class="ac-do-type-toggle" id="ac_do_pack_type_toggle">
              <button type="button" class="ac-do-type-btn active" data-pack-type="CARTON">Carton</button>
              <button type="button" class="ac-do-type-btn" data-pack-type="BASKET">Basket</button>
            </div>
            <select id="ac_do_pack_type" style="display:none;">
              <option value="CARTON" selected>Carton</option>
              <option value="BASKET">Basket</option>
            </select>

            <div class="ac-do-grid-2">
              <div>
                <label class="ac-do-label" for="ac_do_qty">Qty</label>
                <input id="ac_do_qty" type="number" min="0" step="1" value="" class="ac-do-input" inputmode="numeric" enterkeyhint="done" placeholder="Qty">
              </div>
              <div>
                <label class="ac-do-label" for="ac_do_kg">KG</label>
                <input id="ac_do_kg" type="number" min="0" step="1" value="" class="ac-do-input" inputmode="numeric" enterkeyhint="done" placeholder="KG">
              </div>
            </div>
            <input id="ac_do_total" type="hidden" value="0">
            <div class="ac-do-mini-preview" id="ac_do_line_preview" style="display:none;"></div>
            <button id="ac_do_addline" class="ac-do-main-btn" type="button">Add Item</button>
          </div>

          <div class="ac-do-section">
            <div class="ac-do-list-head">
              <div class="ac-do-section-title" style="margin:0;">Items</div>
              <div class="ac-do-count" id="ac_do_line_count">0 line</div>
            </div>
            <div id="ac_do_lines" class="ac-do-lines"><b>No items yet</b></div>
          </div>
        </div>

        <div class="ac-do-sticky-save">
          <button class="ac-do-main-btn ac-do-save-btn" id="ac_do_submit" type="button">Save Delivery Order</button>
        </div>
      </div>

      <!-- Modal popup (stable centered) -->
      <div class="ac-do-picker-modal" id="ac_do_picker_modal" aria-hidden="true">
        <div class="ac-do-picker-backdrop" id="ac_do_picker_backdrop"></div>
        <div class="ac-do-picker-sheet" role="dialog" aria-modal="true" aria-labelledby="ac_do_picker_title">
          <div class="ac-do-picker-head">
            <div class="ac-do-picker-title" id="ac_do_picker_title">Search</div>
            <button type="button" class="ac-do-picker-close" id="ac_do_picker_close" aria-label="Close">✕</button>
          </div>
          <div class="ac-do-picker-body">
            <input type="text" id="ac_do_picker_search" class="ac-do-input ac-do-picker-search" placeholder="Type to search..." autocomplete="off" enterkeyhint="done">
            <div class="ac-do-picker-results" id="ac_do_picker_results"></div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- ========== TAB 2: BASKET RETURN (exact original HTML) ========== -->
  <div id="ac-tab-basket" class="ac-tab-pane" data-tab="basket">
    <div id="ac-basket-return-mobile-root" class="ac-br-wrap br-container"
         data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
         data-rest-return-url="<?php echo esc_attr($rest_return_url); ?>"
         data-show-debtor-code="<?php echo $show_debtor_code ? '1' : '0'; ?>">
      <div class="br-head">
        <h1>Basket Return</h1>
      </div>
      <div class="br-card">
        <input id="ac_br_date" type="hidden" value="<?php echo esc_attr($today_date); ?>" />

        <label class="br-label" for="acBrDebtorInput">Customer Name</label>
        <div class="br-search-wrap" id="acBrDebtorWrapper"
             data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
             data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
          <input type="text" id="acBrDebtorInput" class="br-input" placeholder="Search customer name..." autocomplete="off" />
          <input type="hidden" id="ac_br_debtor_code" value="" />
          <input type="hidden" id="ac_br_debtor_name" value="" />
          <div class="br-dropdown" id="acBrDebtorDropdown"></div>
        </div>

        <label class="br-label" for="ac_br_qty">Basket Return Qty</label>
        <input id="ac_br_qty" type="number" min="1" step="1" class="br-input" value="1" placeholder="Enter basket return quantity" />

        <label class="br-label" for="ac_br_remark">Remark (optional)</label>
        <textarea id="ac_br_remark" class="br-input br-textarea" placeholder="Optional remark"></textarea>

        <button id="ac_br_submit" class="br-btn" type="button">Save Basket Return</button>
        <div id="ac_br_status" class="br-status"></div>
      </div>
    </div>
  </div>
</div>

<!-- ================================================================= -->
<!-- Combined Styles (Delivery Order + Basket Return + Tabs)          -->
<!-- ================================================================= -->
<style>
  /* ----- TABS ----- */
  .ac-combined-root { width:100%; background:#f4f7f5; font-family:"Segoe UI",Arial,sans-serif; box-sizing:border-box; }
  .ac-tab-bar { display:flex; gap:4px; background:#eef2ef; padding:8px 12px 0 12px; border-bottom:1px solid #d0dbd0; }
  .ac-tab-btn { flex:1; background:#eef2ef; border:none; font-size:15px; font-weight:800; padding:12px 8px; border-radius:16px 16px 0 0; color:#2d6a4f; cursor:pointer; transition:all 0.2s ease; }
  .ac-tab-btn.active { background:#f4f7f5; color:#0f5b2e; border-bottom:3px solid #16a34a; }
  .ac-tab-pane { display:none; padding:6px 0; }
  .ac-tab-pane.active { display:block; }

  /* ----- Delivery Order Styles (original, unchanged) ----- */
  .ac-do-fast-wrap{ width:100%; background:#f4f7f5; padding:8px; box-sizing:border-box; color:#1f2937; }
  .ac-do-fast-shell{ max-width:760px; margin:0 auto; }
  .ac-do-fast-head{ margin-bottom:8px; }
  .ac-do-fast-head h1{ margin:0; font-size:20px; font-weight:800; color:#0f172a; }
  .ac-do-fast-sub{ margin-top:2px; font-size:12px; color:#64748b; font-weight:600; }
  .ac-do-fast-panel{ display:flex; flex-direction:column; gap:8px; }
  .ac-do-section{ background:#fff; border:1px solid #e2e8f0; border-radius:10px; padding:10px; box-shadow:0 2px 10px rgba(15,23,42,.03); }
  .ac-do-section-title{ font-size:13px; font-weight:800; color:#0f172a; margin-bottom:8px; }
  .ac-do-label{ display:block; font-size:11px; font-weight:700; color:#475569; margin:0 0 4px; }
  .ac-do-grid-2{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; margin-bottom:8px; }
  .ac-do-input{ width:100%; min-height:40px; border:1px solid #d1d5db; border-radius:8px; padding:9px 10px; font-size:16px; color:#111827; background:#fff; box-sizing:border-box; margin-bottom:8px; }
  .ac-do-grid-2 .ac-do-input{ margin-bottom:0; }
  .ac-do-input:focus, .ac-do-type-btn:focus, .ac-do-main-btn:focus, .ac-do-del-btn:focus{ outline:none; border-color:#15803d; box-shadow:0 0 0 2px rgba(21,128,61,.12); }
  .ac-do-search-wrap{ position:relative; margin-bottom:8px; }
  .ac-do-search-wrap .ac-do-input{ margin-bottom:0; }
  .ac-do-type-toggle{ display:flex; gap:6px; margin-bottom:8px; }
  .ac-do-type-btn{ flex:1; min-height:38px; border-radius:8px; border:1px solid #16a34a; background:#fff; color:#16a34a; font-size:14px; font-weight:800; cursor:pointer; }
  .ac-do-type-btn.active{ background:linear-gradient(90deg,#16a34a,#15803d); color:#fff; }
  .ac-do-mini-preview{ display:none; margin-bottom:8px; border:1px solid #dcfce7; background:#f0fdf4; border-radius:8px; padding:8px 10px; font-size:12px; color:#166534; font-weight:700; line-height:1.45; }
  .ac-do-main-btn{ width:100%; min-height:42px; border:none; border-radius:10px; background:linear-gradient(90deg,#16a34a,#15803d); color:#fff; font-size:15px; font-weight:800; cursor:pointer; }
  .ac-do-main-btn:disabled{ opacity:.7; cursor:not-allowed; }
  .ac-do-list-head{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:8px; }
  .ac-do-count{ font-size:11px; font-weight:800; color:#475569; background:#f8fafc; border:1px solid #e2e8f0; border-radius:999px; padding:4px 8px; white-space:nowrap; }
  .ac-do-lines{ display:flex; flex-direction:column; gap:8px; }
  .ac-do-line-card{ border:1px solid #e2e8f0; border-radius:10px; background:#fff; padding:9px; }
  .ac-do-line-top{ display:flex; align-items:flex-start; justify-content:space-between; gap:8px; margin-bottom:8px; }
  .ac-do-line-name{ font-size:14px; font-weight:800; color:#0f172a; word-break:break-word; }
  .ac-do-line-badge{ display:inline-flex; border-radius:999px; border:1px solid #bbf7d0; background:#f0fdf4; color:#166534; font-size:10px; font-weight:800; text-transform:uppercase; padding:4px 8px; }
  .ac-do-line-meta{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:6px; margin-bottom:8px; }
  .ac-do-line-stat{ background:#f8fafc; border:1px solid #e2e8f0; border-radius:8px; padding:7px 8px; }
  .ac-do-line-meta-label{ display:block; font-size:10px; color:#64748b; font-weight:700; margin-bottom:2px; }
  .ac-do-line-meta-value{ display:block; font-size:13px; color:#0f172a; font-weight:800; }
  .ac-do-del-btn{ width:100%; min-height:34px; border:none; border-radius:8px; background:#ef4444; color:#fff; font-size:13px; font-weight:800; cursor:pointer; }
  .ac-do-no-items{ border:1px dashed #cbd5e1; border-radius:8px; background:#f8fafc; color:#64748b; padding:10px; text-align:center; font-size:13px; font-weight:700; }
  .ac-do-sticky-save{ position:sticky; bottom:0; z-index:25; padding:8px 0 calc(8px + env(safe-area-inset-bottom)); background:linear-gradient(to top, rgba(244,247,245,.98), rgba(244,247,245,0)); }
  .ac-do-save-btn{ min-height:46px; font-size:16px; }
  .ac-so-status{ display:none; }

  /* Delivery Order modal */
  body.ac-do-picker-open{ overflow:hidden; touch-action:none; }
  .ac-do-dropdown{ display:none !important; }
  #acDoDebtorInput, #ac_do_item_name{ cursor:pointer; background:#fff; }
  .ac-do-picker-modal{ position:fixed; inset:0; z-index:9999; display:none; align-items:center; justify-content:center; padding:8px; box-sizing:border-box; }
  .ac-do-picker-modal.active{ display:flex; }
  .ac-do-picker-backdrop{ position:absolute; inset:0; background:rgba(15,23,42,.45); }
  .ac-do-picker-sheet{ position:relative; width:100%; max-width:760px; height:min(88dvh,720px); background:#fff; border-radius:16px; box-shadow:0 10px 30px rgba(0,0,0,.18); overflow:hidden; display:flex; flex-direction:column; }
  .ac-do-picker-head{ flex:0 0 auto; display:flex; align-items:center; justify-content:space-between; gap:10px; padding:12px 12px 10px; border-bottom:1px solid #e5e7eb; background:#fff; }
  .ac-do-picker-title{ font-size:15px; font-weight:800; color:#0f172a; }
  .ac-do-picker-close{ width:36px; height:36px; border:none; border-radius:10px; background:#f1f5f9; color:#0f172a; font-size:18px; font-weight:800; cursor:pointer; }
  .ac-do-picker-body{ flex:1 1 auto; min-height:0; display:flex; flex-direction:column; padding:10px; overflow:hidden; }
  .ac-do-picker-search{ flex:0 0 auto; margin-bottom:10px; min-height:42px; }
  .ac-do-picker-results{ flex:1 1 auto; overflow:auto; -webkit-overflow-scrolling:touch; display:flex; flex-direction:column; gap:8px; padding-bottom:8px; }
  .ac-do-picker-item{ width:100%; text-align:left; border:1px solid #e2e8f0; border-radius:12px; background:#fff; padding:12px; cursor:pointer; }
  .ac-do-picker-item:hover, .ac-do-picker-item.active{ background:#f0fdf4; border-color:#bbf7d0; }
  .ac-do-picker-item-main{ display:block; font-size:15px; font-weight:800; color:#0f172a; }
  .ac-do-picker-item-sub{ display:block; margin-top:4px; font-size:12px; color:#64748b; }
  .ac-do-picker-note{ border:1px dashed #cbd5e1; border-radius:12px; background:#f8fafc; color:#64748b; padding:14px 12px; text-align:center; font-size:13px; font-weight:700; }

  /* ----- Basket Return Styles (original, unchanged) ----- */
 ��N�������������|D
??�<?php
/**
 * DELIVERY ORDER CREATE PAGE - STABLE CENTERED POPUP MODAL
 * - Enter key closes keyboard on mobile (no auto-focus next)
 * - No auto navigation after debtor/item selection
 * - Qty/KG start empty, preview hidden until both filled
 */

if (!defined('ABSPATH')) exit;

if (!function_exists('ac_used_is_mobile_or_tablet')) {
    function ac_used_is_mobile_or_tablet() {
        if (wp_is_mobile()) {
            return true;
        }

        $ua = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');
        foreach (array('ipad', 'tablet', 'kindle', 'silk', 'playbook') as $needle) {
            if ($needle !== '' && strpos($ua, $needle) !== false) {
                return true;
            }
        }

        return false;
    }
}

if (!ac_used_is_mobile_or_tablet()) {
    return;
}

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">
        Please log in to create Delivery Order.
    </div>';
    return;
}

$rest_nonce = wp_create_nonce('wp_rest');

$edit_base = home_url('/index.php/delivery-order-edit/');

$REST_JOB_POST = rest_url('ac/v1/job');
$REST_JOB_BASE = rest_url('ac/v1/job/');

$ajax_url           = admin_url('admin-ajax.php');
$debtor_nonce       = wp_create_nonce('ac_cs_debtor_search');
$item_suggest_nonce = wp_create_nonce('ac_itemcode_suggest');
$default_location   = 'HQ';
$today_date         = current_time('Y-m-d');
?>


<div id="ac-do-create-mobile-root"
     class="ac-do-fast-wrap"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-edit-base="<?php echo esc_attr($edit_base); ?>"
     data-rest-job-post="<?php echo esc_attr($REST_JOB_POST); ?>"
     data-rest-job-base="<?php echo esc_attr($REST_JOB_BASE); ?>"
     data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
     data-item-suggest-nonce="<?php echo esc_attr($item_suggest_nonce); ?>">

  <div class="ac-do-fast-shell">

    <div class="ac-do-fast-head">
      <h1>Create Delivery Order</h1>
      <div class="ac-do-fast-sub">Fast key in for driver</div>
    </div>

    <div class="ac-do-fast-panel">

      <div class="ac-do-section">
        <div class="ac-do-section-title">Delivery Info</div>

        <!-- Hidden date, still defaults to today -->
        <input id="ac_do_date" type="hidden" value="<?php echo esc_attr($today_date); ?>" />

        <input type="hidden" id="ac_do_customer_name" value="" />
        <input type="hidden" id="ac_do_sales_agent" value="" />
        <input type="hidden" id="ac_do_location" value="<?php echo esc_attr($default_location); ?>" />

        <label class="ac-do-label" for="acDoDebtorInput">Debtor Name</label>
        <div class="ac-do-search-wrap"
             id="acDoDebtorWrapper"
             data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
             data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
          <input type="text"
                 id="acDoDebtorInput"
                 class="ac-do-input"
                 placeholder="Tap to search debtor..."
                 autocomplete="off"
                 readonly />
          <input type="hidden" id="ac_do_customer" value="" />
        </div>

        <div id="ac_do_status" class="ac-so-status"></div>
      </div>

      <div class="ac-do-section">
        <div class="ac-do-section-title">Add Item</div>

        <label class="ac-do-label" for="ac_do_item_name">Item Name</label>
        <div class="ac-do-search-wrap">
          <input id="ac_do_item_name" type="text" class="ac-do-input" placeholder="Tap to search item..." autocomplete="off" readonly>
          <input id="ac_do_item" type="hidden" value="">
          <input id="ac_do_item_display" type="hidden" value="">
        </div>

        <label class="ac-do-label">Type</label>
        <div class="ac-do-type-toggle" id="ac_do_pack_type_toggle">
          <button type="button" class="ac-do-type-btn active" data-pack-type="CARTON">Carton</button>
          <button type="button" class="ac-do-type-btn" data-pack-type="BASKET">Basket</button>
        </div>
        <select id="ac_do_pack_type" style="display:none;">
          <option value="CARTON" selected>Carton</option>
          <option value="BASKET">Basket</option>
        </select>

        <div class="ac-do-grid-2">
          <div>
            <label class="ac-do-label" for="ac_do_qty">Qty</label>
            <input id="ac_do_qty" type="number" min="0" step="1" value="" class="ac-do-input" inputmode="numeric" enterkeyhint="done" placeholder="Qty">
          </div>

          <div>
            <label class="ac-do-label" for="ac_do_kg">KG</label>
            <input id="ac_do_kg" type="number" min="0" step="1" value="" class="ac-do-input" inputmode="numeric" enterkeyhint="done" placeholder="KG">
          </div>
        </div>

        <input id="ac_do_total" type="hidden" value="0">

        <div class="ac-do-mini-preview" id="ac_do_line_preview" style="display:none;"></div>

        <button id="ac_do_addline" class="ac-do-main-btn" type="button">Add Item</button>
      </div>

      <div class="ac-do-section">
        <div class="ac-do-list-head">
          <div class="ac-do-section-title" style="margin:0;">Items</div>
          <div class="ac-do-count" id="ac_do_line_count">0 line</div>
        </div>

        <div id="ac_do_lines" class="ac-do-lines"><b>No items yet</b></div>
      </div>

    </div>

    <div class="ac-do-sticky-save">
      <button class="ac-do-main-btn ac-do-save-btn" id="ac_do_submit" type="button">Save Delivery Order</button>
    </div>

  </div>

  <!-- STABLE CENTERED POPUP MODAL -->
  <div class="ac-do-picker-modal" id="ac_do_picker_modal" aria-hidden="true">
    <div class="ac-do-picker-backdrop" id="ac_do_picker_backdrop"></div>

    <div class="ac-do-picker-sheet" role="dialog" aria-modal="true" aria-labelledby="ac_do_picker_title">
      <div class="ac-do-picker-head">
        <div class="ac-do-picker-title" id="ac_do_picker_title">Search</div>
        <button type="button" class="ac-do-picker-close" id="ac_do_picker_close" aria-label="Close">✕</button>
      </div>

      <div class="ac-do-picker-body">
        <input type="text" id="ac_do_picker_search" class="ac-do-input ac-do-picker-search" placeholder="Type to search..." autocomplete="off" enterkeyhint="done">
        <div class="ac-do-picker-results" id="ac_do_picker_results"></div>
      </div>
    </div>
  </div>
</div>

<style>
  .ac-do-fast-wrap{
    width:100%;
    background:#f4f7f5;
    padding:8px;
    box-sizing:border-box;
    font-family:"Segoe UI", Arial, sans-serif;
    color:#1f2937;
  }

  .ac-do-fast-shell{
    max-width:760px;
    margin:0 auto;
  }

  .ac-do-fast-head{
    margin-bottom:8px;
  }

  .ac-do-fast-head h1{
    margin:0;
    font-size:20px;
    line-height:1.15;
    font-weight:800;
    color:#0f172a;
  }

  .ac-do-fast-sub{
    margin-top:2px;
    font-size:12px;
    color:#64748b;
    font-weight:600;
  }

  .ac-do-fast-panel{
    display:flex;
    flex-direction:column;
    gap:8px;
  }

  .ac-do-section{
    background:#ffffff;
    border:1px solid #e2e8f0;
    border-radius:10px;
    padding:10px;
    box-shadow:0 2px 10px rgba(15,23,42,.03);
  }

  .ac-do-section-title{
    font-size:13px;
    font-weight:800;
    color:#0f172a;
    margin-bottom:8px;
  }

  .ac-do-label{
    display:block;
    font-size:11px;
    font-weight:700;
    color:#475569;
    margin:0 0 4px;
  }

  .ac-do-grid-2{
    display:grid;
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:8px;
    margin-bottom:8px;
  }

  .ac-do-input{
    width:100%;
    min-height:40px;
    border:1px solid #d1d5db;
    border-radius:8px;
    padding:9px 10px;
    font-size:16px;
    color:#111827;
    background:#fff;
    box-sizing:border-box;
    margin-bottom:8px;
  }

  .ac-do-grid-2 .ac-do-input{
    margin-bottom:0;
  }

  .ac-do-input:focus,
  .ac-do-type-btn:focus,
  .ac-do-main-btn:focus,
  .ac-do-del-btn:focus{
    outline:none;
    border-color:#15803d;
    box-shadow:0 0 0 2px rgba(21,128,61,.12);
  }

  .ac-do-search-wrap{
    position:relative;
    margin-bottom:8px;
  }

  .ac-do-search-wrap .ac-do-input{
    margin-bottom:0;
  }

  .ac-do-type-toggle{
    display:flex;
    gap:6px;
    margin-bottom:8px;
  }

  .ac-do-type-btn{
    flex:1;
    min-height:38px;
    border-radius:8px;
    border:1px solid #16a34a;
    background:#fff;
    color:#16a34a;
    font-size:14px;
    font-weight:800;
    cursor:pointer;
  }

  .ac-do-type-btn.active{
    background:linear-gradient(90deg,#16a34a,#15803d);
    color:#fff;
  }

  .ac-do-mini-preview{
    display:none;
    margin-bottom:8px;
    border:1px solid #dcfce7;
    background:#f0fdf4;
    border-radius:8px;
    padding:8px 10px;
    font-size:12px;
    color:#166534;
    font-weight:700;
    line-height:1.45;
  }

  .ac-do-main-btn{
    width:100%;
    min-height:42px;
    border:none;
    border-radius:10px;
    background:linear-gradient(90deg,#16a34a,#15803d);
    color:#fff;
    font-size:15px;
    font-weight:800;
    cursor:pointer;
  }

  .ac-do-main-btn:disabled{
    opacity:.7;
    cursor:not-allowed;
  }

  .ac-do-list-head{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:8px;
    margin-bottom:8px;
  }

  .ac-do-count{
    font-size:11px;
    font-weight:800;
    color:#475569;
    background:#f8fafc;
    border:1px solid #e2e8f0;
    border-radius:999px;
    padding:4px 8px;
    white-space:nowrap;
  }

  .ac-do-lines{
    display:flex;
    flex-direction:column;
    gap:8px;
  }

  .ac-do-line-card{
    border:1px solid #e2e8f0;
    border-radius:10px;
    background:#fff;
    padding:9px;
  }

  .ac-do-line-top{
    display:flex;
    align-items:flex-start;
    justify-content:space-between;
    gap:8px;
    margin-bottom:8px;
  }

  .ac-do-line-name{
    font-size:14px;
    line-height:1.3;
    font-weight:800;
    color:#0f172a;
    word-break:break-word;
  }

  .ac-do-line-badge{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border-radius:999px;
    border:1px solid #bbf7d0;
    background:#f0fdf4;
    color:#166534;
    font-size:10px;
    font-weight:800;
    letter-spacing:.04em;
    text-transform:uppercase;
    padding:4px 8px;
    white-space:nowrap;
  }

  .ac-do-line-meta{
    display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));
    gap:6px;
    margin-bottom:8px;
  }

  .ac-do-line-stat{
    background:#f8fafc;
    border:1px solid #e2e8f0;
    border-radius:8px;
    padding:7px 8px;
  }

  .ac-do-line-meta-label{
    display:block;
    font-size:10px;
    color:#64748b;
    font-weight:700;
    margin-bottom:2px;
  }

  .ac-do-line-meta-value{
    display:block;
    font-size:13px;
    color:#0f172a;
    font-weight:800;
  }

  .ac-do-del-btn{
    width:100%;
    min-height:34px;
    border:none;
    border-radius:8px;
    background:#ef4444;
    color:#fff;
    font-size:13px;
    font-weight:800;
    cursor:pointer;
  }

  .ac-do-no-items{
    border:1px dashed #cbd5e1;
    border-radius:8px;
    background:#f8fafc;
    color:#64748b;
    padding:10px;
    text-align:center;
    font-size:13px;
    font-weight:700;
  }

  .ac-do-sticky-save{
    position:sticky;
    bottom:0;
    z-index:25;
    padding:8px 0 calc(8px + env(safe-area-inset-bottom));
    background:linear-gradient(to top, rgba(244,247,245,.98), rgba(244,247,245,0));
  }

  .ac-do-save-btn{
    min-height:46px;
    font-size:16px;
  }

  .ac-so-status{
    display:none;
  }

  @media (max-width: 767px){
    .ac-do-fast-wrap{
      padding:6px;
    }

    .ac-do-fast-head h1{
      font-size:18px;
    }

    .ac-do-fast-sub{
      font-size:11px;
    }

    .ac-do-section{
      padding:8px;
      border-radius:9px;
    }

    .ac-do-grid-2{
      gap:6px;
    }

    .ac-do-input{
      min-height:38px;
      padding:8px 9px;
      font-size:16px;
    }

    .ac-do-type-btn{
      min-height:36px;
      font-size:13px;
    }

    .ac-do-main-btn{
      min-height:40px;
      font-size:14px;
    }

    .ac-do-line-meta{
      gap:5px;
    }

    .ac-do-line-stat{
      padding:6px 7px;
    }

    .ac-do-line-meta-label{
      font-size:10px;
    }

    .ac-do-line-meta-value{
      font-size:12px;
    }
  }

  @media (max-width: 380px){
    .ac-do-line-meta{
      grid-template-columns:repeat(2,minmax(0,1fr));
    }
  }

  /* ========== STABLE POPUP MODAL – NO KEYBOARD SHIFT ========== */
  body.ac-do-picker-open{
    overflow:hidden;
    touch-action:none;
  }

  .ac-do-dropdown{
    display:none !important;
  }

  #acDoDebtorInput,
  #ac_do_item_name{
    cursor:pointer;
    background:#fff;
  }

  .ac-do-picker-modal{
    position:fixed;
    inset:0;
    z-index:9999;
    display:none;
    align-items:center;
    justify-content:center;
    padding:8px;
    box-sizing:border-box;
  }

  .ac-do-picker-modal.active{
    display:flex;
  }

  .ac-do-picker-backdrop{
    position:absolute;
    inset:0;
    background:rgba(15,23,42,.45);
  }

  .ac-do-picker-sheet{
    position:relative;
    width:100%;
    max-width:760px;
    height:min(88dvh, 720px);
    background:#fff;
    border-radius:16px;
    box-shadow:0 10px 30px rgba(0,0,0,.18);
    overflow:hidden;
    display:flex;
    flex-direction:column;
  }

  .ac-do-picker-head{
    flex:0 0 auto;
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:10px;
    padding:12px 12px 10px;
    border-bottom:1px solid #e5e7eb;
    background:#fff;
  }

  .ac-do-picker-title{
    font-size:15px;
    font-weight:800;
    color:#0f172a;
  }

  .ac-do-picker-close{
    width:36px;
    height:36px;
    border:none;
    border-radius:10px;
    background:#f1f5f9;
    color:#0f172a;
    font-size:18px;
    font-weight:800;
    cursor:pointer;
    flex:0 0 auto;
  }

  .ac-do-picker-body{
    flex:1 1 auto;
    min-height:0;
    display:flex;
    flex-direction:column;
    padding:10px;
    overflow:hidden;
  }

  .ac-do-picker-search{
    flex:0 0 auto;
    margin-bottom:10px;
    min-height:42px;
  }

  .ac-do-picker-results{
    flex:1 1 auto;
    min-height:0;
    overflow:auto;
    -webkit-overflow-scrolling:touch;
    display:flex;
    flex-direction:column;
    gap:8px;
    padding-bottom:8px;
  }

  .ac-do-picker-item{
    width:100%;
    text-align:left;
    border:1px solid #e2e8f0;
    border-radius:12px;
    background:#fff;
    padding:12px;
    cursor:pointer;
  }

  .ac-do-picker-item:hover,
  .ac-do-picker-item.active{
    background:#f0fdf4;
    border-color:#bbf7d0;
  }

  .ac-do-picker-item-main{
    display:block;
    font-size:15px;
    line-height:1.3;
    font-weight:800;
    color:#0f172a;
    word-break:break-word;
  }

  .ac-do-picker-item-sub{
    display:block;
    margin-top:4px;
    font-size:12px;
    line-height:1.35;
    color:#64748b;
    word-break:break-word;
  }

  .ac-do-picker-note{
    border:1px dashed #cbd5e1;
    border-radius:12px;
    background:#f8fafc;
    color:#64748b;
    padding:14px 12px;
    text-align:center;
    font-size:13px;
    font-weight:700;
  }

  @media (max-width: 767px){
    .ac-do-picker-modal{
      padding:6px;
      align-items:flex-start;
    }

    .ac-do-picker-sheet{
      width:100%;
      max-width:none;
      height:calc(100dvh - 12px);
      margin-top:6px;
      border-radius:14px;
    }

    .ac-do-picker-head{
      padding:10px 10px 8px;
    }

    .ac-do-picker-body{
      padding:8px;
    }

    .ac-do-picker-item{
      padding:10px;
      border-radius:10px;
    }

    .ac-do-picker-item-main{
      font-size:14px;
    }

    .ac-do-picker-item-sub{
      font-�|D�;�
���������~�Y
??�<?php
/**
 * BASKET SUMMARY PAGE
 * - Debtor-level basket send/return/outstanding summary
 * - Drilldown ledger by debtor
 */

if (!defined('ABSPATH')) exit;

if (!function_exists('ac_used_is_mobile_or_tablet')) {
    function ac_used_is_mobile_or_tablet() {
        if (wp_is_mobile()) {
            return true;
        }

        $ua = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');
        foreach (array('ipad', 'tablet', 'kindle', 'silk', 'playbook') as $needle) {
            if ($needle !== '' && strpos($ua, $needle) !== false) {
                return true;
            }
        }

        return false;
    }
}

if (!ac_used_is_mobile_or_tablet()) {
    return;
}

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to view Basket Summary.</div>';
    return;
}

$rest_nonce = wp_create_nonce('wp_rest');
$list_url   = home_url('/delivery-order-records/');
$rest_summary_url = rest_url('ac/v1/basket/summary');
$rest_ledger_url  = rest_url('ac/v1/basket/ledger');
?>

<div id="ac-basket-summary-mobile-root" class="ac-bs-wrap bs-container"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-rest-summary-url="<?php echo esc_attr($rest_summary_url); ?>"
     data-rest-ledger-url="<?php echo esc_attr($rest_ledger_url); ?>">

  <div class="bs-head">
    <a class="bs-back" href="<?php echo esc_url($list_url); ?>">← Back to List</a>
    <h1>Basket Summary</h1>
  </div>

  <div class="bs-card">
    <div class="bs-grid2">
      <div>
        <label class="bs-label" for="ac_bs_q">Search Debtor</label>
        <input id="ac_bs_q" type="text" class="bs-input" placeholder="Debtor code or name" />
      </div>
      <div>
        <label class="bs-label" for="ac_bs_debtor">Exact Debtor Code</label>
        <input id="ac_bs_debtor" type="text" class="bs-input" placeholder="Optional exact debtor code" />
      </div>
      <div>
        <label class="bs-label" for="ac_bs_date_from">Date From</label>
        <input id="ac_bs_date_from" type="date" class="bs-input" />
      </div>
      <div>
        <label class="bs-label" for="ac_bs_date_to">Date To</label>
        <input id="ac_bs_date_to" type="date" class="bs-input" />
      </div>
    </div>

    <div class="bs-actions">
      <button id="ac_bs_refresh" class="bs-btn" type="button">Refresh Summary</button>
    </div>

    <div id="ac_bs_status" class="bs-status"></div>
  </div>

  <div class="bs-card">
    <div class="bs-totals" id="ac_bs_totals"></div>
    <div id="ac_bs_rows" class="bs-list"><div class="bs-empty">No data</div></div>
  </div>

  <div class="bs-card">
    <h2 class="bs-subtitle" id="ac_bs_ledger_title">Ledger</h2>
    <div id="ac_bs_ledger" class="bs-list"><div class="bs-empty">Select a debtor to view ledger.</div></div>
  </div>
</div>

<style>
  .bs-container{
    max-width:900px;
    margin:0 auto;
    padding:16px;
    font-family:"Segoe UI",Arial,sans-serif;
    background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
    border-radius:16px;
    color:#0f172a;
  }
  .bs-head{
    display:flex;
    align-items:center;
    gap:12px;
    flex-wrap:wrap;
    margin-bottom:12px;
  }
  .bs-head h1{
    margin:0;
    font-size:clamp(24px,4vw,34px);
    font-weight:800;
    line-height:1.1;
  }
  .bs-back{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border:1px solid #cfd9d1;
    background:#fff;
    color:#0f172a;
    padding:10px 12px;
    border-radius:10px;
    text-decoration:none;
    font-weight:700;
    font-size:14px;
    min-height:44px;
  }
  .bs-card{
    background:#fff;
    border:1px solid #e2ebe3;
    border-radius:14px;
    box-shadow:0 4px 16px rgba(15,23,42,.05);
    padding:16px;
    margin-bottom:14px;
  }
  .bs-grid2{
    display:grid;
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:10px;
  }
  .bs-label{
    display:block;
    font-size:13px;
    color:#4b5563;
    margin-bottom:6px;
    font-weight:700;
  }
  .bs-input{
    width:100%;
    min-height:46px;
    font-size:16px;
    padding:10px 12px;
    border-radius:10px;
    border:1px solid #d1d5db;
    box-sizing:border-box;
    background:#fff;
    color:#111;
  }
  .bs-input:focus,
  .bs-btn:focus,
  .bs-row:focus{
    outline:none;
    border-color:#28a745;
    box-shadow:0 0 0 3px rgba(40,167,69,.18);
  }
  .bs-actions{ margin-top:12px; }
  .bs-btn{
    min-height:48px;
    border:none;
    border-radius:12px;
    background:linear-gradient(90deg,#28a745,#1e7e34);
    color:#fff;
    font-size:16px;
    font-weight:800;
    padding:10px 18px;
    cursor:pointer;
  }
  .bs-status{
    margin-top:10px;
    min-height:20px;
    font-size:14px;
    color:#334155;
  }
  .bs-totals{
    display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));
    gap:10px;
    margin-bottom:12px;
  }
  .bs-total-box{
    border:1px solid #e5e7eb;
    background:#f8fafc;
    border-radius:10px;
    padding:10px;
  }
  .bs-total-label{ font-size:12px; color:#64748b; font-weight:700; }
  .bs-total-value{ font-size:20px; font-weight:800; color:#0f172a; margin-top:4px; }
  .bs-list{ display:flex; flex-direction:column; gap:10px; }
  .bs-row{
    width:100%;
    text-align:left;
    border:1px solid #e5e7eb;
    border-radius:12px;
    background:#fff;
    padding:12px;
    cursor:pointer;
  }
  .bs-row:hover{ background:#f8fff9; }
  .bs-row-head{
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap:10px;
    margin-bottom:8px;
  }
  .bs-row-code{ font-weight:800; font-size:16px; color:#0f766e; }
  .bs-row-name{ color:#475569; font-size:14px; }
  .bs-chip{
    display:inline-flex;
    border-radius:999px;
    padding:4px 10px;
    font-size:12px;
    font-weight:800;
    border:1px solid;
    white-space:nowrap;
  }
  .bs-chip.ok{ color:#166534; background:#dcfce7; border-color:#86efac; }
  .bs-chip.warn{ color:#9a3412; background:#ffedd5; border-color:#fdba74; }
  .bs-chip.neg{ color:#991b1b; background:#fee2e2; border-color:#fca5a5; }
  .bs-metrics{
    display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));
    gap:8px;
  }
  .bs-metric{ border:1px solid #e5e7eb; border-radius:8px; padding:8px; background:#f8fafc; }
  .bs-metric-label{ font-size:11px; color:#64748b; font-weight:700; }
  .bs-metric-value{ font-size:17px; font-weight:800; color:#0f172a; margin-top:2px; }
  .bs-subtitle{ margin:0 0 10px; font-size:18px; font-weight:800; color:#0f172a; }
  .bs-ledger-row{
    border:1px solid #e5e7eb;
    border-radius:10px;
    padding:10px;
    background:#fff;
  }
  .bs-ledger-top{ display:flex; justify-content:space-between; align-items:center; gap:10px; margin-bottom:6px; }
  .bs-ledger-type{ font-weight:800; }
  .bs-ledger-type.send{ color:#166534; }
  .bs-ledger-type.return{ color:#9a3412; }
  .bs-ledger-meta{ font-size:13px; color:#64748b; }
  .bs-empty{ color:#64748b; font-size:14px; padding:8px; }

  @media (max-width: 768px){
    .bs-container{
      max-width:none;
      margin-left:-8px;
      margin-right:-8px;
      border-radius:0;
      padding:12px;
    }
    .bs-grid2{ grid-template-columns:1fr; }
    .bs-totals,
    .bs-metrics{ grid-template-columns:1fr; }
  }
</style>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
(function(){
  const wrap = document.getElementById('ac-basket-summary-mobile-root');
  if (!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE = wrap.dataset.restNonce || '';
  const REST_SUMMARY_URL = wrap.dataset.restSummaryUrl || '';
  const REST_LEDGER_URL = wrap.dataset.restLedgerUrl || '';

  const $ = (id) => wrap.querySelector('#' + id);

  function esc(s){
    if (s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
  }

  function fmtQty(n){
    const x = Number(n);
    return Number.isFinite(x) ? x.toFixed(2) : '0.00';
  }

  function setStatus(text){
    const el = $('ac_bs_status');
    if (el) el.textContent = text || '';
  }

  function chipClass(outstanding){
    const v = Number(outstanding) || 0;
    if (v < 0) return 'neg';
    if (v > 0) return 'warn';
    return 'ok';
  }

  async function apiGet(url){
    const res = await fetch(url, {
      method:'GET',
      credentials:'same-origin',
      headers:{
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      cache:'no-store'
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  function buildSummaryUrl(){
    const q = ($('ac_bs_q').value || '').trim();
    const debtorCode = ($('ac_bs_debtor').value || '').trim();
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo = ($('ac_bs_date_to').value || '').trim();

    const url = new URL(REST_SUMMARY_URL, window.location.origin);
    if (q) url.searchParams.set('q', q);
    if (debtorCode) url.searchParams.set('debtorCode', debtorCode);
    if (dateFrom) url.searchParams.set('dateFrom', dateFrom);
    if (dateTo) url.searchParams.set('dateTo', dateTo);
    url.searchParams.set('limit', '500');
    return url.toString();
  }

  function buildLedgerUrl(debtorCode){
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo = ($('ac_bs_date_to').value || '').trim();

    const url = new URL(REST_LEDGER_URL, window.location.origin);
    url.searchParams.set('debtorCode', debtorCode);
    if (dateFrom) url.searchParams.set('dateFrom', dateFrom);
    if (dateTo) url.searchParams.set('dateTo', dateTo);
    url.searchParams.set('limit', '200');
    return url.toString();
  }

  function renderTotals(rows){
    let send = 0;
    let returned = 0;
    let outstanding = 0;

    rows.forEach((r) => {
      send += Number(r.sendQty || 0);
      returned += Number(r.returnQty || 0);
      outstanding += Number(r.outstandingQty || 0);
    });

    $('ac_bs_totals').innerHTML = `
      <div class="bs-total-box"><div class="bs-total-label">Total Send</div><div class="bs-total-value">${fmtQty(send)}</div></div>
      <div class="bs-total-box"><div class="bs-total-label">Total Return</div><div class="bs-total-value">${fmtQty(returned)}</div></div>
      <div class="bs-total-box"><div class="bs-total-label">Total Outstanding</div><div class="bs-total-value">${fmtQty(outstanding)}</div></div>
    `;
  }

  function renderSummary(rows){
    const el = $('ac_bs_rows');
    if (!rows.length) {
      renderTotals([]);
      el.innerHTML = '<div class="bs-empty">No basket records found.</div>';
      return;
    }

    renderTotals(rows);

    el.innerHTML = rows.map((r) => {
      const code = r.debtorCode || '';
      const name = r.debtorName || '';
      const send = Number(r.sendQty || 0);
      const ret = Number(r.returnQty || 0);
      const out = Number(r.outstandingQty || 0);
      const lastDate = r.lastTxnDate || '-';
      const cls = chipClass(out);

      return `
        <button class="bs-row" type="button" data-debtor-code="${esc(code)}" data-debtor-name="${esc(name)}">
          <div class="bs-row-head">
            <div>
              <div class="bs-row-code">${esc(code)}</div>
              <div class="bs-row-name">${esc(name)}</div>
            </div>
            <span class="bs-chip ${cls}">Outstanding: ${fmtQty(out)}</span>
          </div>
          <div class="bs-metrics">
            <div class="bs-metric"><div class="bs-metric-label">Send</div><div class="bs-metric-value">${fmtQty(send)}</div></div>
            <div class="bs-metric"><div class="bs-metric-label">Return</div><div class="bs-metric-value">${fmtQty(ret)}</div></div>
            <div class="bs-metric"><div class="bs-metric-label">Last Txn</div><div class="bs-metric-value">${esc(lastDate)}</div></div>
          </div>
        </button>
      `;
    }).join('');
  }

  function renderLedger(debtorCode, debtorName, rows){
    $('ac_bs_ledger_title').textContent = `Ledger: ${debtorCode}${debtorName ? ' - ' + debtorName : ''}`;

    const el = $('ac_bs_ledger');
    if (!rows.length) {
      el.innerHTML = '<div class="bs-empty">No ledger rows for this debtor.</div>';
      return;
    }

    el.innerHTML = rows.map((r) => {
      const txnType = String(r.txnType || '').toUpperCase();
      const txnTypeLabel = txnType === 'RETURN' ? 'RETURN' : 'SEND';
      const typeClass = txnType === 'RETURN' ? 'return' : 'send';
      const qty = fmtQty(r.qty || 0);
      const txnDate = r.txnDate || '-';
      const sourceType = r.sourceType || '-';
      const sourceRef = r.sourceRef || '-';
      const remark = r.remark || '';

      return `
        <div class="bs-ledger-row">
          <div class="bs-ledger-top">
            <div class="bs-ledger-type ${typeClass}">${txnTypeLabel}</div>
            <div><strong>Qty:</strong> ${qty}</div>
          </div>
          <div class="bs-ledger-meta">Date: ${esc(txnDate)} | Source: ${esc(sourceType)} | Ref: ${esc(sourceRef)}</div>
          ${remark ? `<div class="bs-ledger-meta">Remark: ${esc(remark)}</div>` : ''}
        </div>
      `;
    }).join('');
  }

  async function loadSummary(){
    if (!REST_SUMMARY_URL) {
      setStatus('Summary endpoint missing.');
      return;
    }

    setStatus('Loading summary...');

    try {
      const data = await apiGet(buildSummaryUrl());
      const rows = Array.isArray(data && data.rows) ? data.rows : [];
      renderSummary(rows);
      setStatus(`Loaded ${rows.length} debtor summary row(s). Tap a row for ledger.`);
    } catch (err) {
      setStatus(err && err.message ? err.message : 'Failed to load summary');
      $('ac_bs_rows').innerHTML = '<div class="bs-empty">Failed to load summary.</div>';
    }
  }

  async function loadLedger(debtorCode, debtorName){
    if (!REST_LEDGER_URL) {
      setStatus('Ledger endpoint missing.');
      return;
    }

    setStatus(`Loading ledger for ${debtorCode}...`);

    try {
      const data = await apiGet(buildLedgerUrl(debtorCode));
      const rows = Array.isArray(data && data.rows) ? data.rows : [];
      renderLedger(debtorCode, debtorName, rows);
      setStatus(`Loaded ${rows.length} ledger row(s) for ${debtorCode}.`);
    } catch (err) {
      setStatus(err && err.message ? err.message : 'Failed to load ledger');
      $('ac_bs_ledger').innerHTML = '<div class="bs-empty">Failed to load ledger.</div>';
    }
  }

  $('ac_bs_refresh').addEventListener('click', loadSummary);

  $('ac_bs_rows').addEventListener('click', (e) => {
    const row = e.target.closest('.bs-row[data-debtor-code]');
    if (!row) return;
    const code = row.dataset.debtorCode || '';
    const name = row.dataset.debtorName || '';
    if (!code) return;
    loadLedger(code, name);
  });

  $('ac_bs_q').addEventListener('keypress', (e) => {
    if (e.key === 'Enter') loadSummary();
  });
  $('ac_bs_debtor').addEventListener('keypress', (e) => {
    if (e.key === 'Enter') loadSummary();
  });

  loadSummary();
})();
</script>


<!-- BasketSummary.mobile full copy variant -->
<style id="BasketSummary-mobile-full-copy-variant-style">
  .bs-container{max-width:none !important;margin:0 !important;border-radius:0 !important;padding:10px !important;background:#f5f8f6 !important;}
  .bs-head{gap:8px !important;margin-bottom:10px !important;}
  .bs-head h1{font-size:24px !important;}
  .bs-back{min-height:40px !important;font-size:13px !important;padding:8px 10px !important;}
  .bs-card{border-radius:12px !important;padding:12px !important;margin-bottom:10px !important;}
  .bs-label{font-size:12px !important;margin-bottom:5px !impor�~�Y������������~+�
??�<?php
/**
 * BASKET SUMMARY PAGE (TABLE VIEW)
 * - Debtor-level basket send/return/outstanding summary
 * - Drilldown ledger by debtor
 * - Compact boss‑style tables, no cards
 * - Back to List button removed
 */

if (!defined('ABSPATH')) exit;

if (!function_exists('ac_used_is_mobile_or_tablet')) {
    function ac_used_is_mobile_or_tablet() {
        if (wp_is_mobile()) {
            return true;
        }

        $ua = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');
        foreach (array('ipad', 'tablet', 'kindle', 'silk', 'playbook') as $needle) {
            if ($needle !== '' && strpos($ua, $needle) !== false) {
                return true;
            }
        }

        return false;
    }
}

if (ac_used_is_mobile_or_tablet()) {
    return;
}

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to view Basket Summary.</div>';
    return;
}

$rest_nonce = wp_create_nonce('wp_rest');
$list_url   = home_url('/delivery-order-records/');
$rest_summary_url = rest_url('ac/v1/basket/summary');
$rest_ledger_url  = rest_url('ac/v1/basket/ledger');
?>

<div id="ac-basket-summary-desktop-root" class="ac-bs-wrap bs-container"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-rest-summary-url="<?php echo esc_attr($rest_summary_url); ?>"
     data-rest-ledger-url="<?php echo esc_attr($rest_ledger_url); ?>">

  <div class="bs-head">
    <h1>Basket Summary</h1>
  </div>

  <div class="bs-card">
    <div class="bs-grid2">
      <div>
        <label class="bs-label" for="ac_bs_q">Search Debtor</label>
        <input id="ac_bs_q" type="text" class="bs-input" placeholder="Debtor code or name" />
      </div>
      <div>
        <label class="bs-label" for="ac_bs_debtor">Exact Debtor Code</label>
        <input id="ac_bs_debtor" type="text" class="bs-input" placeholder="Optional exact debtor code" />
      </div>
      <div>
        <label class="bs-label" for="ac_bs_date_from">Date From</label>
        <input id="ac_bs_date_from" type="date" class="bs-input" />
      </div>
      <div>
        <label class="bs-label" for="ac_bs_date_to">Date To</label>
        <input id="ac_bs_date_to" type="date" class="bs-input" />
      </div>
    </div>

    <div class="bs-actions">
      <button id="ac_bs_refresh" class="bs-btn" type="button">Refresh Summary</button>
    </div>

    <div id="ac_bs_status" class="bs-status"></div>
  </div>

  <!-- Summary table (compact) -->
  <div class="bs-card">
    <div class="bs-totals" id="ac_bs_totals"></div>

    <div class="bs-table-wrap">
      <table class="bs-table">
        <thead>
          <tr>
            <th style="width:60px;">No</th>
            <th style="width:140px;">Debtor Code</th>
            <th>Debtor Name</th>
            <th style="width:110px;">Send</th>
            <th style="width:110px;">Return</th>
            <th style="width:130px;">Outstanding</th>
            <th style="width:130px;">Last Txn</th>
            <th style="width:90px;">Action</th>
          </tr>
        </thead>
        <tbody id="ac_bs_rows">
          <tr>
            <td colspan="8" class="bs-empty-cell">No data</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>

  <!-- Ledger table (compact) -->
  <div class="bs-card">
    <h2 class="bs-subtitle" id="ac_bs_ledger_title">Ledger</h2>

    <div class="bs-table-wrap">
      <table class="bs-table">
        <thead>
          <tr>
            <th style="width:60px;">No</th>
            <th style="width:120px;">Date</th>
            <th style="width:100px;">Type</th>
            <th style="width:90px;">Qty</th>
            <th style="width:120px;">Source</th>
            <th style="width:140px;">Ref</th>
            <th>Remark</th>
          </tr>
        </thead>
        <tbody id="ac_bs_ledger">
          <tr>
            <td colspan="7" class="bs-empty-cell">Select a debtor to view ledger.</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

<style>
  .bs-container{
    max-width:1360px;
    margin:0 auto;
    padding:16px;
    font-family:"Segoe UI",Arial,sans-serif;
    background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
    border-radius:16px;
    color:#0f172a;
  }
  .bs-head{
    display:flex;
    align-items:center;
    gap:12px;
    flex-wrap:wrap;
    margin-bottom:12px;
  }
  .bs-head h1{
    margin:0;
    font-size:clamp(24px,4vw,34px);
    font-weight:800;
    line-height:1.1;
  }
  .bs-card{
    background:#fff;
    border:1px solid #e2ebe3;
    border-radius:14px;
    box-shadow:0 4px 16px rgba(15,23,42,.05);
    padding:16px;
    margin-bottom:14px;
  }
  .bs-grid2{
    display:grid;
    grid-template-columns:repeat(4,minmax(0,1fr));
    gap:10px;
  }
  .bs-label{
    display:block;
    font-size:13px;
    color:#4b5563;
    margin-bottom:6px;
    font-weight:700;
  }
  .bs-input{
    width:100%;
    min-height:46px;
    font-size:15px;
    padding:10px 12px;
    border-radius:10px;
    border:1px solid #d1d5db;
    box-sizing:border-box;
    background:#fff;
    color:#111;
  }
  .bs-input:focus,
  .bs-btn:focus{
    outline:none;
    border-color:#28a745;
    box-shadow:0 0 0 3px rgba(40,167,69,.18);
  }
  .bs-actions{
    margin-top:12px;
    display:flex;
    gap:8px;
    flex-wrap:wrap;
  }
  .bs-btn{
    min-height:46px;
    border:none;
    border-radius:12px;
    background:linear-gradient(90deg,#28a745,#1e7e34);
    color:#fff;
    font-size:15px;
    font-weight:800;
    padding:10px 18px;
    cursor:pointer;
  }
  .bs-status{
    margin-top:10px;
    min-height:20px;
    font-size:14px;
    color:#334155;
  }
  .bs-totals{
    display:grid;
    grid-template-columns:repeat(4,minmax(0,1fr));
    gap:10px;
    margin-bottom:14px;
  }
  .bs-total-box{
    border:1px solid #e5e7eb;
    background:#f8fafc;
    border-radius:10px;
    padding:10px;
  }
  .bs-total-label{
    font-size:12px;
    color:#64748b;
    font-weight:700;
  }
  .bs-total-value{
    font-size:22px;
    font-weight:800;
    color:#0f172a;
    margin-top:4px;
  }
  /* TABLE STYLES (compact, no cards) */
  .bs-table-wrap{
    width:100%;
    overflow:auto;
    border:1px solid #e5e7eb;
    border-radius:12px;
  }
  .bs-table{
    width:100%;
    min-width:980px;
    border-collapse:collapse;
    background:#fff;
  }
  .bs-table thead th{
    background:#f8fafc;
    color:#334155;
    font-size:13px;
    font-weight:800;
    text-align:left;
    padding:10px 12px;
    border-bottom:1px solid #e5e7eb;
    white-space:nowrap;
  }
  .bs-table tbody td{
    padding:10px 12px;
    font-size:14px;
    color:#0f172a;
    border-bottom:1px solid #eef2f7;
    vertical-align:middle;
  }
  .bs-table tbody tr:nth-child(even){
    background:#fcfdfc;
  }
  .bs-table tbody tr:hover{
    background:#f6fbf7;
  }
  .bs-empty-cell{
    color:#64748b;
    text-align:center;
    padding:18px !important;
  }
  .bs-view-btn{
    border:1px solid #d1d5db;
    background:#fff;
    color:#0f172a;
    border-radius:8px;
    padding:6px 10px;
    font-size:12px;
    font-weight:700;
    cursor:pointer;
  }
  .bs-row-selected{
    background:#ecfdf3 !important;
  }
  .bs-type-send{
    color:#166534;
    font-weight:800;
  }
  .bs-type-return{
    color:#9a3412;
    font-weight:800;
  }
  .bs-chip{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border-radius:999px;
    padding:4px 10px;
    font-size:12px;
    font-weight:800;
    border:1px solid;
    white-space:nowrap;
  }
  .bs-chip.ok{
    color:#166534;
    background:#dcfce7;
    border-color:#86efac;
  }
  .bs-chip.warn{
    color:#9a3412;
    background:#ffedd5;
    border-color:#fdba74;
  }
  .bs-chip.neg{
    color:#991b1b;
    background:#fee2e2;
    border-color:#fca5a5;
  }
  .bs-subtitle{
    margin:0 0 10px;
    font-size:18px;
    font-weight:800;
    color:#0f172a;
  }

  @media (max-width: 900px){
    .bs-grid2{
      grid-template-columns:repeat(2,minmax(0,1fr));
    }
    .bs-totals{
      grid-template-columns:repeat(2,minmax(0,1fr));
    }
  }

  @media (max-width: 768px){
    .bs-container{
      max-width:none;
      margin-left:-8px;
      margin-right:-8px;
      border-radius:0;
      padding:12px;
    }
    .bs-grid2,
    .bs-totals{
      grid-template-columns:1fr;
    }
  }
</style>

<script>
(function(){
  const wrap = document.getElementById('ac-basket-summary-desktop-root');
  if (!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE = wrap.dataset.restNonce || '';
  const REST_SUMMARY_URL = wrap.dataset.restSummaryUrl || '';
  const REST_LEDGER_URL = wrap.dataset.restLedgerUrl || '';

  const $ = (id) => wrap.querySelector('#' + id);

  function esc(s){
    if (s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({
      '&':'&amp;',
      '<':'&lt;',
      '>':'&gt;',
      '"':'&quot;',
      "'":'&#039;'
    }[c]));
  }

  function fmtQty(n){
    const x = Number(n);
    return Number.isFinite(x) ? x.toFixed(2) : '0.00';
  }

  function setStatus(text){
    const el = $('ac_bs_status');
    if (el) el.textContent = text || '';
  }

  function chipClass(outstanding){
    const v = Number(outstanding) || 0;
    if (v < 0) return 'neg';
    if (v > 0) return 'warn';
    return 'ok';
  }

  async function apiGet(url){
    const res = await fetch(url, {
      method: 'GET',
      credentials: 'same-origin',
      headers: {
        'Accept': 'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      cache: 'no-store'
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  function buildSummaryUrl(){
    const q = ($('ac_bs_q').value || '').trim();
    const debtorCode = ($('ac_bs_debtor').value || '').trim();
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo = ($('ac_bs_date_to').value || '').trim();

    const url = new URL(REST_SUMMARY_URL, window.location.origin);
    if (q) url.searchParams.set('q', q);
    if (debtorCode) url.searchParams.set('debtorCode', debtorCode);
    if (dateFrom) url.searchParams.set('dateFrom', dateFrom);
    if (dateTo) url.searchParams.set('dateTo', dateTo);
    url.searchParams.set('limit', '500');
    return url.toString();
  }

  function buildLedgerUrl(debtorCode){
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo = ($('ac_bs_date_to').value || '').trim();

    const url = new URL(REST_LEDGER_URL, window.location.origin);
    url.searchParams.set('debtorCode', debtorCode);
    if (dateFrom) url.searchParams.set('dateFrom', dateFrom);
    if (dateTo) url.searchParams.set('dateTo', dateTo);
    url.searchParams.set('limit', '200');
    return url.toString();
  }

  function renderTotals(rows){
    let send = 0;
    let returned = 0;
    let outstanding = 0;
    let outstandingDebtors = 0;

    rows.forEach((r) => {
      const sendQty = Number(r.sendQty || 0);
      const returnQty = Number(r.returnQty || 0);
      const outstandingQty = Number(r.outstandingQty || 0);

      send += sendQty;
      returned += returnQty;
      outstanding += outstandingQty;

      if (outstandingQty !== 0) outstandingDebtors++;
    });

    $('ac_bs_totals').innerHTML = `
      <div class="bs-total-box">
        <div class="bs-total-label">Total Send</div>
        <div class="bs-total-value">${fmtQty(send)}</div>
      </div>
      <div class="bs-total-box">
        <div class="bs-total-label">Total Return</div>
        <div class="bs-total-value">${fmtQty(returned)}</div>
      </div>
      <div class="bs-total-box">
        <div class="bs-total-label">Total Outstanding</div>
        <div class="bs-total-value">${fmtQty(outstanding)}</div>
      </div>
      <div class="bs-total-box">
        <div class="bs-total-label">Debtors with Outstanding</div>
        <div class="bs-total-value">${outstandingDebtors}</div>
      </div>
    `;
  }

  function renderSummary(rows){
    const el = $('ac_bs_rows');

    if (!rows.length) {
      renderTotals([]);
      el.innerHTML = `
        <tr>
          <td colspan="8" class="bs-empty-cell">No basket records found.</td>
        </tr>
      `;
      return;
    }

    renderTotals(rows);

    el.innerHTML = rows.map((r, i) => {
      const code = r.debtorCode || '';
      const name = r.debtorName || '';
      const send = Number(r.sendQty || 0);
      const ret = Number(r.returnQty || 0);
      const out = Number(r.outstandingQty || 0);
      const lastDate = r.lastTxnDate || '-';
      const cls = chipClass(out);

      return `
        <tr data-summary-row="1" data-debtor-code="${esc(code)}">
          <td>${i + 1}</td>
          <td>${esc(code)}</td>
          <td>${esc(name)}</td>
          <td>${fmtQty(send)}</td>
          <td>${fmtQty(ret)}</td>
          <td><span class="bs-chip ${cls}">${fmtQty(out)}</span></td>
          <td>${esc(lastDate)}</td>
          <td>
            <button class="bs-view-btn" type="button"
                    data-debtor-code="${esc(code)}"
                    data-debtor-name="${esc(name)}">
              View
            </button>
          </td>
        </tr>
      `;
    }).join('');
  }

  function renderLedger(debtorCode, debtorName, rows){
    $('ac_bs_ledger_title').textContent = `Ledger: ${debtorCode}${debtorName ? ' - ' + debtorName : ''}`;

    const el = $('ac_bs_ledger');

    if (!rows.length) {
      el.innerHTML = `
        <tr>
          <td colspan="7" class="bs-empty-cell">No ledger rows for this debtor.</td>
        </tr>
      `;
      return;
    }

    el.innerHTML = rows.map((r, i) => {
      const txnType = String(r.txnType || '').toUpperCase();
      const txnTypeLabel = txnType === 'RETURN' ? 'RETURN' : 'SEND';
      const typeClass = txnType === 'RETURN' ? 'bs-type-return' : 'bs-type-send';
      const qty = fmtQty(r.qty || 0);
      const txnDate = r.txnDate || '-';
      const sourceType = r.sourceType || '-';
      const sourceRef = r.sourceRef || '-';
      const remark = r.remark || '-';

      return `
        <tr>
          <td>${i + 1}</td>
          <td>${esc(txnDate)}</td>
          <td><span class="${typeClass}">${esc(txnTypeLabel)}</span></td>
          <td>${qty}</td>
          <td>${esc(sourceType)}</td>
          <td>${esc(sourceRef)}</td>
          <td>${esc(remark)}</td>
        </tr>
      `;
    }).join('');
  }

  function markSelectedDebtorRow(debtorCode){
    wrap.querySelectorAll('[data-summary-row="1"]').forEach((row) => {
      row.classList.toggle('bs-row-selected', (row.dataset.debtorCode || '') === debtorCode);
    });
  }

  async function loadSummary(){
    if (!REST_SUMMARY_URL) {
      setStatus('Summary endpoint missing.');
      return;
    }

    setStatus('Loading summary...');

    try {
      const data = await apiGet(buildSummaryUrl());
      const rows = Array.isArray(data && data.rows) ? data.rows : [];
      renderSummary(rows);
      $('ac_bs_ledger_title').textContent = 'Ledger';
      $('ac_bs_ledger').innerHTML = `
        <tr>
          <td colspan="7" class="bs-empty-cell">Select a debtor to view ledger.</td>
        </tr>
      `;
      setStatus(`Loaded ${rows.length} debtor summary row(s).`);
    } catch (err) {
      setStatus(err && err.message ? err.message : 'Failed to load summary');
      $('ac_bs_rows').innerHTML = `
        <tr>
          <td colspan="8" class="bs-empty-cell">Failed to load summary.</td>
        </tr>
      `;
    }
  }

  async function loadLedger(debt�~+�>~�)���������~+�
?����orCode, debtorName){
    if (!REST_LEDGER_URL) {
      setStatus('Ledger endpoint missing.');
      return;
    }

    setStatus(`Loading ledger for ${debtorCode}...`);

    try {
      const data = await apiGet(buildLedgerUrl(debtorCode));
      const rows = Array.isArray(data && data.rows) ? data.rows : [];
      renderLedger(debtorCode, debtorName, rows);
      markSelectedDebtorRow(debtorCode);
      setStatus(`Loaded ${rows.length} ledger row(s) for ${debtorCode}.`);
    } catch (err) {
      setStatus(err && err.message ? err.message : 'Failed to load ledger');
      $('ac_bs_ledger').innerHTML = `
        <tr>
          <td colspan="7" class="bs-empty-cell">Failed to load ledger.</td>
        </tr>
      `;
    }
  }

  // Event listeners
  $('ac_bs_refresh').addEventListener('click', loadSummary);

  // Delegate click on view buttons
  $('ac_bs_rows').addEventListener('click', (e) => {
    const btn = e.target.closest('[data-debtor-code]');
    if (!btn) return;

    const code = btn.dataset.debtorCode || '';
    const name = btn.dataset.debtorName || '';
    if (!code) return;

    markSelectedDebtorRow(code);
    loadLedger(code, name);
  });

  $('ac_bs_q').addEventListener('keypress', (e) => {
    if (e.key === 'Enter') loadSummary();
  });

  $('ac_bs_debtor').addEventListener('keypress', (e) => {
    if (e.key === 'Enter') loadSummary();
  });

  // Initial load
  loadSummary();
})();
</script>�~+�ɭ����������~�Y
?�����tant;}
  .bs-input{min-height:46px !important;font-size:16px !important;}
  .bs-grid2,.bs-totals,.bs-metrics{grid-template-columns:1fr !important;}
  .bs-btn{width:100% !important;min-height:50px !important;font-size:18px !important;}
  .bs-row{padding:10px !important;}
  .bs-row-code{font-size:15px !important;}
  .bs-row-name,.bs-ledger-meta,.bs-status{font-size:12px !important;}
  .bs-chip{font-size:11px !important;}
  .bs-total-value,.bs-metric-value{font-size:20px !important;}
</style>�~�Y" Ƹ��������ҫp:
?;A����<?php
/**
 * BASKET RETURN PAGE
 * Fields:
 * - Date (hidden, default today)
 * - Debtor Code (saved hidden)
 * - Customer Name (shown)
 * - Basket Return Qty
 * - Remark (optional)
 */

if (!defined('ABSPATH')) exit;

if (!function_exists('ac_used_is_mobile_or_tablet')) {
    function ac_used_is_mobile_or_tablet() {
        if (wp_is_mobile()) {
            return true;
        }

        $ua = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');
        foreach (array('ipad', 'tablet', 'kindle', 'silk', 'playbook') as $needle) {
            if ($needle !== '' && strpos($ua, $needle) !== false) {
                return true;
            }
        }

        return false;
    }
}

if (!ac_used_is_mobile_or_tablet()) {
    return;
}

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to record Basket Return.</div>';
    return;
}

$rest_nonce       = wp_create_nonce('wp_rest');
$ajax_url         = admin_url('admin-ajax.php');
$debtor_nonce     = wp_create_nonce('ac_cs_debtor_search');
$rest_return_url  = rest_url('ac/v1/basket/return');
$today_date       = current_time('Y-m-d');
$show_debtor_code = false; // true or false
?>

<div id="ac-basket-return-mobile-root" class="ac-br-wrap br-container"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-rest-return-url="<?php echo esc_attr($rest_return_url); ?>"
     data-show-debtor-code="<?php echo $show_debtor_code ? '1' : '0'; ?>">
  <div class="br-head">
    <h1>Basket Return</h1>
  </div>

  <div class="br-card">
    <input id="ac_br_date" type="hidden" value="<?php echo esc_attr($today_date); ?>" />

    <label class="br-label" for="acBrDebtorInput">Customer Name</label>
    <div class="br-search-wrap" id="acBrDebtorWrapper"
         data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
         data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
      <input type="text" id="acBrDebtorInput" class="br-input" placeholder="Search customer name..." autocomplete="off" />
      <input type="hidden" id="ac_br_debtor_code" value="" />
      <input type="hidden" id="ac_br_debtor_name" value="" />
      <div class="br-dropdown" id="acBrDebtorDropdown"></div>
    </div>

    <label class="br-label" for="ac_br_qty">Basket Return Qty</label>
    <input id="ac_br_qty" type="number" min="1" step="1" class="br-input" value="1" placeholder="Enter basket return quantity" />

    <label class="br-label" for="ac_br_remark">Remark (optional)</label>
    <textarea id="ac_br_remark" class="br-input br-textarea" placeholder="Optional remark"></textarea>

    <button id="ac_br_submit" class="br-btn" type="button">Save Basket Return</button>
    <div id="ac_br_status" class="br-status"></div>
  </div>
</div>

<style>
  .br-container{
    max-width:560px;
    margin:0 auto;
    padding:16px;
    font-family:"Segoe UI",Arial,sans-serif;
    background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
    border-radius:16px;
    color:#0f172a;
  }
  .br-head{
    display:flex;
    align-items:center;
    gap:12px;
    flex-wrap:wrap;
    margin-bottom:12px;
  }
  .br-head h1{
    margin:0;
    font-size:clamp(24px,6vw,34px);
    font-weight:800;
    line-height:1.1;
  }
  .br-card{
    background:#fff;
    border:1px solid #e2ebe3;
    border-radius:14px;
    box-shadow:0 4px 16px rgba(15,23,42,.05);
    padding:16px;
  }
  .br-label{
    display:block;
    font-size:13px;
    color:#4b5563;
    margin-bottom:6px;
    font-weight:700;
  }
  .br-input{
    width:100%;
    min-height:48px;
    font-size:16px;
    padding:12px;
    border-radius:10px;
    border:1px solid #d1d5db;
    margin-bottom:14px;
    box-sizing:border-box;
    background:#fff;
    color:#111;
  }
  .br-textarea{
    min-height:100px;
    resize:vertical;
  }
  .br-input:focus,
  .br-btn:focus{
    outline:none;
    border-color:#28a745;
    box-shadow:0 0 0 3px rgba(40,167,69,.18);
  }
  .br-search-wrap{
    position:relative;
  }
  .br-dropdown{
    position:absolute;
    left:0;
    right:0;
    top:calc(100% - 10px);
    background:#fff;
    border:1px solid #dcdcdc;
    border-radius:10px;
    z-index:80;
    max-height:260px;
    overflow:auto;
    box-shadow:0 8px 24px rgba(0,0,0,.10);
    display:none;
  }
  .br-dropdown.active{
    display:block;
  }
  .br-item{
    padding:10px 12px;
    cursor:pointer;
    border-bottom:1px solid #f1f1f1;
    display:flex;
    flex-direction:column;
    gap:2px;
    font-size:15px;
  }
  .br-item:last-child{
    border-bottom:none;
  }
  .br-item:hover,
  .br-item.active{
    background:#f3f9f4;
  }
  .br-item strong{
    font-weight:700;
    color:#111827;
  }
  .br-item small{
    font-size:12px;
    color:#6b7280;
  }
  .br-btn{
    width:100%;
    min-height:52px;
    border:none;
    border-radius:12px;
    background:linear-gradient(90deg,#28a745,#1e7e34);
    color:#fff;
    font-size:20px;
    font-weight:800;
    padding:12px 14px;
    cursor:pointer;
  }
  .br-btn[disabled]{
    opacity:.7;
    cursor:not-allowed;
  }
  .br-status{
    margin-top:10px;
    font-size:14px;
    color:#334155;
    min-height:20px;
  }

  @media (max-width: 768px){
    .br-container{
      max-width:none;
      margin-left:-8px;
      margin-right:-8px;
      border-radius:0;
      padding:12px;
    }
    .br-card{
      padding:14px;
    }
  }
</style>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
(function(){
  const wrap = document.getElementById('ac-basket-return-mobile-root');
  if (!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE = wrap.dataset.restNonce || '';
  const REST_RETURN_URL = wrap.dataset.restReturnUrl || '';
  const SHOW_DEBTOR_CODE = wrap.dataset.showDebtorCode === '1';

  const $ = (id) => wrap.querySelector('#' + id);
  let isSubmitting = false;

  function esc(s){
    if (s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
  }

  function toast(icon, title, text=''){
    if (!window.Swal) return;
    Swal.fire({
      toast:true,
      position:'top-end',
      icon, title, text,
      showConfirmButton:false,
      timer:2200,
      timerProgressBar:true
    });
  }

  function setStatus(text){
    const el = $('ac_br_status');
    if (el) el.textContent = text || '';
  }

  function whole(n){
    const x = Number(n);
    if (!isFinite(x) || x < 0) return 0;
    return Math.round(x);
  }

  function formatDisplayValue(name, code){
    const safeName = (name || '').trim();
    const safeCode = (code || '').trim();

    if (SHOW_DEBTOR_CODE && safeName && safeCode) {
      return `${safeName} (${safeCode})`;
    }

    return safeName || safeCode;
  }

  async function apiPost(url, body){
    const res = await fetch(url, {
      method:'POST',
      credentials:'same-origin',
      headers:{
        'Content-Type':'application/json',
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      body: JSON.stringify(body)
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  (function initDate(){
    const dateInput = $('ac_br_date');
    if (!dateInput) return;

    if (!dateInput.value) {
      const d = new Date();
      dateInput.value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
    }
  })();

  (function initDebtorDropdown(){
    const wrapper = $('acBrDebtorWrapper');
    if (!wrapper) return;

    const AJAX_URL = wrapper.dataset.ajaxUrl;
    const NONCE = wrapper.dataset.nonce;
    const input = $('acBrDebtorInput');
    const hiddenCode = $('ac_br_debtor_code');
    const hiddenName = $('ac_br_debtor_name');
    const dropdown = $('acBrDebtorDropdown');

    let items = [];
    let activeIndex = -1;
    let abortController = null;
    let timer = null;

    const show = () => dropdown.classList.add('active');
    const hide = () => {
      dropdown.classList.remove('active');
      activeIndex = -1;
    };

    const render = () => {
      if (!items.length) {
        dropdown.innerHTML = '<div class="br-item"><small>No customer found</small></div>';
        show();
        return;
      }

      dropdown.innerHTML = items.map((it, idx) => {
        const code = it.code || it.debtorCode || it.id || '';
        const name = it.name || it.debtorName || it.description || '';

        return `
          <div class="br-item" data-index="${idx}">
            <strong>${esc(name || code)}</strong>
            ${SHOW_DEBTOR_CODE && code ? `<small>${esc(code)}</small>` : ''}
          </div>
        `;
      }).join('');

      show();
    };

    const pick = (idx) => {
      if (idx < 0 || idx >= items.length) return;

      const it = items[idx];
      const code = it.code || it.debtorCode || it.id || '';
      const name = it.name || it.debtorName || it.description || '';

      input.value = formatDisplayValue(name, code);
      hiddenCode.value = code;
      hiddenName.value = name;
      hide();
    };

    const search = async (q) => {
      if (abortController) abortController.abort();
      abortController = new AbortController();

      const query = (q || '').trim();
      if (query.length < 1) {
        hide();
        return;
      }

      dropdown.innerHTML = '<div class="br-item"><small>Searching...</small></div>';
      show();

      try {
        const url = `${AJAX_URL}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(NONCE)}&q=${encodeURIComponent(query)}`;
        const res = await fetch(url, { signal: abortController.signal, credentials:'same-origin' });
        const data = await res.json();

        if (!data.success) {
          throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');
        }

        items = (data.data && data.data.items) ? data.data.items : [];
        render();
      } catch (err) {
        if (err.name === 'AbortError') return;
        dropdown.innerHTML = '<div class="br-item"><small>Failed to load</small></div>';
        show();
      }
    };

    input.addEventListener('input', () => {
      hiddenCode.value = '';
      hiddenName.value = '';
      clearTimeout(timer);
      timer = setTimeout(() => search(input.value), 300);
    });

    input.addEventListener('keydown', (e) => {
      if (!dropdown.classList.contains('active')) return;

      const nodes = Array.from(dropdown.querySelectorAll('.br-item[data-index]'));
      if (!nodes.length) return;

      if (e.key === 'ArrowDown') {
        e.preventDefault();
        activeIndex = Math.min(activeIndex + 1, nodes.length - 1);
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        activeIndex = Math.max(activeIndex - 1, 0);
      } else if (e.key === 'Enter') {
        if (activeIndex >= 0) {
          e.preventDefault();
          pick(activeIndex);
        }
      } else if (e.key === 'Escape') {
        hide();
      }

      nodes.forEach((n, i) => n.classList.toggle('active', i === activeIndex));
    });

    dropdown.addEventListener('click', (e) => {
      const node = e.target.closest('.br-item[data-index]');
      if (!node) return;
      pick(parseInt(node.dataset.index, 10));
    });

    document.addEventListener('click', (e) => {
      if (!wrapper.contains(e.target)) {
        hide();
      }
    });
  })();

  $('ac_br_submit').addEventListener('click', async () => {
    if (isSubmitting) return;

    const debtorCode = ($('ac_br_debtor_code').value || '').trim();
    const debtorName = ($('ac_br_debtor_name').value || '').trim();
    const docDate = ($('ac_br_date').value || '').trim();
    const basketQty = whole(($('ac_br_qty').value || '0'));
    const remark = ($('ac_br_remark').value || '').trim();
    const savedLabel = debtorName || debtorCode;

    if (!debtorCode) {
      toast('error', 'Customer required');
      setStatus('Please select customer.');
      return;
    }

    if (!docDate) {
      toast('error', 'Date required');
      setStatus('Today date is missing.');
      return;
    }

    if (basketQty <= 0) {
      toast('error', 'Invalid quantity');
      setStatus('Basket Return Qty must be greater than 0.');
      return;
    }

    if (!REST_RETURN_URL) {
      toast('error', 'Config error');
      setStatus('Basket return endpoint is missing.');
      return;
    }

    const submitBtn = $('ac_br_submit');

    try {
      isSubmitting = true;
      submitBtn.disabled = true;
      submitBtn.textContent = 'Saving...';
      setStatus('Saving basket return...');

      const payload = {
        debtorCode,
        debtorName,
        docDate,
        basketQty,
        remark
      };

      const res = await apiPost(REST_RETURN_URL, payload);
      if (!res || !res.ok) {
        throw new Error((res && (res.message || res.error)) ? (res.message || res.error) : 'Failed to save basket return');
      }

      const suffix = res.duplicate ? ' (duplicate ignored)' : '';
      setStatus(`Saved basket return for ${savedLabel}: ${basketQty}${suffix}`);
      toast('success', 'Basket return saved', `${savedLabel} | Qty ${basketQty}${suffix}`);

      $('ac_br_qty').value = '1';
      $('ac_br_remark').value = '';
      $('ac_br_qty').focus();
    } catch (err) {
      const msg = err && err.message ? err.message : 'Failed to save basket return';
      setStatus(msg);
      toast('error', 'Save failed', msg);
    } finally {
      isSubmitting = false;
      submitBtn.disabled = false;
      submitBtn.textContent = 'Save Basket Return';
    }
  });
})();
</script>

<style id="BasketReturn-mobile-full-copy-variant-style">
  .br-container{max-width:none !important;margin:0 !important;border-radius:0 !important;padding:10px !important;background:#f5f8f6 !important;}
  .br-head{gap:8px !important;margin-bottom:10px !important;}
  .br-head h1{font-size:24px !important;}
  .br-card{padding:12px !important;border-radius:12px !important;}
  .br-label{font-size:12px !important;margin-bottom:5px !important;}
  .br-input{min-height:46px !important;margin-bottom:10px !important;font-size:16px !important;}
  .br-textarea{min-height:84px !important;}
  .br-btn{min-height:50px !important;font-size:19px !important;border-radius:11px !important;position:sticky;bottom:8px;z-index:20;}
  .br-status{font-size:12px !important;margin-top:8px !important;}
</style>ҫp:�hs�����������
??� .br-container{ max-width:560px; margin:0 auto; padding:16px; font-family:"Segoe UI",Arial,sans-serif; background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%); border-radius:16px; color:#0f172a; }
  .br-head{ display:flex; align-items:center; gap:12px; margin-bottom:12px; }
  .br-head h1{ margin:0; font-size:clamp(24px,6vw,34px); font-weight:800; line-height:1.1; }
  .br-card{ background:#fff; border:1px solid #e2ebe3; border-radius:14px; box-shadow:0 4px 16px rgba(15,23,42,.05); padding:16px; }
  .br-label{ display:block; font-size:13px; color:#4b5563; margin-bottom:6px; font-weight:700; }
  .br-input{ width:100%; min-height:48px; font-size:16px; padding:12px; border-radius:10px; border:1px solid #d1d5db; margin-bottom:14px; box-sizing:border-box; background:#fff; color:#111; }
  .br-textarea{ min-height:100px; resize:vertical; }
  .br-input:focus, .br-btn:focus{ outline:none; border-color:#28a745; box-shadow:0 0 0 3px rgba(40,167,69,.18); }
  .br-search-wrap{ position:relative; }
  .br-dropdown{ position:absolute; left:0; right:0; top:calc(100% - 10px); background:#fff; border:1px solid #dcdcdc; border-radius:10px; z-index:80; max-height:260px; overflow:auto; box-shadow:0 8px 24px rgba(0,0,0,.10); display:none; }
  .br-dropdown.active{ display:block; }
  .br-item{ padding:10px 12px; cursor:pointer; border-bottom:1px solid #f1f1f1; display:flex; flex-direction:column; gap:2px; font-size:15px; }
  .br-item:last-child{ border-bottom:none; }
  .br-item:hover, .br-item.active{ background:#f3f9f4; }
  .br-item strong{ font-weight:700; color:#111827; }
  .br-item small{ font-size:12px; color:#6b7280; }
  .br-btn{ width:100%; min-height:52px; border:none; border-radius:12px; background:linear-gradient(90deg,#28a745,#1e7e34); color:#fff; font-size:20px; font-weight:800; padding:12px 14px; cursor:pointer; }
  .br-btn[disabled]{ opacity:.7; cursor:not-allowed; }
  .br-status{ margin-top:10px; font-size:14px; color:#334155; min-height:20px; }

  /* Responsive fixes */
  @media (max-width:767px){
    .ac-do-fast-wrap{ padding:6px; }
    .ac-do-fast-head h1{ font-size:18px; }
    .ac-do-picker-sheet{ width:100%; max-width:none; height:calc(100dvh - 12px); margin-top:6px; border-radius:14px; }
    .br-container{ max-width:none; margin:0; border-radius:0; padding:12px; }
    .br-card{ padding:14px; }
  }
  .ac-tab-pane .br-container { max-width:none !important; }
</style>

<!-- SweetAlert2 (once) -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

<!-- ================================================================= -->
<!-- DELIVERY ORDER SCRIPT (identical to original, with bug fix)      -->
<!-- ================================================================= -->
<script>
(function(){
  const wrap = document.getElementById('ac-do-create-mobile-root');
  if(!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE    = wrap.dataset.restNonce;
  const EDIT_BASE     = wrap.dataset.editBase;
  const REST_JOB_POST = wrap.dataset.restJobPost;
  const REST_JOB_BASE = wrap.dataset.restJobBase;

  const $ = (id) => wrap.querySelector('#' + id);
  const state = { lines: [], jobFinished: false, isSubmitting: false };

  const DROPDOWN_META = {
    showDebtorCode: false,
    showItemCode: false,
    showSalesAgent: false
  };

  function escapeHtml(s){
    if(s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({
      '&':'&amp;',
      '<':'&lt;',
      '>':'&gt;',
      '"':'&quot;',
      "'":'&#039;'
    }[c]));
  }

  function fmtNum(n){
    const x = parseFloat(n);
    return isNaN(x) ? '0.00' : x.toFixed(2);
  }

  function parseWhole(n){
    const x = Number(n);
    if(!isFinite(x) || x < 0) return 0;
    return Math.round(x);
  }

  function fmtWhole(n){
    return String(parseWhole(n));
  }

  function closeMobileKeyboard(el){
    if (!el) return;
    try { el.blur(); } catch(e) {}
    if (document.activeElement && typeof document.activeElement.blur === 'function') {
      try { document.activeElement.blur(); } catch(e) {}
    }
  }

  function setStatus(msg, type='info'){
    const toastType = type === 'success' ? 'success' : type === 'error' ? 'error' : type === 'warning' ? 'warning' : 'info';
    if (window.Swal) {
      Swal.fire({
        toast: true,
        position: 'top-end',
        icon: toastType,
        title: msg.replace(/<[^>]*>/g, ''),
        showConfirmButton: false,
        timer: 1800,
        timerProgressBar: true
      });
    }
  }

  function showToast(icon, title, text=''){
    if (window.Swal) Swal.fire({ toast: true, position: 'top-end', icon, title, text, showConfirmButton: false, timer: 2600, timerProgressBar: true });
  }

  function showModal(icon, title, html){
    if (window.Swal) Swal.fire({ icon, title, html, confirmButtonText: 'OK' });
  }

  function calcTotal(qty, kg){
    return (parseFloat(qty) || 0) * (parseFloat(kg) || 0);
  }

  function updateEntryTotal(){
    const itemCode = ($('ac_do_item').value || '').trim();
    const itemName = ($('ac_do_item_display').value || '').trim() || itemCode;
    const packType = ($('ac_do_pack_type').value || '').trim();

    const qtyRaw = ($('ac_do_qty').value || '').trim();
    const kgRaw = ($('ac_do_kg').value || '').trim();
    const qty = parseFloat(qtyRaw || '0') || 0;
    const kg = parseWhole(kgRaw || '0');
    const total = calcTotal(qty, kg);

    $('ac_do_total').value = fmtNum(total);

    const pv = $('ac_do_line_preview');
    if(!itemCode || qtyRaw === '' || kgRaw === ''){
      pv.style.display = 'none';
      pv.innerHTML = '';
      return;
    }

    pv.style.display = 'block';
    pv.innerHTML =
      '<div><b>' + escapeHtml(itemName) + '</b></div>' +
      '<div>Type: ' + escapeHtml(packType) + ' | Qty: ' + fmtNum(qty) + ' | KG: ' + fmtWhole(kg) + ' | Total: ' + fmtNum(total) + '</div>';
  }

  function setPackType(type){
    const nextType = String(type || '').toUpperCase() === 'BASKET' ? 'BASKET' : 'CARTON';
    $('ac_do_pack_type').value = nextType;

    wrap.querySelectorAll('#ac_do_pack_type_toggle .ac-do-type-btn').forEach((btn) => {
      const btnType = (btn.dataset.packType || '').toUpperCase();
      btn.classList.toggle('active', btnType === nextType);
    });

    updateEntryTotal();
  }

  function initPackTypeToggle(){
    wrap.querySelectorAll('#ac_do_pack_type_toggle .ac-do-type-btn').forEach((btn) => {
      btn.addEventListener('click', () => {
        setPackType(btn.dataset.packType || 'CARTON');
      });
    });
    setPackType($('ac_do_pack_type').value || 'CARTON');
  }

  function updateLineCount(){
    const count = state.lines.length;
    $('ac_do_line_count').textContent = count + (count === 1 ? ' line' : ' lines');
  }

  function renderLines(){
    const list = $('ac_do_lines');
    list.innerHTML = '';

    if(!state.lines.length){
      list.innerHTML = '<div class="ac-do-no-items">No items yet</div>';
      updateLineCount();
      return;
    }

    state.lines.forEach((l, idx) => {
      const row = document.createElement('div');
      row.className = 'ac-do-line-card';
      row.innerHTML = `
        <div class="ac-do-line-top">
          <div class="ac-do-line-name">${idx + 1}. ${escapeHtml(l.itemName || l.itemCode)}</div>
          <div class="ac-do-line-badge">${escapeHtml(l.packType)}</div>
        </div>

        <div class="ac-do-line-meta">
          <div class="ac-do-line-stat">
            <span class="ac-do-line-meta-label">Qty</span>
            <span class="ac-do-line-meta-value">${fmtNum(l.qty)}</span>
          </div>
          <div class="ac-do-line-stat">
            <span class="ac-do-line-meta-label">KG</span>
            <span class="ac-do-line-meta-value">${fmtWhole(l.kg)}</span>
          </div>
          <div class="ac-do-line-stat">
            <span class="ac-do-line-meta-label">Total</span>
            <span class="ac-do-line-meta-value">${fmtNum(l.total)}</span>
          </div>
        </div>

        <button type="button" class="ac-do-del-btn" data-idx="${idx}">Delete Line</button>
      `;
      list.appendChild(row);
    });

    updateLineCount();
  }

  async function apiGet(url){
    const res = await fetch(url, {
      method:'GET',
      credentials:'same-origin',
      headers:{
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      cache:'no-store'
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    const text = await res.text();
    let data = null;
    try {
      data = text ? JSON.parse(text) : null;
    } catch (e) {
      throw new Error('Invalid JSON from job status endpoint');
    }
    return data;
  }

  async function apiPost(url, body){
    const res = await fetch(url, {
      method:'POST',
      credentials:'same-origin',
      headers:{
        'Content-Type':'application/json',
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      body: JSON.stringify(body)
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  async function pollJob(jobId){
    const max = 45;

    for (let i = 0; i < max; i++) {
      await new Promise(r => setTimeout(r, 2000));

      let r;
      try {
        r = await apiGet(REST_JOB_BASE + jobId + '?_t=' + Date.now());
      } catch (err) {
        setStatus('Polling error: ' + (err.message || String(err)), 'error');
        showToast('error', 'Polling failed', err.message || 'Unable to read job status');
        return false;
      }

      const job = r?.job || r?.data?.job || r;
      const st = String(job?.status || '').toUpperCase();
      if (!st) continue;

      setStatus(`Job #${jobId}: ${st}`, (
        st === 'SUCCESS' ? 'success' :
        (st === 'FAILED' || st === 'FAILED_FINAL') ? 'error' :
        st === 'CANCELLED' ? 'warning' :
        'info'
      ));

      if (st === 'SUCCESS') {
        if (state.jobFinished) return true;
        state.jobFinished = true;

        let result = job.result || {};
        if (typeof result === 'string') {
          try { result = JSON.parse(result); } catch(e) {}
        }

        const docNo = result.docNo || result.DocNo || job.docNo || '';
        if (docNo) {
          const editUrl = EDIT_BASE + '?docno=' + encodeURIComponent(docNo);
          showModal(
            'success',
            'Delivery Order Created',
            `Document <b>${escapeHtml(docNo)}</b> was saved successfully.<br><br>
             <a href="${editUrl}" target="_blank" rel="noopener">Open Delivery Order</a>`
          );
        } else {
          showToast('success', 'Delivery Order created');
        }
        return true;
      }

      if (st === 'FAILED' || st === 'FAILED_FINAL') {
        if (state.jobFinished) return false;
        state.jobFinished = true;

        let result = job.result || {};
        if (typeof result === 'string') {
          try { result = JSON.parse(result); } catch(e) {}
        }

        const err = job.error_message || job.error || result.error || 'Job failed';
        showModal('error', 'Delivery Order Failed', escapeHtml(err));
        return false;
      }

      if (st === 'CANCELLED') {
        if (state.jobFinished) return false;
        state.jobFinished = true;
        showToast('warning', 'Job cancelled');
        return false;
      }
    }

    showModal(
      'warning',
      'Queue Timeout',
      'The page did not receive a final status in time.<br><br>Check the Jobs table to confirm whether it already completed.'
    );
    return false;
  }

  async function searchItemsLive(q){
    const ajaxUrl = wrap.dataset.ajaxUrl || "";
    const nonce = wrap.dataset.itemSuggestNonce || "";
    if(!ajaxUrl || !nonce) return [];

    const fd = new FormData();
    fd.append("action", "ac_itemcode_suggest");
    fd.append("nonce", nonce);
    fd.append("term", q);

    const res = await fetch(ajaxUrl, {
      method: "POST",
      credentials: "same-origin",
      body: fd
    });

    let data;
    try {
      data = await res.json();
    } catch(e) {
      console.error('Item search parse error', e);
      return [];
    }

    if (data && data.success === true && data.data && Array.isArray(data.data.items)) {
      return data.data.items.map(it => ({
        code: it.code || '',
        name: (it.desc || it.name || it.description || '').trim()
      }));
    }

    return [];
  }

  // ========== STABLE POPUP MODAL FUNCTIONS ==========
  async function searchDebtorsLive(q){
    // FIX: use getElementById instead of undefined variable
    const wrapper = document.getElementById('acDoDebtorWrapper');
    const AJAX_URL = wrapper ? (wrapper.dataset.ajaxUrl || '') : '';
    const NONCE = wrapper ? (wrapper.dataset.nonce || '') : '';

    if(!AJAX_URL || !NONCE) return [];

    const url = `${AJAX_URL}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(NONCE)}&q=${encodeURIComponent(q)}`;
    const response = await fetch(url, { credentials:'same-origin' });
    const text = await response.text();

    let data;
    try {
      data = JSON.parse(text);
    } catch(e){
      throw new Error('Invalid JSON response');
    }

    if (!data.success) {
      throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');
    }

    const items = (data.data && data.data.items) ? data.data.items : [];

    return items.map((it) => {
      const name = it.name || it.debtorName || it.description || '';
      const code = it.code || it.debtorCode || it.id || '';
      const sa   = (it.salesAgent || it.sales_agent || it.sa || '').trim();

      const debtorMeta = [];
      if (DROPDOWN_META.showDebtorCode && code) debtorMeta.push(code);
      if (DROPDOWN_META.showSalesAgent && sa) debtorMeta.push('SA: ' + sa);

      return {
        label: name || code,
        meta: debtorMeta.join(' • '),
        raw: {
          name,
          code,
          salesAgent: sa
        }
      };
    });
  }

  const pickerState = {
    items: [],
    fetchFn: null,
    onPick: null
  };

  let pickerTimer = null;

  function renderPickerNote(msg){
    $('ac_do_picker_results').innerHTML = `<div class="ac-do-picker-note">${escapeHtml(msg)}</div>`;
  }

  function renderPickerItems(items){
    const box = $('ac_do_picker_results');

    if(!items.length){
      renderPickerNote('No result found');
      return;
    }

    box.innerHTML = items.map((it, idx) => `
      <button type="button" class="ac-do-picker-item" data-picker-idx="${idx}">
        <span class="ac-do-picker-item-main">${escapeHtml(it.label || '')}</span>
        ${it.meta ? `<span class="ac-do-picker-item-sub">${escapeHtml(it.meta)}</span>` : ''}
      </button>
    `).join('');
  }

  async function runPickerSearch(q){
    const query = (q || '').trim();
    clearTimeout(pickerTimer);

    if(query.length < 1){
      pickerState.items = [];
      renderPickerNote('Type to search');
      return;
    }

    pickerTimer = setTimeout(async () => {
      renderPickerNote('Searching...');

      try{
        const items = await pickerState.fetchFn(query);
        pickerState.items = items || [];
        renderPickerItems(pickerState.items);
      }catch(e){
        pickerState.items = [];
        renderPickerNote('Failed to load result');
      }
    }, 220);
  }

  function openPicker(opts){
    pickerState.items = [];
    pickerState.fetchFn = opts.fetchFn;
    pickerState.onPick = opts.onPick;

    $('ac_do_picker_title').textContent = opts.title ||��ߗ�Ao��������܂y
??�
    }

    #ac-do-desktop-root .acdo-desktop-form-grid{
        display:grid;
        grid-template-columns:minmax(0,1.2fr) minmax(0,0.6fr) minmax(0,0.7fr);
        gap:0.7rem;
        align-items:end;
    }

    #ac-do-desktop-root .acdo-desktop-type-toggle{
        display:grid;
        grid-template-columns:1fr 1fr;
        gap:0.4rem;
    }

    #ac-do-desktop-root .acdo-desktop-type-btn{
        height:2.5rem;
        border:0.0625rem solid var(--acdo-desktop-border-strong);
        border-radius:0.625rem;
        background:#fff;
        color:var(--acdo-desktop-text);
        font-size:0.9rem;
        font-weight:600;
        cursor:pointer;
    }

    #ac-do-desktop-root .acdo-desktop-type-btn.is-active{
        background:var(--acdo-desktop-green-soft);
        color:var(--acdo-desktop-green);
        border-color:#c9ded4;
    }

    #ac-do-desktop-root .acdo-desktop-preview{
        margin-top:0.75rem;
        padding:0.75rem 0.9rem;
        border:0.0625rem solid #e4e9ed;
        border-radius:0.75rem;
        background:#fafcfd;
    }

    #ac-do-desktop-root .acdo-desktop-preview-grid{
        display:grid;
        grid-template-columns:minmax(0,1fr) auto;
        gap:0.8rem;
        align-items:start;
    }

    #ac-do-desktop-root .acdo-desktop-preview-name{
        font-size:0.95rem;
        font-weight:700;
        color:var(--acdo-desktop-text);
        margin-bottom:0.4rem;
    }

    #ac-do-desktop-root .acdo-desktop-preview-meta{
        display:grid;
        grid-template-columns:repeat(3,minmax(0,1fr));
        gap:0.5rem;
    }

    #ac-do-desktop-root .acdo-desktop-preview-box{
        border:0.0625rem solid #e4e9ed;
        border-radius:0.625rem;
        padding:0.5rem 0.6rem;
        background:#fff;
    }

    #ac-do-desktop-root .acdo-desktop-preview-box span{
        display:block;
    }

    #ac-do-desktop-root .acdo-desktop-preview-box .acdo-desktop-preview-box-label{
        font-size:0.7rem;
        color:var(--acdo-desktop-muted);
        margin-bottom:0.15rem;
    }

    #ac-do-desktop-root .acdo-desktop-preview-box .acdo-desktop-preview-box-value{
        font-size:0.85rem;
        font-weight:700;
        color:var(--acdo-desktop-text);
    }

    #ac-do-desktop-root .acdo-desktop-badge{
        display:inline-flex;
        align-items:center;
        justify-content:center;
        min-width:4.5rem;
        padding:0.4rem 0.7rem;
        border-radius:999rem;
        background:var(--acdo-desktop-green-soft);
        color:var(--acdo-desktop-green);
        border:0.0625rem solid #d6e6de;
        font-size:0.75rem;
        font-weight:700;
        text-transform:capitalize;
        white-space:nowrap;
    }

    #ac-do-desktop-root .acdo-desktop-mini-title{
        margin:0 0 0.5rem;
        font-size:0.9rem;
        font-weight:700;
        color:var(--acdo-desktop-text);
    }

    #ac-do-desktop-root .acdo-desktop-mini-table,
    #ac-do-desktop-root .acdo-desktop-lines-wrap{
        border:0.0625rem solid var(--acdo-desktop-border);
        border-radius:0.75rem;
        overflow:hidden;
        background:#fff;
    }

    #ac-do-desktop-root .acdo-desktop-mini-head,
    #ac-do-desktop-root .acdo-desktop-table-head{
        display:grid;
        align-items:center;
        gap:0;
        background:#f8f9fa;
        color:#535d67;
        font-size:0.8rem;
        font-weight:600;
        border-bottom:0.0625rem solid var(--acdo-desktop-border);
    }

    #ac-do-desktop-root .acdo-desktop-mini-head{
        grid-template-columns:minmax(0,1.9fr) 1fr 0.8fr 0.8fr 0.95fr;
    }

    #ac-do-desktop-root .acdo-desktop-table-head{
        grid-template-columns:minmax(0,2fr) 1fr 0.8fr 0.8fr 1fr 0.9fr;
    }

    #ac-do-desktop-root .acdo-desktop-mini-head span,
    #ac-do-desktop-root .acdo-desktop-table-head span{
        padding:0.6rem 0.7rem;
        min-width:0;
    }

    #ac-do-desktop-root .acdo-desktop-mini-body,
    #ac-do-desktop-root .acdo-desktop-table-body{
        background:#fff;
    }

    #ac-do-desktop-root .acdo-desktop-mini-row,
    #ac-do-desktop-root .acdo-desktop-table-row{
        display:grid;
        align-items:center;
        gap:0;
        border-bottom:0.0625rem solid #eef2f5;
    }

    #ac-do-desktop-root .acdo-desktop-mini-row{
        grid-template-columns:minmax(0,1.9fr) 1fr 0.8fr 0.8fr 0.95fr;
    }

    #ac-do-desktop-root .acdo-desktop-table-row{
        grid-template-columns:minmax(0,2fr) 1fr 0.8fr 0.8fr 1fr 0.9fr;
    }

    #ac-do-desktop-root .acdo-desktop-mini-row:last-child,
    #ac-do-desktop-root .acdo-desktop-table-row:last-child{
        border-bottom:none;
    }

    #ac-do-desktop-root .acdo-desktop-cell{
        padding:0.6rem 0.7rem;
        font-size:0.9rem;
        color:var(--acdo-desktop-text);
        min-width:0;
        word-break:break-word;
    }

    #ac-do-desktop-root .acdo-desktop-cell-total{
        font-weight:700;
    }

    #ac-do-desktop-root .acdo-desktop-empty-row{
        padding:1rem;
        text-align:center;
        color:var(--acdo-desktop-muted);
        font-size:0.9rem;
        background:#fff;
    }

    #ac-do-desktop-root .acdo-desktop-delete-btn{
        width:100%;
        max-width:4rem;
        height:2rem;
        border:none;
        border-radius:0.5rem;
        background:var(--acdo-desktop-danger);
        color:#fff;
        font-size:0.8rem;
        font-weight:700;
        cursor:pointer;
    }

    #ac-do-desktop-root .acdo-desktop-save-btn{
        width:auto;
        min-width:9rem;
        padding:0 1rem;
    }
</style>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

<script>
(function(){
    const desktopRoot = document.getElementById('ac-do-desktop-root');
    if (!desktopRoot || desktopRoot.dataset.desktopInit === '1') return;
    desktopRoot.dataset.desktopInit = '1';

    const byId = (id) => desktopRoot.querySelector('#' + id);

    const desktopEls = {
        date: byId('acdo_desktop_date'),
        customerInput: byId('acdo_desktop_customer_input'),
        customerCode: byId('acdo_desktop_customer_code'),
        customerName: byId('acdo_desktop_customer_name'),
        salesAgent: byId('acdo_desktop_sales_agent'),
        location: byId('acdo_desktop_location'),
        customerDropdown: byId('acdo_desktop_customer_dropdown'),
        customerWrapper: byId('acdo_desktop_customer_wrapper'),

        itemInput: byId('acdo_desktop_item_name'),
        itemCode: byId('acdo_desktop_item_code'),
        itemDisplay: byId('acdo_desktop_item_display'),
        itemDropdown: byId('acdo_desktop_item_dropdown'),

        qty: byId('acdo_desktop_qty'),
        kg: byId('acdo_desktop_kg'),
        total: byId('acdo_desktop_total'),
        packType: byId('acdo_desktop_pack_type'),
        packToggle: byId('acdo_desktop_pack_toggle'),
        preview: byId('acdo_desktop_preview'),

        miniLines: byId('acdo_desktop_mini_lines'),
        lines: byId('acdo_desktop_lines'),

        sumLines: byId('acdo_desktop_sum_lines'),
        sumQty: byId('acdo_desktop_sum_qty'),
        sumKg: byId('acdo_desktop_sum_kg'),

        addLine: byId('acdo_desktop_add_line'),
        submit: byId('acdo_desktop_submit')
    };

    const REST_NONCE    = desktopRoot.dataset.restNonce || '';
    const EDIT_BASE     = desktopRoot.dataset.editBase || '';
    const REST_JOB_POST = desktopRoot.dataset.restJobPost || '';
    const REST_JOB_BASE = desktopRoot.dataset.restJobBase || '';
    const AJAX_URL      = desktopRoot.dataset.ajaxUrl || '';
    const ITEM_NONCE    = desktopRoot.dataset.itemSuggestNonce || '';

    const desktopState = {
        lines: [],
        isSubmitting: false,
        jobFinished: false
    };

    const desktopDropdownMeta = {
        showCustomerCode: false,
        showItemCode: false,
        showSalesAgent: false
    };

    function desktopEscapeHtml(value) {
        if (value === null || value === undefined) return '';
        return String(value).replace(/[&<>"']/g, function(char){
            return {
                '&':'&amp;',
                '<':'&lt;',
                '>':'&gt;',
                '"':'&quot;',
                "'":'&#039;'
            }[char];
        });
    }

    function desktopToNumber(value) {
        const num = parseFloat(value);
        return isNaN(num) ? 0 : num;
    }

    function desktopFormatQty(value) {
        const num = desktopToNumber(value);
        return Number.isInteger(num) ? String(num) : num.toFixed(2);
    }

    function desktopFormatKg(value) {
        return desktopToNumber(value).toFixed(2);
    }

    function desktopFormatTotal(value) {
        return desktopToNumber(value).toFixed(2);
    }

    function desktopCalcTotal(qty, kg) {
        return desktopToNumber(qty) * desktopToNumber(kg);
    }

    function desktopShowToast(icon, title, text) {
        if (!window.Swal) return;
        Swal.fire({
            toast: true,
            position: 'top-end',
            icon: icon,
            title: title,
            text: text || '',
            showConfirmButton: false,
            timer: 2200,
            timerProgressBar: true
        });
    }

    function desktopShowModal(icon, title, html) {
        if (!window.Swal) return;
        Swal.fire({
            icon: icon,
            title: title,
            html: html,
            confirmButtonText: 'OK'
        });
    }

    function desktopSetPackType(type) {
        const nextType = String(type || '').toUpperCase() === 'BASKET' ? 'BASKET' : 'CARTON';
        desktopEls.packType.value = nextType;

        desktopEls.packToggle.querySelectorAll('.acdo-desktop-type-btn').forEach(function(btn){
            btn.classList.toggle('is-active', (btn.dataset.packType || '').toUpperCase() === nextType);
        });

        desktopUpdatePreview();
    }

    function desktopUpdateSummary() {
        let totalQty = 0;
        let totalKg = 0;

        desktopState.lines.forEach(function(line){
            totalQty += desktopToNumber(line.qty);
            totalKg += desktopToNumber(line.total);
        });

        desktopEls.sumLines.textContent = String(desktopState.lines.length);
        desktopEls.sumQty.textContent = desktopFormatQty(totalQty);
        desktopEls.sumKg.textContent = desktopFormatKg(totalKg);
    }

    function desktopUpdateEntryTotal() {
        const total = desktopCalcTotal(desktopEls.qty.value, desktopEls.kg.value);
        desktopEls.total.value = String(total);
        desktopUpdatePreview();
    }

    function desktopUpdatePreview() {
        const itemCode = (desktopEls.itemCode.value || '').trim();
        const itemName = (desktopEls.itemDisplay.value || '').trim() || itemCode;
        const packType = (desktopEls.packType.value || '').trim();
        const qty = desktopToNumber(desktopEls.qty.value);
        const kg = desktopToNumber(desktopEls.kg.value);
        const total = desktopCalcTotal(qty, kg);

        if (!itemCode) {
            desktopEls.preview.style.display = 'none';
            desktopEls.preview.innerHTML = '';
            return;
        }

        desktopEls.preview.style.display = 'block';
        desktopEls.preview.innerHTML = `
            <div class="acdo-desktop-preview-grid">
                <div>
                    <div class="acdo-desktop-preview-name">${desktopEscapeHtml(itemName)}</div>
                    <div class="acdo-desktop-preview-meta">
                        <div class="acdo-desktop-preview-box">
                            <span class="acdo-desktop-preview-box-label">Qty</span>
                            <span class="acdo-desktop-preview-box-value">${desktopFormatQty(qty)}</span>
                        </div>
                        <div class="acdo-desktop-preview-box">
                            <span class="acdo-desktop-preview-box-label">KG</span>
                            <span class="acdo-desktop-preview-box-value">${desktopFormatKg(kg)}</span>
                        </div>
                        <div class="acdo-desktop-preview-box">
                            <span class="acdo-desktop-preview-box-label">Total</span>
                            <span class="acdo-desktop-preview-box-value">${desktopFormatTotal(total)}</span>
                        </div>
                    </div>
                </div>
                <div>
                    <span class="acdo-desktop-badge">${desktopEscapeHtml(packType)}</span>
                </div>
            </div>
        `;
    }

    function desktopRenderMiniLines() {
        if (!desktopState.lines.length) {
            desktopEls.miniLines.innerHTML = '<div class="acdo-desktop-empty-row">No items added yet</div>';
            return;
        }

        desktopEls.miniLines.innerHTML = desktopState.lines.map(function(line){
            return `
                <div class="acdo-desktop-mini-row">
                    <div class="acdo-desktop-cell">${desktopEscapeHtml(line.itemName || line.itemCode)}</div>
                    <div class="acdo-desktop-cell"><span class="acdo-desktop-badge">${desktopEscapeHtml(line.packType)}</span></div>
                    <div class="acdo-desktop-cell">${desktopFormatQty(line.qty)}</div>
                    <div class="acdo-desktop-cell">${desktopFormatKg(line.kg)}</div>
                    <div class="acdo-desktop-cell acdo-desktop-cell-total">${desktopFormatTotal(line.total)}</div>
                </div>
            `;
        }).join('');
    }

    function desktopRenderMainLines() {
        if (!desktopState.lines.length) {
            desktopEls.lines.innerHTML = '<div class="acdo-desktop-empty-row">No items added yet</div>';
            desktopUpdateSummary();
            return;
        }

        desktopEls.lines.innerHTML = desktopState.lines.map(function(line, index){
            return `
                <div class="acdo-desktop-table-row">
                    <div class="acdo-desktop-cell">${desktopEscapeHtml(line.itemName || line.itemCode)}</div>
                    <div class="acdo-desktop-cell"><span class="acdo-desktop-badge">${desktopEscapeHtml(line.packType)}</span></div>
                    <div class="acdo-desktop-cell">${desktopFormatQty(line.qty)}</div>
                    <div class="acdo-desktop-cell">${desktopFormatKg(line.kg)}</div>
                    <div class="acdo-desktop-cell acdo-desktop-cell-total">${desktopFormatTotal(line.total)}</div>
                    <div class="acdo-desktop-cell">
                        <button type="button" class="acdo-desktop-delete-btn" data-line-index="${index}">Delete</button>
                    </div>
                </div>
            `;
        }).join('');

        desktopUpdateSummary();
    }

    function desktopRenderAllLines() {
        desktopRenderMiniLines();
        desktopRenderMainLines();
        desktopUpdateSummary();
    }

    async function desktopApiGet(url) {
        const response = await fetch(url, {
            method: 'GET',
            credentials: 'same-origin',
            headers: {
                'Accept': 'application/json',
                'X-WP-Nonce': REST_NONCE
            },
            cache: 'no-store'
        });

        if (!response.ok) {
            let errorMessage = 'HTTP ' + response.status;
            try {
                const errorData = await response.json();
                errorMessage = errorData.message || errorData.error || errorMessage;
            } catch(e) {}
            throw new Error(errorMessage);
        }

        const text = await response.text();
        try {
            return text ? JSON.parse(text) : null;
        } catch(e) {
            throw new Error('Invalid JSON response');
        }
    }

    async function desktopApiPost(url, body) {
        const response = await fetch(url, {
            method: 'POST',
            credentials: 'same-origin',
            he܂y�0i��������܂
??�aders: {
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'X-WP-Nonce': REST_NONCE
            },
            body: JSON.stringify(body)
        });

        if (!response.ok) {
            let errorMessage = 'HTTP ' + response.status;
            try {
                const errorData = await response.json();
                errorMessage = errorData.message || errorData.error || errorMessage;
            } catch(e) {}
            throw new Error(errorMessage);
        }

        return await response.json();
    }

    function desktopMakeClientRequestId(prefix) {
        const rnd = Math.random().toString(36).slice(2, 10);
        const ts = Date.now().toString(36);
        return prefix + '-' + ts + '-' + rnd;
    }

    async function desktopPollJob(jobId) {
        const maxRounds = 45;

        for (let i = 0; i < maxRounds; i++) {
            await new Promise(function(resolve){ setTimeout(resolve, 2000); });

            let result;
            try {
                result = await desktopApiGet(REST_JOB_BASE + jobId + '?_t=' + Date.now());
            } catch (error) {
                desktopShowToast('error', 'Polling failed', error.message || 'Unable to check job status');
                return false;
            }

            const job = result?.job || result?.data?.job || result;
            const status = String(job?.status || '').toUpperCase();

            if (!status) continue;

            if (status === 'SUCCESS') {
                if (desktopState.jobFinished) return true;
                desktopState.jobFinished = true;

                let jobResult = job.result || {};
                if (typeof jobResult === 'string') {
                    try { jobResult = JSON.parse(jobResult); } catch(e) {}
                }

                const docNo = jobResult.docNo || jobResult.DocNo || job.docNo || '';
                if (docNo) {
                    const editUrl = EDIT_BASE + '?docno=' + encodeURIComponent(docNo);
                    desktopShowModal(
                        'success',
                        'Delivery Order Created',
                        `Document <b>${desktopEscapeHtml(docNo)}</b> was saved successfully.<br><br><a href="${editUrl}" target="_blank" rel="noopener">Open Delivery Order</a>`
                    );
                } else {
                    desktopShowToast('success', 'Delivery Order created', '');
                }

                return true;
            }

            if (status === 'FAILED' || status === 'FAILED_FINAL') {
                if (desktopState.jobFinished) return false;
                desktopState.jobFinished = true;

                let jobResult = job.result || {};
                if (typeof jobResult === 'string') {
                    try { jobResult = JSON.parse(jobResult); } catch(e) {}
                }

                const errorMessage = job.error_message || job.error || jobResult.error || 'Job failed';
                desktopShowModal('error', 'Delivery Order Failed', desktopEscapeHtml(errorMessage));
                return false;
            }

            if (status === 'CANCELLED') {
                if (desktopState.jobFinished) return false;
                desktopState.jobFinished = true;
                desktopShowToast('warning', 'Job cancelled', '');
                return false;
            }
        }

        desktopShowModal(
            'warning',
            'Queue Timeout',
            'The page did not receive a final status in time.<br><br>Check the Jobs table to confirm whether it already completed.'
        );

        return false;
    }

    function desktopCloseDropdown(dropdown) {
        dropdown.classList.remove('is-open');
        dropdown.innerHTML = '';
    }

    function desktopOpenDropdown(dropdown) {
        dropdown.classList.add('is-open');
    }

    function desktopBindItemSearch() {
        let searchTimer = null;

        desktopEls.itemInput.addEventListener('input', function(){
            clearTimeout(searchTimer);

            desktopEls.itemCode.value = '';
            desktopEls.itemDisplay.value = '';
            desktopUpdatePreview();

            const query = desktopEls.itemInput.value.trim();
            if (query.length < 1) {
                desktopCloseDropdown(desktopEls.itemDropdown);
                return;
            }

            searchTimer = setTimeout(async function(){
                desktopEls.itemDropdown.innerHTML = '<div class="acdo-desktop-dropdown-item"><span class="acdo-desktop-dropdown-sub">Searching...</span></div>';
                desktopOpenDropdown(desktopEls.itemDropdown);

                try {
                    const formData = new FormData();
                    formData.append('action', 'ac_itemcode_suggest');
                    formData.append('nonce', ITEM_NONCE);
                    formData.append('term', query);

                    const response = await fetch(AJAX_URL, {
                        method: 'POST',
                        credentials: 'same-origin',
                        body: formData
                    });

                    let data = null;
                    try {
                        data = await response.json();
                    } catch(e) {
                        data = null;
                    }

                    const items = (data && data.success && data.data && Array.isArray(data.data.items))
                        ? data.data.items.map(function(item){
                            return {
                                code: item.code || '',
                                name: (item.desc || item.name || item.description || '').trim()
                            };
                        })
                        : [];

                    if (!items.length) {
                        desktopEls.itemDropdown.innerHTML = '<div class="acdo-desktop-dropdown-item"><span class="acdo-desktop-dropdown-sub">No item found</span></div>';
                        return;
                    }

                    desktopEls.itemDropdown.innerHTML = items.map(function(item, index){
                        return `
                            <div class="acdo-desktop-dropdown-item" data-item-index="${index}">
                                <span class="acdo-desktop-dropdown-main">${desktopEscapeHtml(item.name || item.code)}</span>
                                ${desktopDropdownMeta.showItemCode && item.code ? `<span class="acdo-desktop-dropdown-sub">${desktopEscapeHtml(item.code)}</span>` : ''}
                            </div>
                        `;
                    }).join('');

                    desktopEls.itemDropdown.querySelectorAll('[data-item-index]').forEach(function(node){
                        node.addEventListener('click', function(){
                            const item = items[parseInt(node.dataset.itemIndex, 10)];
                            desktopEls.itemInput.value = item.name || item.code || '';
                            desktopEls.itemCode.value = item.code || '';
                            desktopEls.itemDisplay.value = item.name || item.code || '';
                            desktopCloseDropdown(desktopEls.itemDropdown);
                            desktopUpdatePreview();
                        });
                    });
                } catch(e) {
                    desktopEls.itemDropdown.innerHTML = '<div class="acdo-desktop-dropdown-item"><span class="acdo-desktop-dropdown-sub">Failed to load</span></div>';
                }
            }, 220);
        });
    }

    function desktopBindCustomerSearch() {
        let customerItems = [];
        let activeIndex = -1;
        let searchTimer = null;
        let abortController = null;

        const wrapper = desktopEls.customerWrapper;
        const input = desktopEls.customerInput;
        const dropdown = desktopEls.customerDropdown;
        const hiddenCode = desktopEls.customerCode;

        function renderEmpty(message) {
            dropdown.innerHTML = `<div class="acdo-desktop-dropdown-item"><span class="acdo-desktop-dropdown-sub">${desktopEscapeHtml(message)}</span></div>`;
            desktopOpenDropdown(dropdown);
        }

        function renderResults(items) {
            if (!items.length) {
                renderEmpty('No customer found');
                return;
            }

            dropdown.innerHTML = items.map(function(item, index){
                const name = item.name || item.debtorName || item.description || '';
                const code = item.code || item.debtorCode || item.id || '';
                const salesAgent = (item.salesAgent || item.sales_agent || item.sa || '').trim();

                const meta = [];
                if (desktopDropdownMeta.showCustomerCode && code) meta.push(desktopEscapeHtml(code));
                if (desktopDropdownMeta.showSalesAgent && salesAgent) meta.push('SA: ' + desktopEscapeHtml(salesAgent));

                return `
                    <div class="acdo-desktop-dropdown-item" data-customer-index="${index}">
                        <span class="acdo-desktop-dropdown-main">${desktopEscapeHtml(name || code)}</span>
                        ${meta.length ? `<span class="acdo-desktop-dropdown-sub">${meta.join(' • ')}</span>` : ''}
                    </div>
                `;
            }).join('');

            desktopOpenDropdown(dropdown);
            activeIndex = -1;

            dropdown.querySelectorAll('[data-customer-index]').forEach(function(node){
                node.addEventListener('click', function(){
                    selectCustomer(parseInt(node.dataset.customerIndex, 10));
                });
            });
        }

        function selectCustomer(index) {
            if (index < 0 || index >= customerItems.length) return;

            const item = customerItems[index];
            const name = item.name || item.debtorName || item.description || '';
            const code = item.code || item.debtorCode || item.id || '';
            const salesAgent = (item.salesAgent || item.sales_agent || item.sa || '').trim();

            input.value = name || code;
            hiddenCode.value = code;
            desktopEls.customerName.value = name;
            desktopEls.salesAgent.value = salesAgent;

            desktopCloseDropdown(dropdown);
        }

        async function performSearch(query) {
            if (abortController) abortController.abort();
            abortController = new AbortController();

            const q = (query || '').trim();
            if (q.length < 1) {
                desktopCloseDropdown(dropdown);
                return;
            }

            renderEmpty('Searching...');

            try {
                const searchUrl = `${wrapper.dataset.ajaxUrl}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(wrapper.dataset.nonce)}&q=${encodeURIComponent(q)}`;
                const response = await fetch(searchUrl, {
                    signal: abortController.signal,
                    credentials: 'same-origin'
                });

                const text = await response.text();

                let data = null;
                try {
                    data = JSON.parse(text);
                } catch(e) {
                    throw new Error('Invalid JSON response');
                }

                if (!data.success) {
                    throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');
                }

                customerItems = (data.data && data.data.items) ? data.data.items : [];
                renderResults(customerItems);
            } catch(error) {
                if (error.name === 'AbortError') return;
                renderEmpty('Failed to load results');
            }
        }

        input.addEventListener('input', function(){
            hiddenCode.value = '';
            desktopEls.customerName.value = '';
            desktopEls.salesAgent.value = '';

            clearTimeout(searchTimer);
            searchTimer = setTimeout(function(){
                performSearch(input.value);
            }, 280);
        });

        input.addEventListener('focus', function(){
            if (input.value.trim() !== '') {
                clearTimeout(searchTimer);
                searchTimer = setTimeout(function(){
                    performSearch(input.value);
                }, 120);
            }
        });

        input.addEventListener('keydown', function(event){
            if (!dropdown.classList.contains('is-open')) return;

            const nodes = dropdown.querySelectorAll('[data-customer-index]');
            if (!nodes.length) return;

            if (event.key === 'ArrowDown') {
                event.preventDefault();
                activeIndex = (activeIndex + 1) % nodes.length;
                nodes.forEach(function(node, index){
                    node.classList.toggle('is-active', index === activeIndex);
                });
            } else if (event.key === 'ArrowUp') {
                event.preventDefault();
                activeIndex = activeIndex > 0 ? activeIndex - 1 : nodes.length - 1;
                nodes.forEach(function(node, index){
                    node.classList.toggle('is-active', index === activeIndex);
                });
            } else if (event.key === 'Enter' && activeIndex >= 0) {
                event.preventDefault();
                selectCustomer(activeIndex);
            } else if (event.key === 'Escape') {
                desktopCloseDropdown(dropdown);
            }
        });

        document.addEventListener('click', function(event){
            if (!wrapper.contains(event.target)) {
                desktopCloseDropdown(dropdown);
            }
        });
    }

    function desktopInitDate() {
        const now = new Date();
        const y = now.getFullYear();
        const m = String(now.getMonth() + 1).padStart(2, '0');
        const d = String(now.getDate()).padStart(2, '0');
        desktopEls.date.value = `${y}-${m}-${d}`;
    }

    function desktopResetEntry() {
        desktopEls.itemInput.value = '';
        desktopEls.itemCode.value = '';
        desktopEls.itemDisplay.value = '';
        desktopEls.qty.value = '1';
        desktopEls.kg.value = '0';
        desktopEls.total.value = '0';
        desktopSetPackType('CARTON');
        desktopUpdatePreview();
        desktopEls.itemInput.focus();
    }

    desktopEls.packToggle.querySelectorAll('.acdo-desktop-type-btn').forEach(function(btn){
        btn.addEventListener('click', function(){
            desktopSetPackType(btn.dataset.packType || 'CARTON');
        });
    });

    desktopEls.qty.addEventListener('input', desktopUpdateEntryTotal);
    desktopEls.kg.addEventListener('input', desktopUpdateEntryTotal);

    desktopEls.itemInput.addEventListener('keypress', function(event){
        if (event.key === 'Enter') {
            event.preventDefault();
        }
    });

    desktopEls.qty.addEventListener('keypress', function(event){
        if (event.key === 'Enter') {
            event.preventDefault();
        }
    });

    desktopEls.kg.addEventListener('keypress', function(event){
        if (event.key === 'Enter') {
            event.preventDefault();
            desktopEls.addLine.click();
        }
    });

    desktopEls.addLine.addEventListener('click', function(){
        const itemCode = (desktopEls.itemCode.value || '').trim();
        const itemName = (desktopEls.itemDisplay.value || '').trim() || itemCode;
        const packType = (desktopEls.packType.value || '').trim();
        const qty = desktopToNumber(desktopEls.qty.value);
        const kg = desktopToNumber(desktopEls.kg.value);
        const total = desktopCalcTotal(qty, kg);

        if (!itemCode) {
            desktopShowToast('error', 'Please select item name from dropdown'܂	�_��������܂
?�����, '');
            return;
        }

        if (qty <= 0) {
            desktopShowToast('error', 'Qty must be greater than 0', '');
            return;
        }

        if (kg <= 0) {
            desktopShowToast('error', 'Weight must be greater than 0', '');
            return;
        }

        desktopState.lines.push({
            itemCode: itemCode,
            itemName: itemName,
            packType: packType,
            qty: qty,
            kg: kg,
            total: total
        });

        desktopRenderAllLines();
        desktopResetEntry();
        desktopShowToast('success', 'Item added', '');
    });

    desktopEls.lines.addEventListener('click', function(event){
        const deleteBtn = event.target.closest('[data-line-index]');
        if (!deleteBtn) return;

        const index = parseInt(deleteBtn.dataset.lineIndex, 10);
        if (isNaN(index)) return;

        desktopState.lines.splice(index, 1);
        desktopRenderAllLines();
        desktopShowToast('warning', 'Item removed', '');
    });

    desktopEls.submit.addEventListener('click', async function(){
        if (desktopState.isSubmitting) {
            desktopShowToast('warning', 'Already submitting, please wait', '');
            return;
        }

        const customerCode = (desktopEls.customerCode.value || '').trim();
        const customerName = (desktopEls.customerName.value || '').trim();
        const salesAgent = (desktopEls.salesAgent.value || '').trim();
        const location = (desktopEls.location.value || '').trim();
        const docDate = (desktopEls.date.value || '').trim();

        if (!customerCode) {
            desktopShowToast('error', 'Please select customer', '');
            return;
        }

        if (!docDate) {
            desktopShowToast('error', 'Please select date', '');
            return;
        }

        if (!location) {
            desktopShowToast('error', 'Location is missing', '');
            return;
        }

        if (!desktopState.lines.length) {
            desktopShowToast('error', 'Please add at least 1 item', '');
            return;
        }

        desktopState.jobFinished = false;
        desktopState.isSubmitting = true;
        desktopEls.submit.disabled = true;
        desktopEls.submit.textContent = 'Saving...';

        try {
            const payload = {
                customerCode: customerCode,
                customerName: customerName,
                salesAgent: salesAgent,
                debtorCode: customerCode,
                DebtorCode: customerCode,
                debtorName: customerName,
                DebtorName: customerName,
                SalesAgent: salesAgent,
                location: location,
                Location: location,
                docDate: docDate,
                remark: '',
                lines: desktopState.lines.map(function(line){
                    return {
                        itemCode: line.itemCode,
                        qty: line.total,
                        uom: 'KG',
                        unitPrice: 0,
                        amount: 0,
                        taxCode: 'SR-0',
                        taxRate: 0,
                        packType: line.packType,
                        cartonQty: line.qty,
                        kg: line.kg,
                        totalKg: line.total,
                        location: location
                    };
                })
            };

            const requestBody = {
                type: 'DELIVERY_ORDER',
                client_request_id: desktopMakeClientRequestId('DO'),
                source: 'wp-ui',
                payload: payload
            };

            const response = await desktopApiPost(REST_JOB_POST, requestBody);
            const jobId = response.jobId || response.id || (response.job && response.job.id);

            if (!jobId) {
                desktopShowToast('warning', 'Job created but job ID missing', '');
                return;
            }

            desktopShowToast('info', 'Job queued', 'Job #' + jobId);
            await desktopPollJob(jobId);
        } catch (error) {
            desktopShowModal('error', 'Submit error', desktopEscapeHtml(error.message || String(error)));
        } finally {
            desktopState.isSubmitting = false;
            desktopEls.submit.disabled = false;
            desktopEls.submit.textContent = 'Save Delivery Order';
        }
    });

    document.addEventListener('click', function(event){
        if (!desktopEls.itemDropdown.contains(event.target) && event.target !== desktopEls.itemInput) {
            desktopCloseDropdown(desktopEls.itemDropdown);
        }
    });

    desktopInitDate();
    desktopBindCustomerSearch();
    desktopBindItemSearch();
    desktopSetPackType('CARTON');
    desktopUpdateEntryTotal();
    desktopRenderAllLines();
})();
</script>܂�7���������Ҍ�u
?-7����<?php
if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to view Delivery Order records.</div>';
    return;
}

if (!function_exists('get_mssql')) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Function get_mssql() not found.</div>';
    return;
}

$conn = get_mssql();
if (!$conn) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Failed to connect to MSSQL.</div>';
    return;
}

/**
 * Separate helper names so this can coexist with desktop snippet
 */
function ac_do_mobile_dt($v) {
    if ($v instanceof DateTime) return $v->format('Y-m-d H:i:s');
    return is_string($v) ? $v : '';
}

function ac_do_mobile_errors() {
    if (!function_exists('sqlsrv_errors')) return 'Unknown SQLSRV error.';
    $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
    if (empty($errors)) return 'Unknown SQLSRV error.';

    $out = array();
    foreach ($errors as $e) {
        $out[] = '[' . ($e['code'] ?? '') . '] ' . ($e['message'] ?? '');
    }
    return implode(' | ', $out);
}

$q        = trim(wp_unslash($_GET['q'] ?? ''));
$status   = strtoupper(trim(wp_unslash($_GET['status'] ?? 'ALL')));
$dateFrom = trim(wp_unslash($_GET['dateFrom'] ?? ''));
$dateTo   = trim(wp_unslash($_GET['dateTo'] ?? ''));
$limit    = max(1, min(300, intval($_GET['limit'] ?? 50)));

$where  = array();
$params = array();

if ($q !== '') {
    $where[] = "(DOH.DocNo LIKE ? OR DOH.DebtorCode LIKE ? OR DOH.DebtorName LIKE ?)";
    $like = '%' . $q . '%';
    $params[] = $like;
    $params[] = $like;
    $params[] = $like;
}

if ($dateFrom !== '') {
    $where[] = "CAST(DOH.DocDate AS date) >= ?";
    $params[] = $dateFrom;
}

if ($dateTo !== '') {
    $where[] = "CAST(DOH.DocDate AS date) <= ?";
    $params[] = $dateTo;
}

if ($status === 'ACTIVE') {
    $where[] = "ISNULL(DOH.Cancelled,'F') <> 'T'";
} elseif ($status === 'CANCELLED') {
    $where[] = "ISNULL(DOH.Cancelled,'F') = 'T'";
}

$whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';

$sql = "
    SELECT TOP ($limit)
        DOH.DocKey,
        DOH.DocNo,
        DOH.DocDate,
        DOH.DebtorCode,
        DOH.DebtorName,
        ISNULL(DOH.DocStatus,'') AS DocStatus,
        ISNULL(DOH.Cancelled,'F') AS Cancelled,
        DOH.CreatedTimeStamp,
        DOH.LastModified
    FROM dbo.DO AS DOH
    $whereSql
    ORDER BY DOH.DocDate DESC, DOH.DocNo DESC
";

$stmt = sqlsrv_query($conn, $sql, $params);

if ($stmt === false) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">'
        . esc_html(ac_do_mobile_errors())
        . '</div>';
    return;
}

$rows = array();
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
    $docStatus = strtoupper(trim((string)($row['DocStatus'] ?? '')));
    $cancelled = strtoupper(trim((string)($row['Cancelled'] ?? 'F')));
    $statusText = ($cancelled === 'T') ? 'CANCELLED' : (($docStatus === '' || $docStatus === 'A') ? 'ACTIVE' : $docStatus);

    $rows[] = array(
        'docKey'       => (int)($row['DocKey'] ?? 0),
        'docNo'        => (string)($row['DocNo'] ?? ''),
        'docDate'      => ($row['DocDate'] instanceof DateTime) ? $row['DocDate']->format('Y-m-d') : (string)($row['DocDate'] ?? ''),
        'debtorCode'   => (string)($row['DebtorCode'] ?? ''),
        'debtorName'   => (string)($row['DebtorName'] ?? ''),
        'status'       => $statusText,
        'createdAt'    => ac_do_mobile_dt($row['CreatedTimeStamp'] ?? null),
        'lastModified' => ac_do_mobile_dt($row['LastModified'] ?? null),
    );
}
sqlsrv_free_stmt($stmt);
?>

<div class="do-mobile-wrap">
    <div class="do-mobile-head">
        <h2>Delivery Order Records</h2>
    </div>

    <div class="do-mobile-card">
        <form method="get" class="do-mobile-form">
            <div class="do-mobile-field">
                <label class="do-mobile-label">Search</label>
                <input name="q" class="do-mobile-input" type="text" placeholder="Doc No, Debtor" value="<?php echo esc_attr($q); ?>">
            </div>

            <div class="do-mobile-field">
                <label class="do-mobile-label">Status</label>
                <select name="status" class="do-mobile-input">
                    <option value="ALL" <?php selected($status, 'ALL'); ?>>All</option>
                    <option value="ACTIVE" <?php selected($status, 'ACTIVE'); ?>>Active</option>
                    <option value="CANCELLED" <?php selected($status, 'CANCELLED'); ?>>Cancelled</option>
                </select>
            </div>

            <div class="do-mobile-date-row">
                <div class="do-mobile-field">
                    <label class="do-mobile-label">From</label>
                    <input name="dateFrom" class="do-mobile-input" type="date" value="<?php echo esc_attr($dateFrom); ?>">
                </div>

                <div class="do-mobile-field">
                    <label class="do-mobile-label">To</label>
                    <input name="dateTo" class="do-mobile-input" type="date" value="<?php echo esc_attr($dateTo); ?>">
                </div>
            </div>

            <div class="do-mobile-actions">
                <button type="submit" class="do-mobile-btn">Refresh</button>
            </div>
        </form>
    </div>

    <div class="do-mobile-summary">
        Total rows: <strong><?php echo number_format_i18n(count($rows)); ?></strong>
    </div>

    <?php if (empty($rows)): ?>
        <div class="do-mobile-card do-mobile-empty">
            No matching delivery order records.
        </div>
    <?php else: ?>
        <div class="do-mobile-list">
            <?php foreach ($rows as $i => $r): ?>
                <?php
                $badgeClass = 'do-mobile-badge-default';
                if ($r['status'] === 'ACTIVE') $badgeClass = 'do-mobile-badge-active';
                if ($r['status'] === 'CANCELLED') $badgeClass = 'do-mobile-badge-cancelled';
                ?>
                <div class="do-mobile-card do-mobile-item">
                    <div class="do-mobile-top">
                        <div class="do-mobile-no">#<?php echo esc_html($i + 1); ?></div>
                        <span class="do-mobile-badge <?php echo esc_attr($badgeClass); ?>">
                            <?php echo esc_html($r['status']); ?>
                        </span>
                    </div>

                    <div class="do-mobile-docno">
                        <?php echo esc_html($r['docNo'] ?: '-'); ?>
                    </div>

                    <div class="do-mobile-debtor">
                        <?php echo esc_html($r['debtorName'] ?: '-'); ?>
                    </div>

                    <div class="do-mobile-meta">
                        <div class="do-mobile-row">
                            <span class="do-mobile-key">Date</span>
                            <span class="do-mobile-val"><?php echo esc_html($r['docDate'] ?: '-'); ?></span>
                        </div>
                        <div class="do-mobile-row">
                            <span class="do-mobile-key">Debtor Code</span>
                            <span class="do-mobile-val"><?php echo esc_html($r['debtorCode'] ?: '-'); ?></span>
                        </div>
                        <div class="do-mobile-row">
                            <span class="do-mobile-key">Created</span>
                            <span class="do-mobile-val"><?php echo esc_html($r['createdAt'] ?: '-'); ?></span>
                        </div>
                        <div class="do-mobile-row">
                            <span class="do-mobile-key">Last Modified</span>
                            <span class="do-mobile-val"><?php echo esc_html($r['lastModified'] ?: '-'); ?></span>
                        </div>
                    </div>
                </div>
            <?php endforeach; ?>
        </div>
    <?php endif; ?>
</div>

<style>
.do-mobile-wrap{
    max-width:900px;
    margin:0 auto;
    padding:12px;
    font-family:"Segoe UI", Arial, sans-serif;
    color:#0f172a;
}

.do-mobile-head{
    margin-bottom:10px;
}

.do-mobile-head h2{
    margin:0;
    font-size:24px;
    font-weight:800;
    line-height:1.2;
}

.do-mobile-card{
    background:#fff;
    border:1px solid #dbe5dd;
    border-radius:14px;
    box-shadow:0 4px 14px rgba(15,23,42,.05);
    padding:12px;
    margin-bottom:10px;
}

.do-mobile-form{
    display:flex;
    flex-direction:column;
    gap:10px;
}

.do-mobile-date-row{
    display:grid;
    grid-template-columns:1fr 1fr;
    gap:10px;
}

.do-mobile-field{
    display:flex;
    flex-direction:column;
    gap:5px;
}

.do-mobile-label{
    font-size:12px;
    font-weight:700;
    color:#4b5563;
}

.do-mobile-input{
    width:100%;
    min-height:42px;
    border:1px solid #d1d5db;
    border-radius:10px;
    padding:10px 12px;
    font-size:14px;
    box-sizing:border-box;
    background:#fff;
}

.do-mobile-actions{
    display:flex;
}

.do-mobile-btn{
    width:100%;
    min-height:42px;
    border:none;
    border-radius:10px;
    padding:10px 14px;
    background:#1e7e34;
    color:#fff;
    font-size:14px;
    font-weight:700;
    cursor:pointer;
}

.do-mobile-summary{
    margin:8px 0 10px;
    font-size:14px;
    color:#334155;
}

.do-mobile-list{
    display:flex;
    flex-direction:column;
    gap:10px;
}

.do-mobile-item{
    padding:12px;
}

.do-mobile-top{
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap:10px;
    margin-bottom:8px;
}

.do-mobile-no{
    font-size:12px;
    font-weight:800;
    color:#64748b;
}

.do-mobile-docno{
    font-size:18px;
    font-weight:800;
    color:#0f172a;
    line-height:1.25;
    margin-bottom:4px;
}

.do-mobile-debtor{
    font-size:14px;
    font-weight:600;
    color:#334155;
    line-height:1.4;
    margin-bottom:10px;
}

.do-mobile-meta{
    display:flex;
    flex-direction:column;
    gap:6px;
}

.do-mobile-row{
    display:flex;
    justify-content:space-between;
    align-items:flex-start;
    gap:14px;
    padding-top:6px;
    border-top:1px solid #edf2f7;
}

.do-mobile-key{
    font-size:12px;
    font-weight:700;
    color:#64748b;
    min-width:92px;
}

.do-mobile-val{
    font-size:13px;
    font-weight:600;
    color:#0f172a;
    text-align:right;
    word-break:break-word;
}

.do-mobile-empty{
    text-align:center;
    color:#64748b;
    padding:20px 12px;
}

.do-mobile-badge{
    display:inline-flex;
    border-radius:999px;
    border:1px solid;
    padding:4px 10px;
    font-size:11px;
    font-weight:800;
    text-transform:uppercase;
    letter-spacing:.2px;
    white-space:nowrap;
}

.do-mobile-badge-active{
    color:#166534;
    background:#dcfce7;
    border-color:#86efac;
}

.do-mobile-badge-cancelled{
    color:#9f1239;
    background:#ffe4e6;
    border-color:#fda4af;
}

.do-mobile-badge-default{
    color:#374151;
    background:#f3f4f6;
    border-color:#d1d5db;
}
</style>Ҍ�uw]����������҂0G
?'l����<?php
if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to view Delivery Order records.</div>';
    return;
}

if (!function_exists('get_mssql')) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Function get_mssql() not found.</div>';
    return;
}

$conn = get_mssql();
if (!$conn) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Failed to connect to MSSQL.</div>';
    return;
}

function ac_do_dt($v) {
    if ($v instanceof DateTime) return $v->format('Y-m-d H:i:s');
    return is_string($v) ? $v : '';
}

function ac_do_errors() {
    if (!function_exists('sqlsrv_errors')) return 'Unknown SQLSRV error.';
    $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
    if (empty($errors)) return 'Unknown SQLSRV error.';

    $out = array();
    foreach ($errors as $e) {
        $out[] = '[' . ($e['code'] ?? '') . '] ' . ($e['message'] ?? '');
    }
    return implode(' | ', $out);
}

$q        = trim(wp_unslash($_GET['q'] ?? ''));
$status   = strtoupper(trim(wp_unslash($_GET['status'] ?? 'ALL')));
$dateFrom = trim(wp_unslash($_GET['dateFrom'] ?? ''));
$dateTo   = trim(wp_unslash($_GET['dateTo'] ?? ''));
$limit    = max(1, min(300, intval($_GET['limit'] ?? 100)));

$where  = array();
$params = array();

if ($q !== '') {
    $where[] = "(DOH.DocNo LIKE ? OR DOH.DebtorCode LIKE ? OR DOH.DebtorName LIKE ?)";
    $like = '%' . $q . '%';
    $params[] = $like;
    $params[] = $like;
    $params[] = $like;
}

if ($dateFrom !== '') {
    $where[] = "CAST(DOH.DocDate AS date) >= ?";
    $params[] = $dateFrom;
}

if ($dateTo !== '') {
    $where[] = "CAST(DOH.DocDate AS date) <= ?";
    $params[] = $dateTo;
}

if ($status === 'ACTIVE') {
    $where[] = "ISNULL(DOH.Cancelled,'F') <> 'T'";
} elseif ($status === 'CANCELLED') {
    $where[] = "ISNULL(DOH.Cancelled,'F') = 'T'";
}

$whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';

$sql = "
    SELECT TOP ($limit)
        DOH.DocKey,
        DOH.DocNo,
        DOH.DocDate,
        DOH.DebtorCode,
        DOH.DebtorName,
        ISNULL(DOH.DocStatus,'') AS DocStatus,
        ISNULL(DOH.Cancelled,'F') AS Cancelled,
        DOH.CreatedTimeStamp,
        DOH.LastModified
    FROM dbo.DO AS DOH
    $whereSql
    ORDER BY DOH.DocDate DESC, DOH.DocNo DESC
";

$stmt = sqlsrv_query($conn, $sql, $params);

if ($stmt === false) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">'
        . esc_html(ac_do_errors())
        . '</div>';
    return;
}

$rows = array();
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
    $docStatus = strtoupper(trim((string)($row['DocStatus'] ?? '')));
    $cancelled = strtoupper(trim((string)($row['Cancelled'] ?? 'F')));
    $statusText = ($cancelled === 'T') ? 'CANCELLED' : (($docStatus === '' || $docStatus === 'A') ? 'ACTIVE' : $docStatus);

    $rows[] = array(
        'docKey'       => (int)($row['DocKey'] ?? 0),
        'docNo'        => (string)($row['DocNo'] ?? ''),
        'docDate'      => ($row['DocDate'] instanceof DateTime) ? $row['DocDate']->format('Y-m-d') : (string)($row['DocDate'] ?? ''),
        'debtorCode'   => (string)($row['DebtorCode'] ?? ''),
        'debtorName'   => (string)($row['DebtorName'] ?? ''),
        'status'       => $statusText,
        'createdAt'    => ac_do_dt($row['CreatedTimeStamp'] ?? null),
        'lastModified' => ac_do_dt($row['LastModified'] ?? null),
    );
}
sqlsrv_free_stmt($stmt);

$edit_base = home_url('/index.php/delivery-order-edit/'); // kept for possible future use
?>

<div class="dol-wrap">
    <div class="dol-head">
        <h1>Delivery Order Records</h1>
    </div>

    <div class="dol-card">
        <form method="get" class="dol-filter-form">
            <div class="dol-filter-grid">
                <div>
                    <label class="dol-label">Search</label>
                    <input name="q" class="dol-input" type="text" placeholder="Doc No, Debtor" value="<?php echo esc_attr($q); ?>">
                </div>

                <div>
                    <label class="dol-label">Status</label>
                    <select name="status" class="dol-input">
                        <option value="ALL" <?php selected($status, 'ALL'); ?>>All</option>
                        <option value="ACTIVE" <?php selected($status, 'ACTIVE'); ?>>Active</option>
                        <option value="CANCELLED" <?php selected($status, 'CANCELLED'); ?>>Cancelled</option>
                    </select>
                </div>

                <div>
                    <label class="dol-label">From Date</label>
                    <input name="dateFrom" class="dol-input" type="date" value="<?php echo esc_attr($dateFrom); ?>">
                </div>

                <div>
                    <label class="dol-label">To Date</label>
                    <input name="dateTo" class="dol-input" type="date" value="<?php echo esc_attr($dateTo); ?>">
                </div>
            </div>

            <div class="dol-actions">
                <button type="submit" class="dol-btn">Refresh</button>
            </div>
        </form>
    </div>

    <div class="dol-card">
        <div class="dol-summary">
            Total rows: <strong><?php echo number_format_i18n(count($rows)); ?></strong>
        </div>

        <div class="dol-table-wrap">
            <table class="dol-table">
                <thead>
                    <tr>
                        <th style="width:60px;">No</th>
                        <th>Doc No</th>
                        <th>Doc Date</th>
                        <th>Debtor Code</th>
                        <th>Debtor Name</th>
                        <th>Status</th>
                        <th>Created</th>
                        <th>Last Modified</th>
                    </tr>
                </thead>
                <tbody>
                    <?php if (empty($rows)): ?>
                        <tr>
                            <td colspan="8" class="dol-empty-cell">No matching delivery order records.</td>
                        </tr>
                    <?php else: ?>
                        <?php foreach ($rows as $i => $r): ?>
                            <?php
                            $badgeClass = 'dol-badge-default';
                            if ($r['status'] === 'ACTIVE') $badgeClass = 'dol-badge-active';
                            if ($r['status'] === 'CANCELLED') $badgeClass = 'dol-badge-cancelled';
                            ?>
                            <tr>
                                <td><?php echo esc_html($i + 1); ?></td>
                                <td><?php echo esc_html($r['docNo'] ?: '-'); ?></td>
                                <td><?php echo esc_html($r['docDate'] ?: '-'); ?></td>
                                <td><?php echo esc_html($r['debtorCode'] ?: '-'); ?></td>
                                <td><?php echo esc_html($r['debtorName'] ?: '-'); ?></td>
                                <td>
                                    <span class="dol-badge <?php echo esc_attr($badgeClass); ?>">
                                        <?php echo esc_html($r['status']); ?>
                                    </span>
                                </td>
                                <td><?php echo esc_html($r['createdAt'] ?: '-'); ?></td>
                                <td><?php echo esc_html($r['lastModified'] ?: '-'); ?></td>
                            </tr>
                        <?php endforeach; ?>
                    <?php endif; ?>
                </tbody>
            </table>
        </div>
    </div>
</div>

<style>
.dol-wrap{max-width:1400px;margin:0 auto;padding:16px;font-family:"Segoe UI",Arial,sans-serif;color:#0f172a}
.dol-head{margin-bottom:12px}
.dol-head h1{margin:0;font-size:32px;font-weight:800}
.dol-card{background:#fff;border:1px solid #dbe5dd;border-radius:12px;box-shadow:0 4px 16px rgba(15,23,42,.05);padding:14px;margin-bottom:12px}
.dol-filter-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}
.dol-label{display:block;margin-bottom:5px;font-size:12px;color:#4b5563;font-weight:700}
.dol-input{width:100%;min-height:42px;border:1px solid #d1d5db;border-radius:8px;padding:10px 12px;font-size:14px;box-sizing:border-box}
.dol-actions{margin-top:10px;display:flex;gap:8px;flex-wrap:wrap}
.dol-btn{min-height:40px;border:none;border-radius:8px;padding:10px 14px;background:#1e7e34;color:#fff;font-size:14px;font-weight:700;cursor:pointer}
.dol-summary{margin-bottom:10px;font-size:14px;color:#334155}
.dol-table-wrap{overflow:auto}
.dol-table{width:100%;border-collapse:collapse;min-width:1100px}
.dol-table th{background:#f8fafc;color:#334155;font-size:13px;font-weight:700;text-align:left;padding:12px;border-bottom:1px solid #dbe5dd;white-space:nowrap}
.dol-table td{font-size:14px;color:#0f172a;padding:12px;border-bottom:1px solid #edf2f7;vertical-align:middle}
.dol-table tbody tr:hover{background:#f9fbfa}
.dol-empty-cell{text-align:center;color:#64748b;padding:20px}
.dol-badge{display:inline-flex;border-radius:999px;border:1px solid;padding:4px 10px;font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.2px}
.dol-badge-active{color:#166534;background:#dcfce7;border-color:#86efac}
.dol-badge-cancelled{color:#9f1239;background:#ffe4e6;border-color:#fda4af}
.dol-badge-default{color:#374151;background:#f3f4f6;border-color:#d1d5db}
@media (max-width:980px){
  .dol-filter-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:640px){
  .dol-filter-grid{grid-template-columns:1fr}
  .dol-wrap{padding:10px}
}
</style>҂0G/������������>
??� 'Search';
    $('ac_do_picker_search').placeholder = opts.placeholder || 'Type to search...';
    $('ac_do_picker_search').value = opts.initialValue || '';

    $('ac_do_picker_modal').classList.add('active');
    $('ac_do_picker_modal').setAttribute('aria-hidden', 'false');
    document.body.classList.add('ac-do-picker-open');

    renderPickerNote(($('ac_do_picker_search').value || '').trim() ? 'Searching...' : 'Type to search');

    setTimeout(() => {
      const input = $('ac_do_picker_search');

      if(($('ac_do_picker_search').value || '').trim()){
        runPickerSearch($('ac_do_picker_search').value);
      }

      input.focus({ preventScroll: true });
      input.select();
    }, 80);
  }

  function closePicker(){
    $('ac_do_picker_modal').classList.remove('active');
    $('ac_do_picker_modal').setAttribute('aria-hidden', 'true');
    $('ac_do_picker_search').value = '';
    $('ac_do_picker_results').innerHTML = '';
    pickerState.items = [];
    pickerState.fetchFn = null;
    pickerState.onPick = null;
    document.body.classList.remove('ac-do-picker-open');
  }

  function openDebtorPicker(){
    openPicker({
      title: 'Select Debtor',
      placeholder: 'Search debtor...',
      initialValue: $('acDoDebtorInput').value || '',
      fetchFn: searchDebtorsLive,
      onPick: (picked) => {
        if(!picked) return;

        $('acDoDebtorInput').value = picked.name || picked.code || '';
        $('ac_do_customer').value = picked.code || '';
        $('ac_do_customer_name').value = picked.name || '';
        $('ac_do_sales_agent').value = picked.salesAgent || '';

        closePicker();
      }
    });
  }

  function openItemPicker(){
    openPicker({
      title: 'Select Item',
      placeholder: 'Search item...',
      initialValue: $('ac_do_item_name').value || '',
      fetchFn: async (q) => {
        const items = await searchItemsLive(q);
        return items.map((it) => ({
          label: it.name || it.code || '',
          meta: (DROPDOWN_META.showItemCode && it.code) ? it.code : '',
          raw: {
            code: it.code || '',
            name: it.name || it.code || ''
          }
        }));
      },
      onPick: (picked) => {
        if(!picked) return;

        $('ac_do_item_name').value = picked.name || picked.code || '';
        $('ac_do_item').value = picked.code || '';
        $('ac_do_item_display').value = picked.name || picked.code || '';

        updateEntryTotal();
        closePicker();
      }
    });
  }

  function initPickerModal(){
    $('ac_do_picker_close').addEventListener('click', closePicker);
    $('ac_do_picker_backdrop').addEventListener('click', closePicker);

    $('ac_do_picker_search').addEventListener('input', function(){
      runPickerSearch(this.value);
    });

    $('ac_do_picker_search').addEventListener('keydown', function(e){
      if (e.key === 'Enter') {
        e.preventDefault();
        closeMobileKeyboard(e.target);
      }
    });

    $('ac_do_picker_results').addEventListener('click', function(e){
      const btn = e.target.closest('[data-picker-idx]');
      if(!btn) return;

      const idx = parseInt(btn.dataset.pickerIdx, 10);
      if(isNaN(idx) || !pickerState.items[idx]) return;

      const item = pickerState.items[idx];
      if (typeof pickerState.onPick === 'function') {
        pickerState.onPick(item.raw || item);
      }
    });

    document.addEventListener('keydown', function(e){
      if(e.key === 'Escape' && $('ac_do_picker_modal').classList.contains('active')){
        closePicker();
      }
    });
  }

  function initPickerTriggers(){
    $('acDoDebtorInput').setAttribute('readonly', 'readonly');
    $('ac_do_item_name').setAttribute('readonly', 'readonly');

    $('acDoDebtorInput').addEventListener('click', openDebtorPicker);
    $('ac_do_item_name').addEventListener('click', openItemPicker);

    $('acDoDebtorInput').addEventListener('keydown', function(e){
      if(e.key === 'Enter' || e.key === ' '){
        e.preventDefault();
        openDebtorPicker();
      }
    });

    $('ac_do_item_name').addEventListener('keydown', function(e){
      if(e.key === 'Enter' || e.key === ' '){
        e.preventDefault();
        openItemPicker();
      }
    });
  }

  function makeClientRequestId(prefix='DO'){
    const rnd = Math.random().toString(36).slice(2,10);
    const ts  = Date.now().toString(36);
    return `${prefix}-${ts}-${rnd}`;
  }

  function clearLineEntry(){
    setPackType('CARTON');
    $('ac_do_qty').value = '';
    $('ac_do_kg').value = '';
    $('ac_do_total').value = '0.00';
    $('ac_do_item_name').value = '';
    $('ac_do_item').value = '';
    $('ac_do_item_display').value = '';
    updateEntryTotal();
  }

  (function init(){
    const d = new Date();
    $('ac_do_date').value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;

    initPickerModal();
    initPickerTriggers();

    $('ac_do_qty').addEventListener('keydown', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        closeMobileKeyboard(e.target);
      }
    });

    $('ac_do_kg').addEventListener('keydown', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        closeMobileKeyboard(e.target);
      }
    });

    $('ac_do_qty').addEventListener('input', updateEntryTotal);
    $('ac_do_kg').addEventListener('input', updateEntryTotal);
    $('ac_do_pack_type').addEventListener('change', () => setPackType($('ac_do_pack_type').value));

    initPackTypeToggle();
    updateEntryTotal();
    renderLines();
  })();

  $('ac_do_addline').addEventListener('click', () => {
    const itemCode = ($('ac_do_item').value || '').trim();
    const itemName = ($('ac_do_item_display').value || '').trim() || itemCode;
    const packType = ($('ac_do_pack_type').value || '').trim();
    const qtyRaw = ($('ac_do_qty').value || '').trim();
    const kgRaw = ($('ac_do_kg').value || '').trim();
    const qty = parseFloat(qtyRaw || '0') || 0;
    const kg = parseWhole(kgRaw || '0');
    const total = calcTotal(qty, kg);

    if(!itemCode){
      setStatus('Please select Item Name from picker.', 'error');
      return;
    }

    if(qtyRaw === '' || qty <= 0){
      setStatus('Qty must be greater than 0.', 'error');
      return;
    }

    if(kgRaw === '' || kg <= 0){
      setStatus('KG must be greater than 0.', 'error');
      return;
    }

    state.lines.push({ itemCode, itemName, packType, qty, kg, total });
    renderLines();
    clearLineEntry();
    setStatus('Line added.', 'success');
  });

  $('ac_do_lines').addEventListener('click', (e) => {
    const btn = e.target.closest('button[data-idx]');
    if(!btn) return;

    const idx = parseInt(btn.dataset.idx, 10);
    if(isNaN(idx)) return;

    state.lines.splice(idx, 1);
    renderLines();
    setStatus('Line removed.', 'warning');
  });

  $('ac_do_submit').addEventListener('click', async () => {
    if (state.isSubmitting) {
      setStatus('Already submitting, please wait...', 'warning');
      return;
    }

    const submitBtn = $('ac_do_submit');
    state.jobFinished = false;
    state.isSubmitting = true;

    try {
      submitBtn.disabled = true;
      submitBtn.textContent = 'Saving...';

      const customerCode = ($('ac_do_customer').value || '').trim();
      const customerName = ($('ac_do_customer_name').value || '').trim();
      const salesAgent = ($('ac_do_sales_agent').value || '').trim();
      const location = ($('ac_do_location').value || '').trim();
      const docDate = ($('ac_do_date').value || '').trim();

      if(!customerCode){
        setStatus('Please select Debtor Name.', 'error');
        return;
      }

      if(!docDate){
        setStatus('Today date is missing.', 'error');
        return;
      }

      if(!location){
        setStatus('Location is missing.', 'error');
        return;
      }

      if(!state.lines.length){
        setStatus('Please add at least 1 line item.', 'error');
        return;
      }

      const payload = {
        customerCode,
        customerName,
        salesAgent,
        debtorCode: customerCode,
        DebtorCode: customerCode,
        debtorName: customerName,
        DebtorName: customerName,
        SalesAgent: salesAgent,
        location,
        Location: location,
        docDate,
        remark: '',
        lines: state.lines.map(l => ({
          itemCode: l.itemCode,
          qty: l.total,
          uom: 'KG',
          unitPrice: 0,
          amount: 0,
          taxCode: 'SR-0',
          taxRate: 0,
          packType: l.packType,
          cartonQty: l.qty,
          kg: l.kg,
          totalKg: l.total,
          location
        }))
      };

      const body = {
        type: 'DELIVERY_ORDER',
        client_request_id: makeClientRequestId('DO'),
        source: 'wp-ui',
        payload
      };

      const r = await apiPost(REST_JOB_POST, body);
      const jobId = r.jobId || r.id || (r.job && r.job.id);

      if (!jobId) {
        setStatus('Job created but jobId missing in response.', 'warning');
        showToast('warning', 'Job created, but jobId missing');
        return;
      }

      setStatus(`Job queued: #${jobId}. Waiting BridgeWorker...`, 'info');
      showToast('info', 'Job queued', `Job #${jobId}`);
      await pollJob(jobId);

    } catch(err) {
      setStatus('Submit error: ' + (err.message || String(err)), 'error');
      showModal('error', 'Submit error', escapeHtml(err.message || String(err)));
    } finally {
      state.isSubmitting = false;
      submitBtn.disabled = false;
      submitBtn.textContent = 'Save Delivery Order';
    }
  });
})();
</script>

<!-- ================================================================= -->
<!-- BASKET RETURN SCRIPT (identical to original)                     -->
<!-- ================================================================= -->
<script>
(function(){
  const wrap = document.getElementById('ac-basket-return-mobile-root');
  if (!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE = wrap.dataset.restNonce || '';
  const REST_RETURN_URL = wrap.dataset.restReturnUrl || '';
  const SHOW_DEBTOR_CODE = wrap.dataset.showDebtorCode === '1';

  const $ = (id) => wrap.querySelector('#' + id);
  let isSubmitting = false;

  function esc(s){
    if (s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
  }

  function toast(icon, title, text=''){
    if (!window.Swal) return;
    Swal.fire({
      toast:true,
      position:'top-end',
      icon, title, text,
      showConfirmButton:false,
      timer:2200,
      timerProgressBar:true
    });
  }

  function setStatus(text){
    const el = $('ac_br_status');
    if (el) el.textContent = text || '';
  }

  function whole(n){
    const x = Number(n);
    if (!isFinite(x) || x < 0) return 0;
    return Math.round(x);
  }

  function formatDisplayValue(name, code){
    const safeName = (name || '').trim();
    const safeCode = (code || '').trim();

    if (SHOW_DEBTOR_CODE && safeName && safeCode) {
      return `${safeName} (${safeCode})`;
    }

    return safeName || safeCode;
  }

  async function apiPost(url, body){
    const res = await fetch(url, {
      method:'POST',
      credentials:'same-origin',
      headers:{
        'Content-Type':'application/json',
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      body: JSON.stringify(body)
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  (function initDate(){
    const dateInput = $('ac_br_date');
    if (!dateInput) return;

    if (!dateInput.value) {
      const d = new Date();
      dateInput.value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
    }
  })();

  (function initDebtorDropdown(){
    const wrapper = $('acBrDebtorWrapper');
    if (!wrapper) return;

    const AJAX_URL = wrapper.dataset.ajaxUrl;
    const NONCE = wrapper.dataset.nonce;
    const input = $('acBrDebtorInput');
    const hiddenCode = $('ac_br_debtor_code');
    const hiddenName = $('ac_br_debtor_name');
    const dropdown = $('acBrDebtorDropdown');

    let items = [];
    let activeIndex = -1;
    let abortController = null;
    let timer = null;

    const show = () => dropdown.classList.add('active');
    const hide = () => {
      dropdown.classList.remove('active');
      activeIndex = -1;
    };

    const render = () => {
      if (!items.length) {
        dropdown.innerHTML = '<div class="br-item"><small>No customer found</small></div>';
        show();
        return;
      }

      dropdown.innerHTML = items.map((it, idx) => {
        const code = it.code || it.debtorCode || it.id || '';
        const name = it.name || it.debtorName || it.description || '';

        return `
          <div class="br-item" data-index="${idx}">
            <strong>${esc(name || code)}</strong>
            ${SHOW_DEBTOR_CODE && code ? `<small>${esc(code)}</small>` : ''}
          </div>
        `;
      }).join('');

      show();
    };

    const pick = (idx) => {
      if (idx < 0 || idx >= items.length) return;

      const it = items[idx];
      const code = it.code || it.debtorCode || it.id || '';
      const name = it.name || it.debtorName || it.description || '';

      input.value = formatDisplayValue(name, code);
      hiddenCode.value = code;
      hiddenName.value = name;
      hide();
    };

    const search = async (q) => {
      if (abortController) abortController.abort();
      abortController = new AbortController();

      const query = (q || '').trim();
      if (query.length < 1) {
        hide();
        return;
      }

      dropdown.innerHTML = '<div class="br-item"><small>Searching...</small></div>';
      show();

      try {
        const url = `${AJAX_URL}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(NONCE)}&q=${encodeURIComponent(query)}`;
        const res = await fetch(url, { signal: abortController.signal, credentials:'same-origin' });
        const data = await res.json();

        if (!data.success) {
          throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');
        }

        items = (data.data && data.data.items) ? data.data.items : [];
        render();
      } catch (err) {
        if (err.name === 'AbortError') return;
        dropdown.innerHTML = '<div class="br-item"><small>Failed to load</small></div>';
        show();
      }
    };

    input.addEventListener('input', () => {
      hiddenCode.value = '';
      hiddenName.value = '';
      clearTimeout(timer);
      timer = setTimeout(() => search(input.value), 300);
    });

    input.addEventListener('keydown', (e) => {
      if (!dropdown.classList.contains('active')) return;

      const nodes = Array.from(dropdown.querySelectorAll('.br-item[data-index]'));
      if (!nodes.length) return;

      if (e.key === 'ArrowDown') {
        e.preventDefault();
        activeIndex = Math.min(activeIndex + 1, nodes.length - 1);
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        activeIndex = Math.max(activeIndex - 1, 0);
      } else if (e.key === 'Enter') {
        if (activeIndex >= 0) {
          e.preventDefault();
          pick(activeIndex);
        }
      } else if (e.key === 'Escape') {
        hide();
      }

      nodes.forEach((n, i) => n.classList.toggle('active', i === activeIndex));
    });

    dropdown.addEventListener('��>�EE����������>
?
�����click', (e) => {
      const node = e.target.closest('.br-item[data-index]');
      if (!node) return;
      pick(parseInt(node.dataset.index, 10));
    });

    document.addEventListener('click', (e) => {
      if (!wrapper.contains(e.target)) {
        hide();
      }
    });
  })();

  $('ac_br_submit').addEventListener('click', async () => {
    if (isSubmitting) return;

    const debtorCode = ($('ac_br_debtor_code').value || '').trim();
    const debtorName = ($('ac_br_debtor_name').value || '').trim();
    const docDate = ($('ac_br_date').value || '').trim();
    const basketQty = whole(($('ac_br_qty').value || '0'));
    const remark = ($('ac_br_remark').value || '').trim();
    const savedLabel = debtorName || debtorCode;

    if (!debtorCode) {
      toast('error', 'Customer required');
      setStatus('Please select customer.');
      return;
    }

    if (!docDate) {
      toast('error', 'Date required');
      setStatus('Today date is missing.');
      return;
    }

    if (basketQty <= 0) {
      toast('error', 'Invalid quantity');
      setStatus('Basket Return Qty must be greater than 0.');
      return;
    }

    if (!REST_RETURN_URL) {
      toast('error', 'Config error');
      setStatus('Basket return endpoint is missing.');
      return;
    }

    const submitBtn = $('ac_br_submit');

    try {
      isSubmitting = true;
      submitBtn.disabled = true;
      submitBtn.textContent = 'Saving...';
      setStatus('Saving basket return...');

      const payload = {
        debtorCode,
        debtorName,
        docDate,
        basketQty,
        remark
      };

      const res = await apiPost(REST_RETURN_URL, payload);
      if (!res || !res.ok) {
        throw new Error((res && (res.message || res.error)) ? (res.message || res.error) : 'Failed to save basket return');
      }

      const suffix = res.duplicate ? ' (duplicate ignored)' : '';
      setStatus(`Saved basket return for ${savedLabel}: ${basketQty}${suffix}`);
      toast('success', 'Basket return saved', `${savedLabel} | Qty ${basketQty}${suffix}`);

      $('ac_br_qty').value = '1';
      $('ac_br_remark').value = '';
      $('ac_br_qty').focus();
    } catch (err) {
      const msg = err && err.message ? err.message : 'Failed to save basket return';
      setStatus(msg);
      toast('error', 'Save failed', msg);
    } finally {
      isSubmitting = false;
      submitBtn.disabled = false;
      submitBtn.textContent = 'Save Basket Return';
    }
  });
})();
</script>

<!-- ================================================================= -->
<!-- Tab switching logic (simple, no re-init)                         -->
<!-- ================================================================= -->
<script>
(function(){
  const tabs = document.querySelectorAll('.ac-tab-btn');
  const panes = {
    delivery: document.getElementById('ac-tab-delivery'),
    basket: document.getElementById('ac-tab-basket')
  };
  function activateTab(tabId){
    tabs.forEach(btn => btn.classList.toggle('active', btn.dataset.tab === tabId));
    Object.keys(panes).forEach(id => panes[id].classList.toggle('active', id === tabId));
  }
  tabs.forEach(btn => btn.addEventListener('click', () => {
    const tabId = btn.dataset.tab;
    if(tabId && panes[tabId]) activateTab(tabId);
  }));
  if(!document.querySelector('.ac-tab-btn.active')) activateTab('delivery');
})();
</script>��>°k���������|.<
??�size:11px;
    }
  }
</style>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

<script>
(function(){
  const wrap = document.getElementById('ac-do-create-mobile-root');
  if(!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE    = wrap.dataset.restNonce;
  const EDIT_BASE     = wrap.dataset.editBase;
  const REST_JOB_POST = wrap.dataset.restJobPost;
  const REST_JOB_BASE = wrap.dataset.restJobBase;

  const $ = (id) => wrap.querySelector('#' + id);
  const state = { lines: [], jobFinished: false, isSubmitting: false };

  const DROPDOWN_META = {
    showDebtorCode: false,
    showItemCode: false,
    showSalesAgent: false
  };

  function escapeHtml(s){
    if(s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({
      '&':'&amp;',
      '<':'&lt;',
      '>':'&gt;',
      '"':'&quot;',
      "'":'&#039;'
    }[c]));
  }

  function fmtNum(n){
    const x = parseFloat(n);
    return isNaN(x) ? '0.00' : x.toFixed(2);
  }

  function parseWhole(n){
    const x = Number(n);
    if(!isFinite(x) || x < 0) return 0;
    return Math.round(x);
  }

  function fmtWhole(n){
    return String(parseWhole(n));
  }

  // Helper to close mobile keyboard on Enter
  function closeMobileKeyboard(el){
    if (!el) return;
    try { el.blur(); } catch(e) {}
    if (document.activeElement && typeof document.activeElement.blur === 'function') {
      try { document.activeElement.blur(); } catch(e) {}
    }
  }

  function setStatus(msg, type='info'){
    const toastType =
      type === 'success' ? 'success' :
      type === 'error'   ? 'error' :
      type === 'warning' ? 'warning' :
      'info';

    if (window.Swal) {
      Swal.fire({
        toast: true,
        position: 'top-end',
        icon: toastType,
        title: msg.replace(/<[^>]*>/g, ''),
        showConfirmButton: false,
        timer: 1800,
        timerProgressBar: true
      });
    }
  }

  function showToast(icon, title, text=''){
    if (window.Swal) {
      Swal.fire({
        toast: true,
        position: 'top-end',
        icon,
        title,
        text,
        showConfirmButton: false,
        timer: 2600,
        timerProgressBar: true
      });
    }
  }

  function showModal(icon, title, html){
    if (window.Swal) {
      Swal.fire({
        icon,
        title,
        html,
        confirmButtonText: 'OK'
      });
    }
  }

  function calcTotal(qty, kg){
    return (parseFloat(qty) || 0) * (parseFloat(kg) || 0);
  }

  function updateEntryTotal(){
    const itemCode = ($('ac_do_item').value || '').trim();
    const itemName = ($('ac_do_item_display').value || '').trim() || itemCode;
    const packType = ($('ac_do_pack_type').value || '').trim();

    const qtyRaw = ($('ac_do_qty').value || '').trim();
    const kgRaw = ($('ac_do_kg').value || '').trim();
    const qty = parseFloat(qtyRaw || '0') || 0;
    const kg = parseWhole(kgRaw || '0');
    const total = calcTotal(qty, kg);

    $('ac_do_total').value = fmtNum(total);

    const pv = $('ac_do_line_preview');
    if(!itemCode || qtyRaw === '' || kgRaw === ''){
      pv.style.display = 'none';
      pv.innerHTML = '';
      return;
    }

    pv.style.display = 'block';
    pv.innerHTML =
      '<div><b>' + escapeHtml(itemName) + '</b></div>' +
      '<div>Type: ' + escapeHtml(packType) + ' | Qty: ' + fmtNum(qty) + ' | KG: ' + fmtWhole(kg) + ' | Total: ' + fmtNum(total) + '</div>';
  }

  function setPackType(type){
    const nextType = String(type || '').toUpperCase() === 'BASKET' ? 'BASKET' : 'CARTON';
    $('ac_do_pack_type').value = nextType;

    wrap.querySelectorAll('#ac_do_pack_type_toggle .ac-do-type-btn').forEach((btn) => {
      const btnType = (btn.dataset.packType || '').toUpperCase();
      btn.classList.toggle('active', btnType === nextType);
    });

    updateEntryTotal();
  }

  function initPackTypeToggle(){
    wrap.querySelectorAll('#ac_do_pack_type_toggle .ac-do-type-btn').forEach((btn) => {
      btn.addEventListener('click', () => {
        setPackType(btn.dataset.packType || 'CARTON');
      });
    });
    setPackType($('ac_do_pack_type').value || 'CARTON');
  }

  function updateLineCount(){
    const count = state.lines.length;
    $('ac_do_line_count').textContent = count + (count === 1 ? ' line' : ' lines');
  }

  function renderLines(){
    const list = $('ac_do_lines');
    list.innerHTML = '';

    if(!state.lines.length){
      list.innerHTML = '<div class="ac-do-no-items">No items yet</div>';
      updateLineCount();
      return;
    }

    state.lines.forEach((l, idx) => {
      const row = document.createElement('div');
      row.className = 'ac-do-line-card';
      row.innerHTML = `
        <div class="ac-do-line-top">
          <div class="ac-do-line-name">${idx + 1}. ${escapeHtml(l.itemName || l.itemCode)}</div>
          <div class="ac-do-line-badge">${escapeHtml(l.packType)}</div>
        </div>

        <div class="ac-do-line-meta">
          <div class="ac-do-line-stat">
            <span class="ac-do-line-meta-label">Qty</span>
            <span class="ac-do-line-meta-value">${fmtNum(l.qty)}</span>
          </div>
          <div class="ac-do-line-stat">
            <span class="ac-do-line-meta-label">KG</span>
            <span class="ac-do-line-meta-value">${fmtWhole(l.kg)}</span>
          </div>
          <div class="ac-do-line-stat">
            <span class="ac-do-line-meta-label">Total</span>
            <span class="ac-do-line-meta-value">${fmtNum(l.total)}</span>
          </div>
        </div>

        <button type="button" class="ac-do-del-btn" data-idx="${idx}">Delete Line</button>
      `;
      list.appendChild(row);
    });

    updateLineCount();
  }

  async function apiGet(url){
    const res = await fetch(url, {
      method:'GET',
      credentials:'same-origin',
      headers:{
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      cache:'no-store'
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    const text = await res.text();
    let data = null;
    try {
      data = text ? JSON.parse(text) : null;
    } catch (e) {
      throw new Error('Invalid JSON from job status endpoint');
    }
    return data;
  }

  async function apiPost(url, body){
    const res = await fetch(url, {
      method:'POST',
      credentials:'same-origin',
      headers:{
        'Content-Type':'application/json',
        'Accept':'application/json',
        'X-WP-Nonce': REST_NONCE
      },
      body: JSON.stringify(body)
    });

    if (!res.ok) {
      let errMsg = `HTTP ${res.status}`;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  async function pollJob(jobId){
    const max = 45;

    for (let i = 0; i < max; i++) {
      await new Promise(r => setTimeout(r, 2000));

      let r;
      try {
        r = await apiGet(REST_JOB_BASE + jobId + '?_t=' + Date.now());
      } catch (err) {
        setStatus('Polling error: ' + (err.message || String(err)), 'error');
        showToast('error', 'Polling failed', err.message || 'Unable to read job status');
        return false;
      }

      const job = r?.job || r?.data?.job || r;
      const st = String(job?.status || '').toUpperCase();
      if (!st) continue;

      setStatus(`Job #${jobId}: ${st}`, (
        st === 'SUCCESS' ? 'success' :
        (st === 'FAILED' || st === 'FAILED_FINAL') ? 'error' :
        st === 'CANCELLED' ? 'warning' :
        'info'
      ));

      if (st === 'SUCCESS') {
        if (state.jobFinished) return true;
        state.jobFinished = true;

        let result = job.result || {};
        if (typeof result === 'string') {
          try { result = JSON.parse(result); } catch(e) {}
        }

        const docNo = result.docNo || result.DocNo || job.docNo || '';
        if (docNo) {
          const editUrl = EDIT_BASE + '?docno=' + encodeURIComponent(docNo);
          showModal(
            'success',
            'Delivery Order Created',
            `Document <b>${escapeHtml(docNo)}</b> was saved successfully.<br><br>
             <a href="${editUrl}" target="_blank" rel="noopener">Open Delivery Order</a>`
          );
        } else {
          showToast('success', 'Delivery Order created');
        }
        return true;
      }

      if (st === 'FAILED' || st === 'FAILED_FINAL') {
        if (state.jobFinished) return false;
        state.jobFinished = true;

        let result = job.result || {};
        if (typeof result === 'string') {
          try { result = JSON.parse(result); } catch(e) {}
        }

        const err = job.error_message || job.error || result.error || 'Job failed';
        showModal('error', 'Delivery Order Failed', escapeHtml(err));
        return false;
      }

      if (st === 'CANCELLED') {
        if (state.jobFinished) return false;
        state.jobFinished = true;
        showToast('warning', 'Job cancelled');
        return false;
      }
    }

    showModal(
      'warning',
      'Queue Timeout',
      'The page did not receive a final status in time.<br><br>Check the Jobs table to confirm whether it already completed.'
    );
    return false;
  }

  async function searchItemsLive(q){
    const ajaxUrl = wrap.dataset.ajaxUrl || "";
    const nonce = wrap.dataset.itemSuggestNonce || "";
    if(!ajaxUrl || !nonce) return [];

    const fd = new FormData();
    fd.append("action", "ac_itemcode_suggest");
    fd.append("nonce", nonce);
    fd.append("term", q);

    const res = await fetch(ajaxUrl, {
      method: "POST",
      credentials: "same-origin",
      body: fd
    });

    let data;
    try {
      data = await res.json();
    } catch(e) {
      console.error('Item search parse error', e);
      return [];
    }

    if (data && data.success === true && data.data && Array.isArray(data.data.items)) {
      return data.data.items.map(it => ({
        code: it.code || '',
        name: (it.desc || it.name || it.description || '').trim()
      }));
    }

    return [];
  }

  // ========== STABLE POPUP MODAL FUNCTIONS ==========
  async function searchDebtorsLive(q){
    const wrapper = acDoDebtorWrapper;
    const AJAX_URL = wrapper ? (wrapper.dataset.ajaxUrl || '') : '';
    const NONCE = wrapper ? (wrapper.dataset.nonce || '') : '';

    if(!AJAX_URL || !NONCE) return [];

    const url = `${AJAX_URL}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(NONCE)}&q=${encodeURIComponent(q)}`;
    const response = await fetch(url, { credentials:'same-origin' });
    const text = await response.text();

    let data;
    try {
      data = JSON.parse(text);
    } catch(e){
      throw new Error('Invalid JSON response');
    }

    if (!data.success) {
      throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');
    }

    const items = (data.data && data.data.items) ? data.data.items : [];

    return items.map((it) => {
      const name = it.name || it.debtorName || it.description || '';
      const code = it.code || it.debtorCode || it.id || '';
      const sa   = (it.salesAgent || it.sales_agent || it.sa || '').trim();

      const debtorMeta = [];
      if (DROPDOWN_META.showDebtorCode && code) debtorMeta.push(code);
      if (DROPDOWN_META.showSalesAgent && sa) debtorMeta.push('SA: ' + sa);

      return {
        label: name || code,
        meta: debtorMeta.join(' • '),
        raw: {
          name,
          code,
          salesAgent: sa
        }
      };
    });
  }

  const pickerState = {
    items: [],
    fetchFn: null,
    onPick: null
  };

  let pickerTimer = null;

  function renderPickerNote(msg){
    $('ac_do_picker_results').innerHTML = `<div class="ac-do-picker-note">${escapeHtml(msg)}</div>`;
  }

  function renderPickerItems(items){
    const box = $('ac_do_picker_results');

    if(!items.length){
      renderPickerNote('No result found');
      return;
    }

    box.innerHTML = items.map((it, idx) => `
      <button type="button" class="ac-do-picker-item" data-picker-idx="${idx}">
        <span class="ac-do-picker-item-main">${escapeHtml(it.label || '')}</span>
        ${it.meta ? `<span class="ac-do-picker-item-sub">${escapeHtml(it.meta)}</span>` : ''}
      </button>
    `).join('');
  }

  async function runPickerSearch(q){
    const query = (q || '').trim();
    clearTimeout(pickerTimer);

    if(query.length < 1){
      pickerState.items = [];
      renderPickerNote('Type to search');
      return;
    }

    pickerTimer = setTimeout(async () => {
      renderPickerNote('Searching...');

      try{
        const items = await pickerState.fetchFn(query);
        pickerState.items = items || [];
        renderPickerItems(pickerState.items);
      }catch(e){
        pickerState.items = [];
        renderPickerNote('Failed to load result');
      }
    }, 220);
  }

  function openPicker(opts){
    pickerState.items = [];
    pickerState.fetchFn = opts.fetchFn;
    pickerState.onPick = opts.onPick;

    $('ac_do_picker_title').textContent = opts.title || 'Search';
    $('ac_do_picker_search').placeholder = opts.placeholder || 'Type to search...';
    $('ac_do_picker_search').value = opts.initialValue || '';

    $('ac_do_picker_modal').classList.add('active');
    $('ac_do_picker_modal').setAttribute('aria-hidden', 'false');
    document.body.classList.add('ac-do-picker-open');

    renderPickerNote(($('ac_do_picker_search').value || '').trim() ? 'Searching...' : 'Type to search');

    setTimeout(() => {
      const input = $('ac_do_picker_search');

      if(($('ac_do_picker_search').value || '').trim()){
        runPickerSearch($('ac_do_picker_search').value);
      }

      input.focus({ preventScroll: true });
      input.select();
    }, 80);
  }

  function closePicker(){
    $('ac_do_picker_modal').classList.remove('active');
    $('ac_do_picker_modal').setAttribute('aria-hidden', 'true');
    $('ac_do_picker_search').value = '';
    $('ac_do_picker_results').innerHTML = '';
    pickerState.items = [];
    pickerState.fetchFn = null;
    pickerState.onPick = null;
    document.body.classList.remove('ac-do-picker-open');
  }

  function openDebtorPicker(){
    openPicker({
      title: 'Select Debtor',
      placeholder: 'Search debtor...',
      initialValue: $('acDoDebtorInput').value || '',
      fetchFn: searchDebtorsLive,
      onPick: (picked) => {
        if(!picked) return;

        $('acDoDebtorInput').value = picked.name || picked.code || '';
        $('ac_do_customer').value = picked.code || '';
        $('ac_do_customer_name').value = picked.name || '';
        $('ac_do_sales_agent').value = picked.salesAgent || '';

        closePicker();
      }
    });
  }

  function openItemPicker(){
    openPicker({
      title: 'Select Item',
      placeholder: 'Search item...',
      initialValue: $('ac_do_item_name').value || '',
      fetchFn: async (q) => {
        const items = await searchItemsLive(q);
        return items.map((it) => ({
          label: it.name || it.code || '',
          meta: (DROPDOWN_META.showItemCode && it.code) ? it.code : '',
          raw: {
            code: it.code || '',
            name: it.name || it.code || ''
          }
        }));
      },
      onPick: (picked) => {
        if(!picked) return;

        $('ac_do_item_name').value = picked.name || picked.code || '';
        $('ac_do_item').value = picked.code || '';
        $('ac_do_item_display').value = picked.name || picked.code || '';

    �|.<����������|.<
?�����    updateEntryTotal();
        closePicker();
      }
    });
  }

  function initPickerModal(){
    $('ac_do_picker_close').addEventListener('click', closePicker);
    $('ac_do_picker_backdrop').addEventListener('click', closePicker);

    $('ac_do_picker_search').addEventListener('input', function(){
      runPickerSearch(this.value);
    });

    // Enter on popup search closes keyboard
    $('ac_do_picker_search').addEventListener('keydown', function(e){
      if (e.key === 'Enter') {
        e.preventDefault();
        closeMobileKeyboard(e.target);
      }
    });

    $('ac_do_picker_results').addEventListener('click', function(e){
      const btn = e.target.closest('[data-picker-idx]');
      if(!btn) return;

      const idx = parseInt(btn.dataset.pickerIdx, 10);
      if(isNaN(idx) || !pickerState.items[idx]) return;

      const item = pickerState.items[idx];
      if (typeof pickerState.onPick === 'function') {
        pickerState.onPick(item.raw || item);
      }
    });

    document.addEventListener('keydown', function(e){
      if(e.key === 'Escape' && $('ac_do_picker_modal').classList.contains('active')){
        closePicker();
      }
    });
  }

  function initPickerTriggers(){
    $('acDoDebtorInput').setAttribute('readonly', 'readonly');
    $('ac_do_item_name').setAttribute('readonly', 'readonly');

    $('acDoDebtorInput').addEventListener('click', openDebtorPicker);
    $('ac_do_item_name').addEventListener('click', openItemPicker);

    $('acDoDebtorInput').addEventListener('keydown', function(e){
      if(e.key === 'Enter' || e.key === ' '){
        e.preventDefault();
        openDebtorPicker();
      }
    });

    $('ac_do_item_name').addEventListener('keydown', function(e){
      if(e.key === 'Enter' || e.key === ' '){
        e.preventDefault();
        openItemPicker();
      }
    });
  }

  function makeClientRequestId(prefix='DO'){
    const rnd = Math.random().toString(36).slice(2,10);
    const ts  = Date.now().toString(36);
    return `${prefix}-${ts}-${rnd}`;
  }

  function clearLineEntry(){
    setPackType('CARTON');
    $('ac_do_qty').value = '';
    $('ac_do_kg').value = '';
    $('ac_do_total').value = '0.00';
    $('ac_do_item_name').value = '';
    $('ac_do_item').value = '';
    $('ac_do_item_display').value = '';
    updateEntryTotal();
  }

  (function init(){
    const d = new Date();
    $('ac_do_date').value = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;

    initPickerModal();
    initPickerTriggers();

    // Qty: Enter closes keyboard
    $('ac_do_qty').addEventListener('keydown', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        closeMobileKeyboard(e.target);
      }
    });

    // KG: Enter closes keyboard
    $('ac_do_kg').addEventListener('keydown', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        closeMobileKeyboard(e.target);
      }
    });

    $('ac_do_qty').addEventListener('input', updateEntryTotal);
    $('ac_do_kg').addEventListener('input', updateEntryTotal);
    $('ac_do_pack_type').addEventListener('change', () => setPackType($('ac_do_pack_type').value));

    initPackTypeToggle();
    updateEntryTotal();
    renderLines();
  })();

  $('ac_do_addline').addEventListener('click', () => {
    const itemCode = ($('ac_do_item').value || '').trim();
    const itemName = ($('ac_do_item_display').value || '').trim() || itemCode;
    const packType = ($('ac_do_pack_type').value || '').trim();
    const qtyRaw = ($('ac_do_qty').value || '').trim();
    const kgRaw = ($('ac_do_kg').value || '').trim();
    const qty = parseFloat(qtyRaw || '0') || 0;
    const kg = parseWhole(kgRaw || '0');
    const total = calcTotal(qty, kg);

    if(!itemCode){
      setStatus('Please select Item Name from picker.', 'error');
      return;
    }

    if(qtyRaw === '' || qty <= 0){
      setStatus('Qty must be greater than 0.', 'error');
      return;
    }

    if(kgRaw === '' || kg <= 0){
      setStatus('KG must be greater than 0.', 'error');
      return;
    }

    state.lines.push({ itemCode, itemName, packType, qty, kg, total });
    renderLines();
    clearLineEntry();
    setStatus('Line added.', 'success');
  });

  $('ac_do_lines').addEventListener('click', (e) => {
    const btn = e.target.closest('button[data-idx]');
    if(!btn) return;

    const idx = parseInt(btn.dataset.idx, 10);
    if(isNaN(idx)) return;

    state.lines.splice(idx, 1);
    renderLines();
    setStatus('Line removed.', 'warning');
  });

  $('ac_do_submit').addEventListener('click', async () => {
    if (state.isSubmitting) {
      setStatus('Already submitting, please wait...', 'warning');
      return;
    }

    const submitBtn = $('ac_do_submit');
    state.jobFinished = false;
    state.isSubmitting = true;

    try {
      submitBtn.disabled = true;
      submitBtn.textContent = 'Saving...';

      const customerCode = ($('ac_do_customer').value || '').trim();
      const customerName = ($('ac_do_customer_name').value || '').trim();
      const salesAgent = ($('ac_do_sales_agent').value || '').trim();
      const location = ($('ac_do_location').value || '').trim();
      const docDate = ($('ac_do_date').value || '').trim();

      if(!customerCode){
        setStatus('Please select Debtor Name.', 'error');
        return;
      }

      if(!docDate){
        setStatus('Today date is missing.', 'error');
        return;
      }

      if(!location){
        setStatus('Location is missing.', 'error');
        return;
      }

      if(!state.lines.length){
        setStatus('Please add at least 1 line item.', 'error');
        return;
      }

      const payload = {
        customerCode,
        customerName,
        salesAgent,
        debtorCode: customerCode,
        DebtorCode: customerCode,
        debtorName: customerName,
        DebtorName: customerName,
        SalesAgent: salesAgent,
        location,
        Location: location,
        docDate,
        remark: '',
        lines: state.lines.map(l => ({
          itemCode: l.itemCode,
          qty: l.total,
          uom: 'KG',
          unitPrice: 0,
          amount: 0,
          taxCode: 'SR-0',
          taxRate: 0,
          packType: l.packType,
          cartonQty: l.qty,
          kg: l.kg,
          totalKg: l.total,
          location
        }))
      };

      const body = {
        type: 'DELIVERY_ORDER',
        client_request_id: makeClientRequestId('DO'),
        source: 'wp-ui',
        payload
      };

      const r = await apiPost(REST_JOB_POST, body);
      const jobId = r.jobId || r.id || (r.job && r.job.id);

      if (!jobId) {
        setStatus('Job created but jobId missing in response.', 'warning');
        showToast('warning', 'Job created, but jobId missing');
        return;
      }

      setStatus(`Job queued: #${jobId}. Waiting BridgeWorker...`, 'info');
      showToast('info', 'Job queued', `Job #${jobId}`);
      await pollJob(jobId);

    } catch(err) {
      setStatus('Submit error: ' + (err.message || String(err)), 'error');
      showModal('error', 'Submit error', escapeHtml(err.message || String(err)));
    } finally {
      state.isSubmitting = false;
      submitBtn.disabled = false;
      submitBtn.textContent = 'Save Delivery Order';
    }
  });
})();
</script>�|.<&*w�����ג��E�?,��'�	� infimumsupremum�	f��DO-Create?&�
[xyz-ips snippet="DO-Create"]�����j��DO-CREATE-2?&��[xyz-ips snippet="DO-CREATE-2"]����$� t��DOcreate-desktop?&��[xyz-ips snippet="DOcreate-desktop"]����#�(r��DOcreate-mobile?&�J[xyz-ips snippet="DOcreate-mobile"]����"�0p��DOList-desktop?&'l[xyz-ips snippet="DOList-desktop"]����!�
8n�
�DOList-mobile?&-7[xyz-ips snippet="DOList-mobile"]����(�@|��BasketReturn-desktop?
&=K[xyz-ips snippet="BasketReturn-desktop"]����'�Hz��BasketReturn-mobile?&;A[xyz-ips snippet="BasketReturn-mobile"]����)�P�c�
�BasketSummary-desktop?&E�[xyz-ips snippet="BasketSummary-desktop"]����(�X{��BasketSummary-mobile?
&A�[xyz-ips snippet="BasketSummary-mobile"]����}`ɀ�Logout<?php echo do_shortcode('[wppb-logout text="" redirect_url="https://website.ipohserver.com/VegeBasketDO/" link_text=""]'); ?>[xyz-ips snippet="Logout"]�����
hh��DriverPage?&�[xyz-ips snippet="DriverPage"]����(��p���MenuInfo-CurrentUser<?php
if (is_user_logged_in()) {
    $user = wp_get_current_user();

    $display_name = !empty($user->display_name) ? $user->display_name : $user->user_login;

    $initial = function_exists('mb_substr')
        ? mb_strtoupper(mb_substr($display_name, 0, 1))
        : strtoupper(substr($display_name, 0, 1));

    $role = !empty($user->roles) ? $user->roles[0] : '';

    $role_labels = array(
        'administrator'   => 'Administrator',
        'editor'          => 'Editor',
        'author'          => 'Author',
        'contributor'     => 'Contributor',
        'subscriber'      => 'Operations Staff', // change if needed
        'operations_staff'=> 'Operations Staff',
        'delivery_driver' => 'Delivery Driver',
        'staff'           => 'Staff',
    );

    $role_text = isset($role_labels[$role])
        ? $role_labels[$role]
        : ucwords(str_replace(array('-', '_'), ' ', $role));

    echo '<div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:#fff;font-family:Arial,sans-serif;">';
        echo '<div style="width:36px;height:36px;min-width:36px;border-radius:50%;background:#0f6b43;color:#fff;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:700;line-height:1;">' . esc_html($initial) . '</div>';
        echo '<div style="display:flex;flex-direction:column;line-height:1.2;">';
            echo '<div style="font-size:13px;font-weight:600;color:#1f2937;">' . esc_html($display_name) . '</div>';
            echo '<div style="font-size:12px;color:#7a7a7a;margin-top:2px;">' . esc_html($role_text) . '</div>';
        echo '</div>';
    echo '</div>';
}
?>[xyz-ips snippet="MenuInfo-CurrentUser"]����(^�xƀ�MenuInfo-CurrentPage<?php
if (is_front_page() || is_home()) {
    $page_name = 'Dashboard';
} else {
    $page_name = get_the_title();

    if (empty($page_name)) {
        $page_name = wp_title('', false);
    }
}

echo '<div style="color:#ffffff;font-family:Roboto, Arial, sans-serif;font-weight:700;font-size:1rem;">' . esc_html($page_name) . '</div>';
?>[xyz-ips snippet="MenuInfo-CurrentPage"]����&���]��DashboardInfo-Good<?php
$hour = (int) current_time('G');

if ($hour >= 5 && $hour < 12) {
    $greeting = 'Good Morning';
} elseif ($hour >= 12 && $hour < 17) {
    $greeting = 'Good Afternoon';
} else {
    $greeting = 'Good Evening';
}

$user = wp_get_current_user();
$name = !empty($user->display_name) ? $user->display_name : $user->user_login;

echo '<div style="color:#0B4A2D;font-family:Roboto, Arial, sans-serif;font-weight:700;font-size:1.5rem;">' . esc_html($greeting . ', ' . $name) . '</div>';
?>[xyz-ips snippet="DashboardInfo-Good"]����,	��y��DashboardInfo-DO-Counter<?php
global $wpdb;

$table_name          = $wpdb->prefix . 'ac_jobs';
$job_type_column     = 'job_type';        // change if needed
$created_at_column   = 'created_at';      // change if needed
$delivery_order_type = 'DELIVERY_ORDER';  // change if needed

$today = current_time('Y-m-d');

$total_today = (int) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COUNT(*)
         FROM {$table_name}
         WHERE {$job_type_column} = %s
         AND DATE({$created_at_column}) = %s",
        $delivery_order_type,
        $today
    )
);
?>

<div id="ac-delivery-counter" data-target="<?php echo esc_attr($total_today); ?>">0</div>

<style>
#ac-delivery-counter{
    font-size: 2rem;
    font-weight: 700;
    font-family: Roboto, sans-serif;
    color: #000000;
    line-height: 1;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const counter = document.getElementById('ac-delivery-counter');
    if (!counter) return;

    const target = parseInt(counter.dataset.target) || 0;
    const duration = 1000;
    const start = performance.now();

    function animate(now) {
        const progress = Math.min((now - start) / duration, 1);
        const value = Math.floor(target * (1 - Math.pow(1 - progress, 3)));
        counter.textContent = value;

        if (progress < 1) {
            requestAnimationFrame(animate);
        } else {
            counter.textContent = target;
        }
    }

    requestAnimationFrame(animate);
});
</script>[xyz-ips snippet="DashboardInfo-DO-Counter"]����0Y��р�DashboardInfo-Return-Counter<?php
global $wpdb;

$table_name        = $wpdb->prefix . 'ac_basket_ledger';
$txn_type_column   = 'txn_type';
$created_at_column = 'created_at'; // change if needed
$qty_column        = 'qty'; // change if needed

$today = current_time('Y-m-d');

$total_today = (float) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COALESCE(SUM({$qty_column}), 0)
         FROM {$table_name}
         WHERE {$txn_type_column} = %s
         AND DATE({$created_at_column}) = %s",
        'RETURN',
        $today
    )
);
?>

<div id="ac-basket-return-counter" data-target="<?php echo esc_attr($total_today); ?>">0</div>

<style>
#ac-basket-return-counter{
    font-size: 2rem;
    font-weight: 700;
    font-family: Roboto, sans-serif;
    color: #000000;
    line-height: 1;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const counter = document.getElementById('ac-basket-return-counter');
    if (!counter) return;

    const target = parseFloat(counter.dataset.target) || 0;
    const duration = 1000;
    const start = performance.now();

    function animate(now) {
        const progress = Math.min((now - start) / duration, 1);
        const value = target * (1 - Math.pow(1 - progress, 3));
        counter.textContent = Number.isInteger(target) ? Math.floor(value) : value.toFixed(2);

        if (progress < 1) {
            requestAnimationFrame(animate);
        } else {
            counter.textContent = Number.isInteger(target) ? target : target.toFixed(2);
        }
    }

    requestAnimationFrame(animate);
});
</script>[xyz-ips snippet="DashboardInfo-Return-Counter"]����1��|��DashboardInfo-Pending-Counter<?php
global $wpdb;

$table_name          = $wpdb->prefix . 'ac_jobs';
$job_type_column     = 'job_type';        // change if needed
$created_at_column   = 'created_at';      // change if needed
$delivery_order_type = 'PENDING';  // change if needed

$today = current_time('Y-m-d');

$total_today = (int) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COUNT(*)
         FROM {$table_name}
         WHERE {$job_type_column} = %s
         AND DATE({$created_at_column}) = %s",
        $delivery_order_type,
        $today
    )
);
?>

<div id="ac-delivery-counter" data-target="<?php echo esc_attr($total_today); ?>">0</div>

<style>
#ac-delivery-counter{
    font-size: 2rem;
    font-weight: 700;
    font-family: Roboto, sans-serif;
    color: #000000;
    line-height: 1;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const counter = document.getElementById('ac-delivery-counter');
    if (!counter) return;

    const target = parseInt(counter.dataset.target) || 0;
    const duration = 1000;
    const start = performance.now();

    function animate(now) {
        const progress = Math.min((now - start) / duration, 1);
        const value = Math.floor(target * (1 - Math.pow(1 - progress, 3)));
        counter.textContent = value;

        if (progress < 1) {
            requestAnimationFrame(animate);
        } else {
            counter.textContent = target;
        }
    }

    requestAnimationFrame(animate);
});
</script>[xyz-ips snippet="DashboardInfo-Pending-Counter"]����2����DashboardInfo-Customer-Counter<?php
global $wpdb;

$table_name          = $wpdb->prefix . 'ac_jobs';
$job_type_column     = 'job_type';
$created_at_column   = 'created_at';
$payload_column      = 'payload';
$delivery_order_type = 'DELIVERY_ORDER';

$today = current_time('Y-m-d');

$total_today = (int) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COUNT(DISTINCT JSON_UNQUOTE(JSON_EXTRACT({$payload_column}, '$.customerCode')))
         FROM {$table_name}
         WHERE {$job_type_column} = %s
         AND DATE({$created_at_column}) = %s
         AND JSON_UNQUOTE(JSON_EXTRACT({$payload_column}, '$.customerCode')) IS NOT NULL
         AND JSON_UNQUOTE(JSON_EXTRACT({$payload_column}, '$.customerCode')) != ''",
        $delivery_order_type,
        $today
    )
);
?>

<div id="ac-customers-served-counter" data-target="<?php echo esc_attr($total_today); ?>">0</div>

<style>
#ac-customers-served-counter{
    font-size: 2rem;
    font-weight: 700;
    font-family: Roboto, sans-serif;
    color: #000000;
    line-height: 1;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const counter = document.getElementById('ac-customers-served-counter');
    if (!counter) return;

    const target = parseInt(counter.dataset.target) || 0;
    const duration = 1000;
    const start = performance.now();

    function animate(now) {
        const progress = Math.min((now - start) / duration, 1);
        const value = Math.floor(target * (1 - Math.pow(1 - progress, 3)));
        counter.textContent = value;

        if (progress < 1) {
            requestAnimationFrame(animate);
        } else {
            counter.textContent = target;
        }
    }

    requestAnimationFrame(animate);
});
</script>[xyz-ips snippet="DashboardInfo-Customer-Counter"]����pp��cג��ן�����+�V=E�?+k�&!D
� infimumsupremum2�
���DashboardInfo-Customer-Counter<?php
global $wpdb;

$table_name          = $wpdb->prefix . 'ac_jobs';
$job_type_column     = 'job_type';
$created_at_column   = 'created_at';
$payload_column      = 'payload';
$delivery_order_type = 'DELIVERY_ORDER';

$today = current_time('Y-m-d');

$total_today = (int) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COUNT(DISTINCT JSON_UNQUOTE(JSON_EXTRACT({$payload_column}, '$.customerCode')))
         FROM {$table_name}
         WHERE {$job_type_column} = %s
         AND DATE({$created_at_column}) = %s
         AND JSON_UNQUOTE(JSON_EXTRACT({$payload_column}, '$.customerCode')) IS NOT NULL
         AND JSON_UNQUOTE(JSON_EXTRACT({$payload_column}, '$.customerCode')) != ''",
        $delivery_order_type,
        $today
    )
);
?>

<div id="ac-customers-served-counter" data-target="<?php echo esc_attr($total_today); ?>">0</div>

<style>
#ac-customers-served-counter{
    font-size: 2rem;
    font-weight: 700;
    font-family: Roboto, sans-serif;
    color: #000000;
    line-height: 1;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const counter = document.getElementById('ac-customers-served-counter');
    if (!counter) return;

    const target = parseInt(counter.dataset.target) || 0;
    const duration = 1000;
    const start = performance.now();

    function animate(now) {
        const progress = Math.min((now - start) / duration, 1);
        const value = Math.floor(target * (1 - Math.pow(1 - progress, 3)));
        counter.textContent = value;

        if (progress < 1) {
            requestAnimationFrame(animate);
        } else {
            counter.textContent = target;
        }
    }

    requestAnimationFrame(animate);
});
</script>[xyz-ips snippet="DashboardInfo-Customer-Counter"]����/͂!c��MenuInfo-CurrentUser-Mobile<?php
if (is_user_logged_in()) {
    $user = wp_get_current_user();

    $display_name = !empty($user->display_name) ? $user->display_name : $user->user_login;

    $initial = function_exists('mb_substr')
        ? mb_strtoupper(mb_substr($display_name, 0, 1))
        : strtoupper(substr($display_name, 0, 1));

    echo '<div style="display:inline-flex;align-items:center;justify-content:center;">';
        echo '<div style="width:36px;height:36px;min-width:36px;border-radius:50%;background:#0f6b43;color:#fff;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:700;line-height:1;font-family:Arial,sans-serif;">' . esc_html($initial) . '</div>';
    echo '</div>';
}
?>[xyz-ips snippet="MenuInfo-CurrentUser-Mobile"]����.'� ����DashboardInfo-Latest-Entry<?php
global $wpdb;

$jobs_table   = $wpdb->prefix . 'ac_jobs';
$ledger_table = $wpdb->prefix . 'ac_basket_ledger';

$job_type_column     = 'job_type';
$job_time_column     = 'created_at';
$job_type_value      = 'DELIVERY_ORDER';

$ledger_type_column  = 'txn_type';
$ledger_time_column  = 'created_at';
$ledger_type_value   = 'RETURN';

function ac_pick_value($row, $keys = array(), $default = '-') {
    foreach ($keys as $key) {
        if (isset($row[$key]) && $row[$key] !== '' && $row[$key] !== null) {
            return $row[$key];
        }
    }
    return $default;
}

function ac_pick_json_value($json, $keys = array(), $default = '-') {
    if (empty($json)) return $default;

    $data = json_decode($json, true);
    if (!is_array($data)) return $default;

    foreach ($keys as $key) {
        if (isset($data[$key]) && $data[$key] !== '' && $data[$key] !== null) {
            return $data[$key];
        }
    }

    return $default;
}

function ac_format_time_only($datetime) {
    if (empty($datetime) || $datetime === '-') return '-';

    $timestamp = strtotime($datetime);
    if (!$timestamp) return $datetime;

    return wp_date('g:i A', $timestamp);
}

$job_rows = $wpdb->get_results(
    $wpdb->prepare(
        "SELECT *
         FROM {$jobs_table}
         WHERE {$job_type_column} = %s
         ORDER BY {$job_time_column} DESC
         LIMIT 20",
        $job_type_value
    ),
    ARRAY_A
);

$ledger_rows = $wpdb->get_results(
    $wpdb->prepare(
        "SELECT *
         FROM {$ledger_table}
         WHERE {$ledger_type_column} = %s
         ORDER BY {$ledger_time_column} DESC
         LIMIT 20",
        $ledger_type_value
    ),
    ARRAY_A
);

$combined = array();

if (!empty($job_rows)) {
    foreach ($job_rows as $row) {
        $payload  = isset($row['payload']) ? $row['payload'] : '';
        $result   = isset($row['result']) ? $row['result'] : '';
        $time_raw = ac_pick_value($row, array('created_at', 'created_time', 'CreatedAt'));

        $combined[] = array(
            'time_raw' => $time_raw,
            'time'     => ac_format_time_only($time_raw),
            'type'     => 'Sending',
            'customer' => ac_pick_json_value($payload, array('customerName', 'debtorName', 'DebtorName'), '-'),
            'ref'      => ac_pick_json_value($result, array('docNo'), '-'),
        );
    }
}

if (!empty($ledger_rows)) {
    foreach ($ledger_rows as $row) {
        $time_raw = ac_pick_value($row, array('created_at', 'created_time', 'txn_time', 'txn_date'));

        $combined[] = array(
            'time_raw' => $time_raw,
            'time'     => ac_format_time_only($time_raw),
            'type'     => 'Return',
            'customer' => ac_pick_value($row, array('customer_name', 'debtor_name', 'customer', 'debtor', 'customerCode', 'debtorCode'), '-'),
            'ref'      => ac_pick_value($row, array('reference_no', 'ref_no', 'doc_no', 'docno', 'reference', 'txn_ref'), '-'),
        );
    }
}

usort($combined, function($a, $b) {
    return strtotime($b['time_raw']) <=> strtotime($a['time_raw']);
});

$combined = array_slice($combined, 0, 5);
?>

<div class="ac-simple-log-wrap">
    <table class="ac-simple-log-table">
        <thead>
            <tr>
                <th class="col-time">Time</th>
                <th class="col-type">Type</th>
                <th class="col-customer">Customer</th>
                <th class="col-ref">Reference Number</th>
            </tr>
        </thead>
        <tbody>
            <?php if (!empty($combined)) : ?>
                <?php foreach ($combined as $item) : ?>
                    <tr>
                        <td class="col-time"><?php echo esc_html($item['time']); ?></td>
                        <td class="col-type"><?php echo esc_html($item['type']); ?></td>
                        <td class="col-customer"><?php echo esc_html($item['customer']); ?></td>
                        <td class="col-ref"><?php echo esc_html($item['ref']); ?></td>
                    </tr>
                <?php endforeach; ?>
            <?php else : ?>
                <tr>
                    <td colspan="4">No records found.</td>
                </tr>
            <?php endif; ?>
        </tbody>
    </table>
</div>

<style>
.ac-simple-log-wrap{
    width:100%;
    margin:0;
    overflow-x:auto;
    -webkit-overflow-scrolling:touch;
}

.ac-simple-log-table{
    width:100%;
    min-width:38rem;
    border-collapse:collapse;
    border-spacing:0;
    margin:0;
    font-family:Roboto, sans-serif;
    font-size:1rem;
    background:#FFFFFF;
}

.ac-simple-log-table th,
.ac-simple-log-table td{
    padding:0.45rem 0.4rem;
    border-bottom:1px solid #e5e7eb;
    text-align:left;
    vertical-align:top;
    color:#374151;
}

.ac-simple-log-table th{
    font-weight:700;
    background:#f8f9fa;
    color:#111827;
    white-space:nowrap;
}

.ac-simple-log-table .col-time{
    width:5.5rem;
    white-space:nowrap;
}

.ac-simple-log-table .col-type{
    width:5.5rem;
    white-space:nowrap;
}

.ac-simple-log-table .col-customer{
    min-width:10rem;
}

.ac-simple-log-table .col-ref{
    min-width:9rem;
    white-space:nowrap;
}

/* Tablet */
@media (max-width: 64rem){
    .ac-simple-log-table{
        min-width:34rem;
        font-size:0.92rem;
    }

    .ac-simple-log-table th,
    .ac-simple-log-table td{
        padding:0.38rem 0.32rem;
    }
}

/* Mobile */
@media (max-width: 48rem){
    .ac-simple-log-wrap{
        overflow-x:auto;
    }

    .ac-simple-log-table{
        min-width:30rem;
        font-size:0.84rem;
    }

    .ac-simple-log-table th,
    .ac-simple-log-table td{
        padding:0.32rem 0.28rem;
    }

    .ac-simple-log-table .col-time{
        width:4.8rem;
    }

    .ac-simple-log-table .col-type{
        width:4.8rem;
    }

    .ac-simple-log-table .col-customer{
        min-width:8rem;
    }

    .ac-simple-log-table .col-ref{
        min-width:8rem;
    }
}
</style>[xyz-ips snippet="DashboardInfo-Latest-Entry"]�����
(v� 0n(� 8%�@���
H�$� P�%�Xŀ �BasketSummary-All?U&)[xyz-ips snippet="BasketSummary-All"]����"�(`�@'�h��&�Delivery-Order-Edit?H&F�[xyz-ips snippet="Delivery-Order-Edit"]����"��pӀ#�Login-Redirect<?php
if (is_user_logged_in()) {

    $current_user = wp_get_current_user();

    // Allow User01 to stay on this page
    if ($current_user->user_login !== 'User01') {
        wp_safe_redirect(home_url('/'));
        exit;
    }
}
?>[xyz-ips snippet="Login-Redirect"]����+�x���DOcreate-staff-workflow?N&`
[xyz-ips snippet="DOcreate-staff-workflow"]����-�����$�Delivery-Order-Staff-List?D&�J[xyz-ips snippet="Delivery-Order-Staff-List"]����6�"����"�basketdo-assigned-driver-dashboard?O&��[xyz-ips snippet="basketdo-assigned-driver-dashboard"]����,������page-view-delivery-order?W&�8[xyz-ips snippet="page-view-delivery-order"]����p&�c+�V=Z�D�����+���E�?'݀#|0	�!cinfimumsupremum(� �} $R�
   �0��(#���MenuInfo-CurrentUser-Desktop<?php
if (is_user_logged_in()) {
    $user = wp_get_current_user();

    $display_name = !empty($user->display_name) ? $user->display_name : $user->user_login;

    $initial = function_exists('mb_substr')
        ? mb_strtoupper(mb_substr($display_name, 0, 1))
        : strtoupper(substr($display_name, 0, 1));

    $role = !empty($user->roles) ? $user->roles[0] : '';

    $role_labels = array(
        'administrator'   => 'Administrator',
        'editor'          => 'Editor',
        'author'          => 'Author',
        'contributor'     => 'Contributor',
        'subscriber'      => 'Operations Staff', // change if needed
        'operations_staff'=> 'Operations Staff',
        'delivery_driver' => 'Delivery Driver',
        'staff'           => 'Staff',
    );

    $role_text = isset($role_labels[$role])
        ? $role_labels[$role]
        : ucwords(str_replace(array('-', '_'), ' ', $role));

    echo '<div style="display:flex;align-items:center;gap:12px;padding:10px 14px;background:#fff;font-family:Arial,sans-serif;">';
        echo '<div style="width:36px;height:36px;min-width:36px;border-radius:50%;background:#0f6b43;color:#fff;display:flex;align-items:center;justify-content:center;font-size:16px;font-weight:700;line-height:1;">' . esc_html($initial) . '</div>';
        echo '<div style="display:flex;flex-direction:column;line-height:1.2;">';
            echo '<div style="font-size:13px;font-weight:600;color:#1f2937;">' . esc_html($display_name) . '</div>';
            echo '<div style="font-size:12px;color:#7a7a7a;margin-top:2px;">' . esc_html($role_text) . '</div>';
        echo '</div>';
    echo '</div>';
}
?>[xyz-ips snippet="MenuInfo-CurrentUser-Desktop"]����(^�0&��8���DashboardInfo-Good<?php
$hour = (int) current_time('G');

if ($hour >= 5 && $hour < 12) {
    $greeting = 'Good Morning';
} elseif ($hour >= 12 && $hour < 17) {
    $greeting = 'Good Afternoon';
} else {
    $greeting = 'Good Evening';
}

$user = wp_get_current_user();
$name = !empty($user->display_name) ? $user->display_name : $user->user_login;
?>

<div class="ac-greeting"><?php echo esc_html($greeting . ', ' . $name); ?></div>

<style>
.ac-greeting{
    color:#0B4A2D;
    font-family:Roboto, Arial, sans-serif;
    font-weight:700;
    font-size:1.5rem;
}

@media (max-width: 767px){
    .ac-greeting{
        font-size:1.25rem;
    }
}
</style>[xyz-ips snippet="DashboardInfo-Good"]����,	�@y��DashboardInfo-DO-Counter<?php
global $wpdb;

$table_name          = $wpdb->prefix . 'ac_jobs';
$job_type_column     = 'job_type';        // change if needed
$created_at_column   = 'created_at';      // change if needed
$delivery_order_type = 'DELIVERY_ORDER';  // change if needed

$today = current_time('Y-m-d');

$total_today = (int) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COUNT(*)
         FROM {$table_name}
         WHERE {$job_type_column} = %s
         AND DATE({$created_at_column}) = %s",
        $delivery_order_type,
        $today
    )
);
?>

<div id="ac-delivery-counter" data-target="<?php echo esc_attr($total_today); ?>">0</div>

<style>
#ac-delivery-counter{
    font-size: 2rem;
    font-weight: 700;
    font-family: Roboto, sans-serif;
    color: #000000;
    line-height: 1;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const counter = document.getElementById('ac-delivery-counter');
    if (!counter) return;

    const target = parseInt(counter.dataset.target) || 0;
    const duration = 1000;
    const start = performance.now();

    function animate(now) {
        const progress = Math.min((now - start) / duration, 1);
        const value = Math.floor(target * (1 - Math.pow(1 - progress, 3)));
        counter.textContent = value;

        if (progress < 1) {
            requestAnimationFrame(animate);
        } else {
            counter.textContent = target;
        }
    }

    requestAnimationFrame(animate);
});
</script>[xyz-ips snippet="DashboardInfo-DO-Counter"]����0Y�H��DashboardInfo-Return-Counter<?php
global $wpdb;

$table_name        = $wpdb->prefix . 'ac_basket_ledger';
$txn_type_column   = 'txn_type';
$created_at_column = 'created_at'; // change if needed
$qty_column        = 'qty'; // change if needed

$today = current_time('Y-m-d');

$total_today = (float) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COALESCE(SUM({$qty_column}), 0)
         FROM {$table_name}
         WHERE {$txn_type_column} = %s
         AND DATE({$created_at_column}) = %s",
        'RETURN',
        $today
    )
);
?>

<div id="ac-basket-return-counter" data-target="<?php echo esc_attr($total_today); ?>">0</div>

<style>
#ac-basket-return-counter{
    font-size: 2rem;
    font-weight: 700;
    font-family: Roboto, sans-serif;
    color: #000000;
    line-height: 1;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function () {
    const counter = document.getElementById('ac-basket-return-counter');
    if (!counter) return;

    const target = parseFloat(counter.dataset.target) || 0;
    const duration = 1000;
    const start = performance.now();

    function animate(now) {
        const progress = Math.min((now - start) / duration, 1);
        const value = target * (1 - Math.pow(1 - progress, 3));
        counter.textContent = Number.isInteger(target) ? Math.floor(value) : value.toFixed(2);

        if (progress < 1) {
            requestAnimationFrame(animate);
        } else {
            counter.textContent = Number.isInteger(target) ? target : target.toFixed(2);
        }
    }

    requestAnimationFrame(animate);
});
</script>[xyz-ips snippet="DashboardInfo-Return-Counter"]����1� P��	Xf��DO-Create?&�
[xyz-ips snippet="DO-Create"]�����`.��DO-CREATE-2?&��[xyz-ips snippet="DO-CREATE-2"]����$� ht#� p�@"� xp!�
 ���(��|��BasketReturn-desktop?
&=K[xyz-ips snippet="BasketReturn-desktop"]����'���Y��BasketReturn-mobile?&;A[xyz-ips snippet="BasketReturn-mobile"]����)� ��3(����5��MenuInfo-CurrentPage<?php
if (is_front_page() || is_home()) {
    $page_name = 'Dashboard';
} elseif (is_page('create-delivery-order')) {
    $page_name = 'Create / Return';
} else {
    $page_name = get_the_title();

    if (empty($page_name)) {
        $page_name = wp_title('', false);
    }
}

echo '<div style="color:#ffffff;font-family:Roboto, Arial, sans-serif;font-weight:700;font-size:1rem;">' . esc_html($page_name) . '</div>';
?>[xyz-ips snippet="MenuInfo-CurrentPage"]����p$�c+���$�#U"��������܁�]
??�<?php
/**
 * DELIVERY ORDER CREATE PAGE - DESKTOP ONLY
 * - Visuals rebuilt for desktop layout only
 * - "Debtor" wording changed to "Customer"
 * - CSS classes / JS IDs renamed with desktop prefix to avoid conflict
 * - Scoped styles only inside this desktop root
 */

if (!defined('ABSPATH')) exit;

if (!function_exists('ac_used_is_mobile_or_tablet')) {
    function ac_used_is_mobile_or_tablet() {
        if (wp_is_mobile()) return true;

        $ua = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');
        foreach (array('ipad', 'tablet', 'kindle', 'silk', 'playbook') as $needle) {
            if ($needle !== '' && strpos($ua, $needle) !== false) return true;
        }

        return false;
    }
}

if (ac_used_is_mobile_or_tablet()) {
    return;
}

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">
        Please log in to create Delivery Order.
    </div>';
    return;
}

$rest_nonce = wp_create_nonce('wp_rest');

$edit_base = home_url('/index.php/delivery-order-edit/');

$REST_JOB_POST = rest_url('ac/v1/job');
$REST_JOB_BASE = rest_url('ac/v1/job/');

$ajax_url             = admin_url('admin-ajax.php');
$customer_nonce       = wp_create_nonce('ac_cs_debtor_search'); // backend action stays the same
$item_suggest_nonce   = wp_create_nonce('ac_itemcode_suggest');
$default_location     = 'HQ';
?>

<div id="ac-do-desktop-root"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-edit-base="<?php echo esc_attr($edit_base); ?>"
     data-rest-job-post="<?php echo esc_attr($REST_JOB_POST); ?>"
     data-rest-job-base="<?php echo esc_attr($REST_JOB_BASE); ?>"
     data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
     data-customer-nonce="<?php echo esc_attr($customer_nonce); ?>"
     data-item-suggest-nonce="<?php echo esc_attr($item_suggest_nonce); ?>">

    <input type="hidden" id="acdo_desktop_customer_name" value="" />
    <input type="hidden" id="acdo_desktop_sales_agent" value="" />
    <input type="hidden" id="acdo_desktop_location" value="<?php echo esc_attr($default_location); ?>" />

    <div class="acdo-desktop-shell">

        <section class="acdo-desktop-card acdo-desktop-order-card">
            <div class="acdo-desktop-card-head">
                <h2>Order Information</h2>
            </div>

            <div class="acdo-desktop-card-body acdo-desktop-order-layout">
                <div class="acdo-desktop-order-fields">
                    <div class="acdo-desktop-field">
                        <label class="acdo-desktop-label" for="acdo_desktop_date">Date</label>
                        <input id="acdo_desktop_date" type="date" class="acdo-desktop-input" />
                    </div>

                    <div class="acdo-desktop-field">
                        <label class="acdo-desktop-label" for="acdo_desktop_customer_input">Customer</label>
                        <div class="acdo-desktop-search-wrap"
                             id="acdo_desktop_customer_wrapper"
                             data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
                             data-nonce="<?php echo esc_attr($customer_nonce); ?>">
                            <input type="text"
                                   id="acdo_desktop_customer_input"
                                   class="acdo-desktop-input"
                                   placeholder="Search customer name..."
                                   autocomplete="off" />
                            <input type="hidden" id="acdo_desktop_customer_code" value="" />
                            <div class="acdo-desktop-dropdown" id="acdo_desktop_customer_dropdown"></div>
                        </div>
                    </div>
                </div>

                <div class="acdo-desktop-stats">
                    <div class="acdo-desktop-stat-box">
                        <div class="acdo-desktop-stat-label">Lines</div>
                        <div class="acdo-desktop-stat-value" id="acdo_desktop_sum_lines">0</div>
                    </div>

                    <div class="acdo-desktop-stat-box">
                        <div class="acdo-desktop-stat-label">Total Qty</div>
                        <div class="acdo-desktop-stat-value" id="acdo_desktop_sum_qty">0</div>
                    </div>

                    <div class="acdo-desktop-stat-box">
                        <div class="acdo-desktop-stat-label">Total KG</div>
                        <div class="acdo-desktop-stat-value" id="acdo_desktop_sum_kg">0.00</div>
                    </div>
                </div>
            </div>
        </section>

        <section class="acdo-desktop-card acdo-desktop-add-card">
            <div class="acdo-desktop-card-head">
                <h2>Add Item</h2>
            </div>

            <div class="acdo-desktop-card-body acdo-desktop-add-layout">
                <div class="acdo-desktop-add-left">
                    <div class="acdo-desktop-inline-row">
                        <div class="acdo-desktop-inline-grow">
                            <label class="acdo-desktop-label" for="acdo_desktop_item_name">Item Name</label>
                            <div class="acdo-desktop-search-wrap">
                                <input id="acdo_desktop_item_name" type="text" class="acdo-desktop-input" placeholder="Search item name..." autocomplete="off">
                                <input id="acdo_desktop_item_code" type="hidden" value="">
                                <input id="acdo_desktop_item_display" type="hidden" value="">
                                <div class="acdo-desktop-dropdown acdo-desktop-item-dropdown" id="acdo_desktop_item_dropdown"></div>
                            </div>
                        </div>

                        <div class="acdo-desktop-add-btn-wrap">
                            <button id="acdo_desktop_add_line" class="acdo-desktop-primary-btn" type="button">Add Item</button>
                        </div>
                    </div>

                    <div class="acdo-desktop-form-grid">
                        <div class="acdo-desktop-field">
                            <label class="acdo-desktop-label">Type</label>
                            <div class="acdo-desktop-type-toggle" id="acdo_desktop_pack_toggle">
                                <button type="button" class="acdo-desktop-type-btn is-active" data-pack-type="CARTON">Carton</button>
                                <button type="button" class="acdo-desktop-type-btn" data-pack-type="BASKET">Basket</button>
                            </div>
                            <select id="acdo_desktop_pack_type" style="display:none;">
                                <option value="CARTON" selected>Carton</option>
                                <option value="BASKET">Basket</option>
                            </select>
                        </div>

                        <div class="acdo-desktop-field">
                            <label class="acdo-desktop-label" for="acdo_desktop_qty">Qty</label>
                            <input id="acdo_desktop_qty" type="number" min="0" step="1" value="1" class="acdo-desktop-input" />
                        </div>

                        <div class="acdo-desktop-field">
                            <label class="acdo-desktop-label" for="acdo_desktop_kg">Weight (KG)</label>
                            <input id="acdo_desktop_kg" type="number" min="0" step="0.01" value="0" class="acdo-desktop-input" />
                        </div>
                    </div>

                    <input id="acdo_desktop_total" type="hidden" value="0" />

                    <div class="acdo-desktop-preview" id="acdo_desktop_preview" style="display:none;"></div>
                </div>

                <div class="acdo-desktop-add-right">
                    <div class="acdo-desktop-mini-title">Current Items</div>

                    <div class="acdo-desktop-mini-table">
                        <div class="acdo-desktop-mini-head">
                            <span>Item</span>
                            <span>Type</span>
                            <span>Qty</span>
                            <span>KG</span>
                            <span>Total</span>
                        </div>

                        <div class="acdo-desktop-mini-body" id="acdo_desktop_mini_lines">
                            <div class="acdo-desktop-empty-row">No items added yet</div>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <section class="acdo-desktop-card acdo-desktop-lines-card">
            <div class="acdo-desktop-card-head acdo-desktop-card-head-space">
                <h2>Current Items</h2>
                <button class="acdo-desktop-primary-btn acdo-desktop-save-btn" id="acdo_desktop_submit" type="button">Save Delivery Order</button>
            </div>

            <div class="acdo-desktop-card-body acdo-desktop-lines-wrap">
                <div class="acdo-desktop-table-head">
                    <span>Item</span>
                    <span>Type</span>
                    <span>Qty</span>
                    <span>KG</span>
                    <span>Total</span>
                    <span>Action</span>
                </div>

                <div class="acdo-desktop-table-body" id="acdo_desktop_lines">
                    <div class="acdo-desktop-empty-row">No items added yet</div>
                </div>
            </div>
        </section>
    </div>
</div>

<style>
    #ac-do-desktop-root{
        --acdo-desktop-bg:#f6f7f8;
        --acdo-desktop-panel:#ffffff;
        --acdo-desktop-border:#dde2e6;
        --acdo-desktop-border-strong:#cfd6dc;
        --acdo-desktop-text:#202327;
        --acdo-desktop-muted:#6e7781;
        --acdo-desktop-green:#134e3a;
        --acdo-desktop-green-2:#1b6b4f;
        --acdo-desktop-green-soft:#edf5f1;
        --acdo-desktop-danger:#d9485f;
        --acdo-desktop-shadow:0 0.5rem 1rem rgba(16,24,40,.05);
        font-family:Roboto,"Segoe UI",Arial,sans-serif;
        color:var(--acdo-desktop-text);
        font-size:1rem;
    }

    #ac-do-desktop-root *{
        box-sizing:border-box;
    }

    #ac-do-desktop-root .acdo-desktop-shell{
        width:100%;
        max-width:100%;
        display:grid;
        gap:0.75rem;
        padding:0;
        background:transparent;
    }

    #ac-do-desktop-root .acdo-desktop-card{
        background:var(--acdo-desktop-panel);
        border:0.0625rem solid var(--acdo-desktop-border);
        border-radius:0.875rem;
        box-shadow:var(--acdo-desktop-shadow);
        overflow:hidden;
    }

    #ac-do-desktop-root .acdo-desktop-card-head{
        display:flex;
        align-items:center;
        justify-content:space-between;
        gap:1rem;
        padding:0.75rem 1rem;
        border-bottom:0.0625rem solid #eceff2;
    }

    #ac-do-desktop-root .acdo-desktop-card-head-space{
        justify-content:space-between;
    }

    #ac-do-desktop-root .acdo-desktop-card-head h2{
        margin:0;
        font-size:1rem;
        line-height:1.2;
        font-weight:700;
        color:var(--acdo-desktop-text);
    }

    #ac-do-desktop-root .acdo-desktop-card-body{
        padding:1rem;
    }

    #ac-do-desktop-root .acdo-desktop-order-layout{
        display:grid;
        grid-template-columns:minmax(0,1.45fr) minmax(0,0.95fr);
        gap:0.75rem;
        align-items:end;
    }

    #ac-do-desktop-root .acdo-desktop-order-fields{
        display:grid;
        grid-template-columns:15rem minmax(0,1fr);
        gap:0.75rem;
        align-items:end;
    }

    #ac-do-desktop-root .acdo-desktop-stats{
        display:grid;
        grid-template-columns:repeat(3,minmax(0,1fr));
        gap:0;
        border:0.0625rem solid var(--acdo-desktop-border);
        border-radius:0.75rem;
        overflow:hidden;
        background:#fff;
    }

    #ac-do-desktop-root .acdo-desktop-stat-box{
        padding:0.6rem 0.75rem;
        display:flex;
        flex-direction:column;
        justify-content:center;
        background:#fff;
    }

    #ac-do-desktop-root .acdo-desktop-stat-box + .acdo-desktop-stat-box{
        border-left:0.0625rem solid var(--acdo-desktop-border);
    }

    #ac-do-desktop-root .acdo-desktop-stat-label{
        font-size:0.75rem;
        color:var(--acdo-desktop-muted);
        margin-bottom:0.2rem;
        font-weight:500;
    }

    #ac-do-desktop-root .acdo-desktop-stat-value{
        font-size:1rem;
        font-weight:700;
        color:var(--acdo-desktop-text);
        line-height:1.2;
    }

    #ac-do-desktop-root .acdo-desktop-label{
        display:block;
        margin:0 0 0.3rem;
        font-size:1rem;
        color:#4c5661;
        font-weight:500;
    }

    #ac-do-desktop-root .acdo-desktop-input{
        width:100%;
        height:2.5rem;
        border:0.0625rem solid var(--acdo-desktop-border-strong);
        border-radius:0.625rem;
        padding:0 0.75rem;
        font-size:1rem;
        color:var(--acdo-desktop-text);
        background:#fff;
        outline:none;
    }

    #ac-do-desktop-root .acdo-desktop-input:focus{
        border-color:var(--acdo-desktop-green-2);
        box-shadow:0 0 0 0.15rem rgba(27,107,79,.10);
    }

    #ac-do-desktop-root .acdo-desktop-search-wrap{
        position:relative;
    }

    #ac-do-desktop-root .acdo-desktop-dropdown{
        position:absolute;
        left:0;
        right:0;
        top:calc(100% + 0.25rem);
        background:#fff;
        border:0.0625rem solid var(--acdo-desktop-border-strong);
        border-radius:0.75rem;
        box-shadow:0 0.5rem 1.5rem rgba(15,23,42,.10);
        z-index:50;
        max-height:18rem;
        overflow:auto;
        display:none;
    }

    #ac-do-desktop-root .acdo-desktop-dropdown.is-open{
        display:block;
    }

    #ac-do-desktop-root .acdo-desktop-dropdown-item{
        padding:0.6rem 0.8rem;
        cursor:pointer;
        border-bottom:0.0625rem solid #eef2f5;
    }

    #ac-do-desktop-root .acdo-desktop-dropdown-item:last-child{
        border-bottom:none;
    }

    #ac-do-desktop-root .acdo-desktop-dropdown-item:hover,
    #ac-do-desktop-root .acdo-desktop-dropdown-item.is-active{
        background:#f7faf8;
    }

    #ac-do-desktop-root .acdo-desktop-dropdown-main{
        display:block;
        font-size:1rem;
        font-weight:600;
        color:var(--acdo-desktop-text);
        line-height:1.2;
    }

    #ac-do-desktop-root .acdo-desktop-dropdown-sub{
        display:block;
        margin-top:0.2rem;
        font-size:0.8rem;
        color:var(--acdo-desktop-muted);
        line-height:1.2;
    }

    #ac-do-desktop-root .acdo-desktop-add-layout{
        display:grid;
        grid-template-columns:minmax(0,1fr) minmax(0,0.9fr);
        gap:1rem;
        align-items:start;
    }

    #ac-do-desktop-root .acdo-desktop-inline-row{
        display:grid;
        grid-template-columns:minmax(0,1fr) auto;
        gap:0.5rem;
        align-items:end;
        margin-bottom:0.75rem;
    }

    #ac-do-desktop-root .acdo-desktop-inline-grow{
        min-width:0;
    }

    #ac-do-desktop-root .acdo-desktop-add-btn-wrap{
        width:7rem;
    }

    #ac-do-desktop-root .acdo-desktop-primary-btn{
        width:100%;
        height:2.5rem;
        border:none;
        border-radius:0.625rem;
        background:linear-gradient(180deg,var(--acdo-desktop-green-2),var(--acdo-desktop-green));
        color:#fff;
        font-size:0.9rem;
        font-weight:700;
        cursor:pointer;
        box-shadow:none;
    }

    #ac-do-desktop-root .acdo-desktop-primary-btn:hover{
        filter:brightness(.98);
    }

    #ac-do-desktop-root .acdo-desktop-primary-btn:disabled{
        opacity:.75;
        cursor:not-allowed;܁�]���@��������_��
??�B<?php
/**
 * BasketDO One-Page Driver Dashboard
 * Paste into XYZ Insert PHP Code Snippet PHP code box.
 * Paste as-is. Do not create a shortcode inside this code.
 */

if (!defined('ABSPATH')) {
    exit;
}

if (!is_user_logged_in()) {
    echo '<div style="max-width:420px;margin:40px auto;padding:22px;border:1px solid #fecaca;border-radius:18px;background:#fff1f2;color:#991b1b;font-family:Arial,sans-serif;text-align:center;">Please log in to continue.</div>';
    return;
}

global $wpdb;

// Hide WP admin bar visually for driver page.
add_filter('show_admin_bar', '__return_false');

if (!function_exists('bdo_driver_current_page_url')) {
    function bdo_driver_current_page_url() {
        $scheme = is_ssl() ? 'https://' : 'http://';
        $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : '';
        $uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
        $url = $scheme . $host . $uri;
        return remove_query_arg([
            'bdo_msg',
            'bdo_err',
            'bdo_job_id',
            'bdo_br_ledger_id',
            'bdo_tab',
        ], $url);
    }
}

if (!function_exists('bdo_driver_table_exists')) {
    function bdo_driver_table_exists($table_name) {
        global $wpdb;
        return $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table_name)) === $table_name;
    }
}

if (!function_exists('bdo_driver_table_columns')) {
    function bdo_driver_table_columns($table_name) {
        global $wpdb;
        static $cache = [];

        if (isset($cache[$table_name])) {
            return $cache[$table_name];
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table_name);
        $cols = $wpdb->get_col("SHOW COLUMNS FROM `{$safe_table}`", 0);
        $cache[$table_name] = is_array($cols) ? array_flip($cols) : [];

        return $cache[$table_name];
    }
}

if (!function_exists('bdo_driver_pick')) {
    function bdo_driver_pick($arr, $keys, $fallback = '') {
        if (!is_array($arr)) {
            return $fallback;
        }

        foreach ($keys as $key) {
            if (isset($arr[$key]) && $arr[$key] !== '' && $arr[$key] !== null) {
                return $arr[$key];
            }
        }

        return $fallback;
    }
}

if (!function_exists('bdo_driver_json')) {
    function bdo_driver_json($value) {
        if (is_array($value)) {
            return $value;
        }

        if (!is_string($value) || trim($value) === '') {
            return [];
        }

        $decoded = json_decode($value, true);
        return is_array($decoded) ? $decoded : [];
    }
}

if (!function_exists('bdo_driver_sum_lines')) {
    function bdo_driver_sum_lines($payload) {
        $summary = [
            'items'   => 0,
            'baskets' => 0,
            'cartons' => 0,
            'kg'      => 0,
            'lines'   => [],
        ];

        if (!is_array($payload)) {
            return $summary;
        }

        $lines = [];
        if (isset($payload['lines']) && is_array($payload['lines'])) {
            $lines = $payload['lines'];
        } elseif (isset($payload['Lines']) && is_array($payload['Lines'])) {
            $lines = $payload['Lines'];
        }

        foreach ($lines as $line) {
            if (!is_array($line)) {
                continue;
            }

            $pack_type = strtoupper(trim((string) bdo_driver_pick($line, ['packType', 'PackType'], '')));
            $qty       = (float) bdo_driver_pick($line, ['qty', 'Qty', 'quantity', 'Quantity'], 0);
            $unit_qty  = (float) bdo_driver_pick($line, ['unitQty', 'UnitQty'], 0);
            $basket    = (float) bdo_driver_pick($line, ['basketQty', 'basket_qty', 'BasketQty', 'UDF_BASKET'], 0);
            $carton    = (float) bdo_driver_pick($line, ['cartonQty', 'carton_qty', 'CartonQty', 'UDF_CARTON'], 0);
            $total_kg  = (float) bdo_driver_pick($line, ['totalKg', 'TotalKg', 'totalKG', 'qty', 'Qty'], 0);

            if ($basket <= 0 && $pack_type === 'BASKET') {
                $basket = $unit_qty > 0 ? $unit_qty : $qty;
            }

            if ($carton <= 0 && $pack_type === 'CARTON') {
                $carton = $unit_qty > 0 ? $unit_qty : $qty;
            }

            $item_code = (string) bdo_driver_pick($line, ['itemCode', 'ItemCode'], '');
            $item_name = (string) bdo_driver_pick($line, ['itemName', 'ItemName', 'description', 'Description', 'itemDesc'], $item_code);

            $summary['items']++;
            $summary['baskets'] += $basket;
            $summary['cartons'] += $carton;
            $summary['kg'] += $total_kg;
            $summary['lines'][] = [
                'itemCode' => $item_code,
                'itemName' => $item_name,
                'packType' => $pack_type ?: '-',
                'basket'   => (int) round($basket),
                'carton'   => (int) round($carton),
                'kg'       => (int) round($total_kg),
            ];
        }

        $summary['baskets'] = (int) round($summary['baskets']);
        $summary['cartons'] = (int) round($summary['cartons']);
        $summary['kg']      = (int) round($summary['kg']);

        return $summary;
    }
}

// ============================================================
// FAST VERSION: No Media Library, no server resize, 4MB limit
// ============================================================
if (!function_exists('bdo_driver_upload_image')) {
    function bdo_driver_upload_image($field_name) {
        if (empty($_FILES[$field_name]) || empty($_FILES[$field_name]['name'])) {
            return new WP_Error('missing_file', 'Photo proof is required.');
        }

        if (!empty($_FILES[$field_name]['error'])) {
            return new WP_Error('upload_error', 'Upload failed. Error code: ' . (int) $_FILES[$field_name]['error']);
        }

        require_once ABSPATH . 'wp-admin/includes/file.php';

        $allowed_mimes = [
            'jpg|jpeg|jpe' => 'image/jpeg',
            'png'          => 'image/png',
            'webp'         => 'image/webp',
        ];

        // Safety limit after browser compression.
        // 4MB should be more than enough for POD proof.
        $max_size = 4 * 1024 * 1024;
        if (!empty($_FILES[$field_name]['size']) && (int) $_FILES[$field_name]['size'] > $max_size) {
            return new WP_Error('file_too_large', 'Photo is still too large. Please retake or choose a smaller photo.');
        }

        $uploaded = wp_handle_upload($_FILES[$field_name], [
            'test_form' => false,
            'mimes'     => $allowed_mimes,
        ]);

        if (isset($uploaded['error'])) {
            return new WP_Error('upload_error', $uploaded['error']);
        }

        $file_path = $uploaded['file'];
        $file_url  = $uploaded['url'];
        $mime_type = $uploaded['type'];

        /*
         * FAST MODE:
         * Do not insert into WordPress Media Library.
         * Do not generate attachment metadata.
         * Do not resize again on server.
         *
         * We only need image_url/image_path for POD receipt.
         */
        return [
            'attachment_id' => 0,
            'image_url'     => $file_url,
            'image_path'    => $file_path,
            'mime_type'     => $mime_type,
            'file_size'     => file_exists($file_path) ? (int) filesize($file_path) : 0,
        ];
    }
}
// ============================================================

if (!function_exists('bdo_driver_insert_proof')) {
    function bdo_driver_insert_proof($data) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_do_proof_images';
        if (!bdo_driver_table_exists($table)) {
            return false;
        }

        $cols = bdo_driver_table_columns($table);

        $base = [
            'job_id'           => isset($data['job_id']) ? (int) $data['job_id'] : 0,
            'doc_no'           => isset($data['doc_no']) ? (string) $data['doc_no'] : '',
            'doc_key'          => isset($data['doc_key']) ? (int) $data['doc_key'] : 0,
            'receipt_token_id' => isset($data['receipt_token_id']) ? (int) $data['receipt_token_id'] : null,
            'proof_type'       => isset($data['proof_type']) ? (string) $data['proof_type'] : 'DELIVERY_PROOF',
            'attachment_id'    => isset($data['attachment_id']) ? (int) $data['attachment_id'] : 0,
            'image_url'        => isset($data['image_url']) ? (string) $data['image_url'] : '',
            'image_path'       => isset($data['image_path']) ? (string) $data['image_path'] : '',
            'mime_type'        => isset($data['mime_type']) ? (string) $data['mime_type'] : '',
            'file_size'        => isset($data['file_size']) ? (int) $data['file_size'] : 0,
            'captured_by'      => get_current_user_id(),
            'captured_at'      => current_time('mysql'),
            'created_at'       => current_time('mysql'),
        ];

        $insert = [];
        foreach ($base as $key => $value) {
            if (isset($cols[$key])) {
                $insert[$key] = $value;
            }
        }

        if (empty($insert)) {
            return false;
        }

        return $wpdb->insert($table, $insert) ? (int) $wpdb->insert_id : false;
    }
}


if (!function_exists('bdo_driver_get_delivery_proof_image')) {
    function bdo_driver_get_delivery_proof_image($job_id) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_do_proof_images';
        if (!$job_id || !bdo_driver_table_exists($table)) {
            return '';
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $cols = bdo_driver_table_columns($table);

        $where = "job_id = %d";
        $args = [(int) $job_id];

        if (isset($cols['proof_type'])) {
            $where .= " AND proof_type = %s";
            $args[] = 'DELIVERY_PROOF';
        }

        if (isset($cols['deleted_at'])) {
            $where .= " AND deleted_at IS NULL";
        }

        $url = $wpdb->get_var($wpdb->prepare(
            "SELECT image_url FROM `{$safe_table}` WHERE {$where} ORDER BY id DESC LIMIT 1",
            $args
        ));

        return $url ? (string) $url : '';
    }
}


if (!function_exists('bdo_driver_insert_basket_return_proof')) {
    function bdo_driver_insert_basket_return_proof($data) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_basket_return_proof_images';
        if (!bdo_driver_table_exists($table)) {
            return false;
        }

        $cols = bdo_driver_table_columns($table);

        $base = [
            'ledger_id'     => isset($data['ledger_id']) ? (int) $data['ledger_id'] : 0,
            'source_ref'    => isset($data['source_ref']) ? (string) $data['source_ref'] : '',
            'debtor_code'   => isset($data['debtor_code']) ? (string) $data['debtor_code'] : '',
            'debtor_name'   => isset($data['debtor_name']) ? (string) $data['debtor_name'] : '',
            'attachment_id' => isset($data['attachment_id']) ? (int) $data['attachment_id'] : 0,
            'image_url'     => isset($data['image_url']) ? (string) $data['image_url'] : '',
            'image_path'    => isset($data['image_path']) ? (string) $data['image_path'] : '',
            'mime_type'     => isset($data['mime_type']) ? (string) $data['mime_type'] : '',
            'file_size'     => isset($data['file_size']) ? (int) $data['file_size'] : 0,
            'captured_by'   => get_current_user_id(),
            'captured_at'   => current_time('mysql'),
            'created_at'    => current_time('mysql'),
        ];

        $insert = [];
        foreach ($base as $key => $value) {
            if (isset($cols[$key])) {
                $insert[$key] = $value;
            }
        }

        if (empty($insert)) {
            return false;
        }

        return $wpdb->insert($table, $insert) ? (int) $wpdb->insert_id : false;
    }
}

// ============================================================
// Fetch basket return proof image
// ============================================================
if (!function_exists('bdo_driver_get_basket_return_proof_image')) {
    function bdo_driver_get_basket_return_proof_image($ledger_id) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_basket_return_proof_images';
        if (!$ledger_id || !bdo_driver_table_exists($table)) {
            return '';
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $cols = bdo_driver_table_columns($table);

        $where = "ledger_id = %d";
        $args = [(int) $ledger_id];

        if (isset($cols['deleted_at'])) {
            $where .= " AND deleted_at IS NULL";
        }

        $url = $wpdb->get_var($wpdb->prepare(
            "SELECT image_url FROM `{$safe_table}` WHERE {$where} ORDER BY id DESC LIMIT 1",
            $args
        ));

        return $url ? (string) $url : '';
    }
}
// ============================================================

if (!function_exists('bdo_driver_job_summary')) {
    function bdo_driver_job_summary($job) {
        $payload = bdo_driver_json($job->payload ?? '');
        $result  = bdo_driver_json($job->result ?? '');
        $lines   = bdo_driver_sum_lines($payload);

        $doc_no = bdo_driver_pick($result, ['docNo', 'DocNo', 'doc_no'], '');
        if ($doc_no === '') {
            $doc_no = bdo_driver_pick($payload, ['docNo', 'DocNo', 'sourceDocNo', 'SourceDocNo'], 'JOB-' . (int) ($job->id ?? 0));
        }

        $doc_key = (int) bdo_driver_pick($result, ['docKey', 'DocKey', 'doc_key'], 0);

        $customer_name = bdo_driver_pick($payload, ['customerName', 'CustomerName', 'debtorName', 'DebtorName', 'customer_name'], 'Customer');
        $customer_code = bdo_driver_pick($payload, ['customerCode', 'CustomerCode', 'debtorCode', 'DebtorCode', 'customer_code'], '');
        $location      = bdo_driver_pick($payload, ['location', 'Location', 'headerLocation', 'HeaderLocation'], 'HQ');

        $address_parts = [];
        foreach (['deliveryAddress', 'DeliveryAddress', 'address', 'Address', 'shipToAddress', 'ShipToAddress', 'address1', 'Address1', 'address2', 'Address2', 'address3', 'Address3', 'address4', 'Address4'] as $key) {
            $v = bdo_driver_pick($payload, [$key], '');
            if ($v !== '') {
                $address_parts[] = $v;
            }
        }
        $address = !empty($address_parts) ? implode(', ', array_unique($address_parts)) : '';

        return [
            'id'             => (int) ($job->id ?? 0),
            'docNo'          => $doc_no,
            'docKey'         => $doc_key,
            'customerName'   => $customer_name,
            'customerCode'   => $customer_code,
            'address'        => $address,
            'location'       => $location,
            'status'         => (string) ($job->status ?? ''),
            'deliveryStatus' => (string) ($job->delivery_status ?? ''),
            'createdAt'      => (string) ($job->created_at ?? ''),
            'summary'        => $lines,
            'proofImage'     => bdo_driver_get_delivery_proof_image((int) ($job->id ?? 0)),
        ];
    }
}

$jobs_table        = $wpdb->prefix . 'ac_jobs';
$ledger_table      = $wpdb->prefix . 'ac_basket_ledger';
$proof_table       = $wpdb->prefix . 'ac_do_proof_images';
$basket_proof_table = $wpdb->prefix . 'ac_basket_return_proof_images';
$jobs_table_safe   = preg_replace('/[^A-Za-z0-9_]/', '', $jobs_table);
$ledger_table_safe = preg_replace('/[^A-Za-z0-9_]/', '', $ledger_table);

$current_user = wp_get_current_user();
$current_user_id = get_current_user_id();
$today = current_time('Y-m-d');
$current_url = bdo_driver_current_page_url();
$form_nonce = wp_create_nonce('bdo_driver_page_action');

// ------------------------------------------------------------------
// POST handlers: complete _���ϮA����������<
?!�����<?php
global $wpdb;

/**
 * Small Basket Return Receipt
 * URL:
 * /view-basket-receipt/?source_ref=BR-xxxx
 * or
 * /view-basket-receipt/?ledger_id=23
 */

if (!is_user_logged_in()) {
    wp_die('Please login to view this basket receipt.');
}

$ledger_table = $wpdb->prefix . 'ac_basket_ledger';

if (!function_exists('ac_br_h')) {
    function ac_br_h($value) {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
}

if (!function_exists('ac_br_num')) {
    function ac_br_num($value) {
        if ($value === '' || $value === null) return '';
        $num = (float) $value;

        if (floor($num) == $num) {
            return (string) intval($num);
        }

        return rtrim(rtrim(number_format($num, 2, '.', ''), '0'), '.');
    }
}

$source_ref = isset($_GET['source_ref']) ? sanitize_text_field(wp_unslash($_GET['source_ref'])) : '';
$ledger_id  = isset($_GET['ledger_id']) ? absint($_GET['ledger_id']) : 0;

if ($source_ref !== '') {
    $rows = $wpdb->get_results(
        $wpdb->prepare("
            SELECT *
            FROM {$ledger_table}
            WHERE source_ref = %s
              AND txn_type = 'RETURN'
            ORDER BY id ASC
        ", $source_ref),
        ARRAY_A
    );
} elseif ($ledger_id > 0) {
    $first = $wpdb->get_row(
        $wpdb->prepare("
            SELECT *
            FROM {$ledger_table}
            WHERE id = %d
              AND txn_type = 'RETURN'
            LIMIT 1
        ", $ledger_id),
        ARRAY_A
    );

    if (!$first) {
        wp_die('Basket return receipt not found.');
    }

    $rows = $wpdb->get_results(
        $wpdb->prepare("
            SELECT *
            FROM {$ledger_table}
            WHERE source_ref = %s
              AND txn_type = 'RETURN'
            ORDER BY id ASC
        ", $first['source_ref']),
        ARRAY_A
    );

    if (empty($rows)) {
        $rows = array($first);
    }
} else {
    wp_die('Missing receipt reference.');
}

if (empty($rows)) {
    wp_die('Basket return receipt not found.');
}

$first_row = $rows[0];

$company_name = 'EXCELLENT VEGE SDN. BHD.';
$show_signatures = false;
$show_receipt_no = false;

$receipt_no = 'BR-' . str_pad((string) $first_row['id'], 6, '0', STR_PAD_LEFT);

$customer_code = $first_row['debtor_code'] ?? '';
$customer_name = $first_row['debtor_name'] ?? '';

$txn_date = $first_row['txn_date'] ?? $first_row['created_at'] ?? date('Y-m-d');
$timestamp = strtotime($txn_date);
$display_date = $timestamp ? date('d/m/Y', $timestamp) : $txn_date;

$total_basket = 0;

foreach ($rows as $row) {
    $total_basket += (float) ($row['qty'] ?? 0);
}
?>

<style>
    * {
        box-sizing: border-box;
    }

    .ac-br-page-wrap {
        width: 100%;
        background: #e5e5e5;
        padding: 20px 0 40px;
        font-family: Arial, Helvetica, sans-serif;
        color: #222;
    }

    .ac-br-actions {
        width: 380px;
        margin: 0 auto 12px;
        text-align: right;
    }

    .ac-br-actions button {
        border: 0;
        background: #0B4A2D;
        color: #fff;
        padding: 9px 14px;
        border-radius: 6px;
        font-size: 13px;
        cursor: pointer;
        font-weight: 700;
    }

    .ac-br-receipt {
        width: 380px;
        margin: 0 auto;
        background: #fff;
        padding: 18px 20px;
        border: 1px solid #222;
        font-size: 13px;
    }

    .ac-br-company {
        text-align: center;
        font-weight: 800;
        font-size: 15px;
        margin-bottom: 6px;
    }

    .ac-br-title {
        text-align: center;
        font-weight: 800;
        font-size: 14px;
        border-top: 1px dashed #555;
        border-bottom: 1px dashed #555;
        padding: 7px 0;
        margin-bottom: 12px;
    }

    .ac-br-row {
        display: grid;
        grid-template-columns: 95px 1fr;
        gap: 8px;
        margin-bottom: 7px;
    }

    .ac-br-label {
        font-weight: 700;
    }

    .ac-br-main-total {
        margin: 14px 0;
        padding: 12px;
        border: 1px solid #222;
        text-align: center;
    }

    .ac-br-main-total div:first-child {
        font-size: 12px;
        font-weight: 700;
        margin-bottom: 4px;
    }

    .ac-br-main-total div:last-child {
        font-size: 34px;
        font-weight: 900;
        line-height: 1;
    }

    .ac-br-small-table {
        width: 100%;
        border-collapse: collapse;
        margin-top: 10px;
        font-size: 12px;
    }

    .ac-br-small-table th,
    .ac-br-small-table td {
        border-bottom: 1px dashed #aaa;
        padding: 5px 2px;
        text-align: left;
    }

    .ac-br-small-table th:last-child,
    .ac-br-small-table td:last-child {
        text-align: right;
        font-weight: 700;
    }

    .ac-br-signatures {
        margin-top: 26px;
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 20px;
        font-size: 12px;
    }

    .ac-br-sign {
        border-top: 1px solid #333;
        padding-top: 5px;
        text-align: center;
    }

    .ac-br-footer {
        margin-top: 18px;
        text-align: center;
        font-size: 11px;
        color: #555;
    }

    @media print {
        html,
        body {
            background: #fff !important;
            margin: 0 !important;
            padding: 0 !important;
            -webkit-print-color-adjust: exact !important;
            print-color-adjust: exact !important;
        }

        #wpadminbar,
        header,
        footer,
        .site-header,
        .site-footer,
        .elementor-location-header,
        .elementor-location-footer,
        .ac-br-actions {
            display: none !important;
        }

        .ac-br-page-wrap {
            background: #fff !important;
            padding: 0 !important;
            margin: 0 !important;
        }

        .ac-br-receipt {
            width: 80mm !important;
            margin: 0 auto !important;
            border: 0 !important;
            padding: 5mm !important;
        }

        @page {
            size: 80mm auto;
            margin: 4mm;
        }
    }
</style>

<div class="ac-br-page-wrap">
    <div class="ac-br-actions">
        <button type="button" onclick="window.print()">Print / Save PDF</button>
    </div>

    <div class="ac-br-receipt">
        <div class="ac-br-company">
            <?php echo ac_br_h($company_name); ?>
        </div>

        <div class="ac-br-title">
            BASKET RETURN RECEIPT
        </div>

        <?php if ($show_receipt_no): ?>
            <div class="ac-br-row">
                <div class="ac-br-label">Receipt No</div>
                <div><?php echo ac_br_h($receipt_no); ?></div>
            </div>
        <?php endif; ?>

        <div class="ac-br-row">
            <div class="ac-br-label">Date</div>
            <div><?php echo ac_br_h($display_date); ?></div>
        </div>

        <div class="ac-br-row">
            <div class="ac-br-label">Customer</div>
            <div>
                <?php echo ac_br_h($customer_name); ?>
            </div>
        </div>

        <div class="ac-br-main-total">
            <div>RETURNED BASKET</div>
            <div><?php echo ac_br_h(ac_br_num($total_basket)); ?></div>
        </div>

        <?php if (count($rows) > 1): ?>
            <table class="ac-br-small-table">
                <thead>
                    <tr>
                        <th>No.</th>
                        <th>Qty</th>
                    </tr>
                </thead>
                <tbody>
                    <?php $i = 1; ?>
                    <?php foreach ($rows as $row): ?>
                        <tr>
                            <td><?php echo $i++; ?></td>
                            <td><?php echo ac_br_h(ac_br_num($row['qty'] ?? 0)); ?></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        <?php endif; ?>

        <?php if ($show_signatures): ?>
            <div class="ac-br-signatures">
                <div class="ac-br-sign">Received by</div>
                <div class="ac-br-sign">Checked by</div>
            </div>
        <?php endif; ?>

        <div class="ac-br-footer">
            Thank you
        </div>
    </div>
</div>��<�+B��������`�
??�Cdelivery + basket return with proof.
// ------------------------------------------------------------------
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['bdo_driver_action'])) {
    $action = sanitize_text_field(wp_unslash($_POST['bdo_driver_action']));

    if (!isset($_POST['bdo_driver_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['bdo_driver_nonce'])), 'bdo_driver_page_action')) {
        wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Security check failed.')], $current_url));
        exit;
    }

    if ($action === 'complete_delivery') {
        $job_id = isset($_POST['job_id']) ? absint($_POST['job_id']) : 0;

        if (!$job_id || !bdo_driver_table_exists($jobs_table)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Active job not found.')], $current_url));
            exit;
        }

        $job = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$jobs_table_safe}` WHERE id = %d LIMIT 1", $job_id));
        if (!$job) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Delivery job not found.')], $current_url));
            exit;
        }

        $job_cols_for_owner = bdo_driver_table_columns($jobs_table);
        if (isset($job_cols_for_owner['created_by']) && (int) ($job->created_by ?? 0) !== (int) $current_user_id) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('This delivery belongs to another driver account.')], $current_url));
            exit;
        }

        $status = strtoupper((string) ($job->status ?? ''));
        if ($status !== 'SUCCESS') {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Please wait until AutoCount finishes creating the DO before completing delivery.')], $current_url));
            exit;
        }

        $upload = bdo_driver_upload_image('delivery_proof');
        if (is_wp_error($upload)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode($upload->get_error_message())], $current_url));
            exit;
        }

        $summary = bdo_driver_job_summary($job);

        bdo_driver_insert_proof(array_merge($upload, [
            'job_id'     => $job_id,
            'doc_no'     => $summary['docNo'],
            'doc_key'    => $summary['docKey'],
            'proof_type' => 'DELIVERY_PROOF',
        ]));

        $cols = bdo_driver_table_columns($jobs_table);
        $update = [];
        if (isset($cols['delivery_status'])) {
            $update['delivery_status'] = 'DELIVERED';
        }
        if (isset($cols['delivery_completed_by'])) {
            $update['delivery_completed_by'] = $current_user_id;
        }
        if (isset($cols['delivery_completed_at'])) {
            $update['delivery_completed_at'] = current_time('mysql');
        }
        if (isset($cols['delivery_note'])) {
            $update['delivery_note'] = 'Completed with proof of delivery';
        }
        if (isset($cols['updated_at'])) {
            $update['updated_at'] = current_time('mysql');
        }

        if (!empty($update)) {
            $wpdb->update($jobs_table, $update, ['id' => $job_id]);
        }

        wp_safe_redirect(add_query_arg(['bdo_msg' => 'delivered', 'bdo_job_id' => $job_id, 'bdo_tab' => 'deliveries'], $current_url));
        exit;
    }

    if ($action === 'basket_return') {
        $debtor_code = isset($_POST['br_debtor_code']) ? sanitize_text_field(wp_unslash($_POST['br_debtor_code'])) : '';
        $debtor_name = isset($_POST['br_debtor_name']) ? sanitize_text_field(wp_unslash($_POST['br_debtor_name'])) : '';
        $basket_qty  = isset($_POST['br_basket_qty']) ? absint($_POST['br_basket_qty']) : 0;

        if ($debtor_code === '' || $debtor_name === '') {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Please select customer for basket return.')], $current_url));
            exit;
        }

        if ($basket_qty <= 0) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Basket return quantity must be more than 0.')], $current_url));
            exit;
        }

        if (!bdo_driver_table_exists($ledger_table)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Basket ledger table not found.')], $current_url));
            exit;
        }

        if (!bdo_driver_table_exists($basket_proof_table)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Basket return proof image table not found.')], $current_url));
            exit;
        }

        $upload = bdo_driver_upload_image('basket_return_proof');
        if (is_wp_error($upload)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode($upload->get_error_message())], $current_url));
            exit;
        }

        $source_ref = 'BR-' . current_time('YmdHis') . '-' . $current_user_id;
        $cols = bdo_driver_table_columns($ledger_table);

        $possible_insert = [
            'source_ref'  => $source_ref,
            'txn_type'    => 'RETURN',
            'debtor_code' => $debtor_code,
            'debtor_name' => $debtor_name,
            'txn_date'    => $today,
            'qty'         => $basket_qty,
            'created_by'  => $current_user_id,
            'created_at'  => current_time('mysql'),
            'updated_at'  => current_time('mysql'),
            'remark'      => 'Basket return by driver',
            'note'        => 'Basket return by driver',
        ];

        $insert = [];
        foreach ($possible_insert as $key => $value) {
            if (isset($cols[$key])) {
                $insert[$key] = $value;
            }
        }

        $ok = !empty($insert) ? $wpdb->insert($ledger_table, $insert) : false;
        if (!$ok) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Failed to save basket return.')], $current_url));
            exit;
        }

        $ledger_id = (int) $wpdb->insert_id;

        bdo_driver_insert_basket_return_proof(array_merge($upload, [
            'ledger_id'   => $ledger_id,
            'source_ref'  => $source_ref,
            'debtor_code' => $debtor_code,
            'debtor_name' => $debtor_name,
        ]));

        wp_safe_redirect(add_query_arg(['bdo_msg' => 'br_saved', 'bdo_br_ledger_id' => $ledger_id, 'bdo_tab' => 'return'], $current_url));
        exit;
    }
}

// ------------------------------------------------------------------
// Dashboard data.
// ------------------------------------------------------------------
$rest_nonce         = wp_create_nonce('wp_rest');
$ajax_url           = admin_url('admin-ajax.php');
$debtor_nonce       = wp_create_nonce('ac_cs_debtor_search');
$item_nonce         = wp_create_nonce('ac_itemcode_suggest');
$REST_JOB_POST      = rest_url('ac/v1/job');
$REST_JOB_BASE      = rest_url('ac/v1/job/');
$REST_RECEIPT_TOKEN = rest_url('ac/v1/do-receipt-token');
$receipt_page_base  = 'https://website.ipohserver.com/VegeBasketDO/do-receipt/';
$default_location   = 'HQ';

$stats = [
    'created_today'  => 0,
    'active'         => 0,
    'delivered'      => 0,
    'return_baskets' => 0,
];

$driver_filter_sql = '';
$driver_filter_args = [];

if (bdo_driver_table_exists($jobs_table)) {
    $job_cols = bdo_driver_table_columns($jobs_table);
    if (isset($job_cols['created_by'])) {
        $driver_filter_sql = ' AND created_by = %d ';
        $driver_filter_args[] = $current_user_id;
    }

    $stats['created_today'] = (int) $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(*) FROM `{$jobs_table_safe}` WHERE job_type = 'DELIVERY_ORDER' AND DATE(created_at) = %s {$driver_filter_sql}",
        array_merge([$today], $driver_filter_args)
    ));

    $stats['active'] = (int) $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(*) FROM `{$jobs_table_safe}` WHERE job_type = 'DELIVERY_ORDER' AND status IN ('SUCCESS','PENDING','PROCESSING') AND delivery_status NOT IN ('DELIVERED','COMPLETED','CANCELLED') {$driver_filter_sql}",
        $driver_filter_args
    ));

    $stats['delivered'] = (int) $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(*) FROM `{$jobs_table_safe}` WHERE job_type = 'DELIVERY_ORDER' AND delivery_status = 'DELIVERED' AND DATE(COALESCE(delivery_completed_at, updated_at, created_at)) = %s {$driver_filter_sql}",
        array_merge([$today], $driver_filter_args)
    ));
}

if (bdo_driver_table_exists($ledger_table)) {
    $ledger_cols = bdo_driver_table_columns($ledger_table);
    $date_col = isset($ledger_cols['created_at']) ? 'created_at' : (isset($ledger_cols['txn_date']) ? 'txn_date' : '');
    $ledger_driver_filter_sql = '';
    $ledger_driver_filter_args = [];

    if (isset($ledger_cols['created_by'])) {
        $ledger_driver_filter_sql = ' AND created_by = %d ';
        $ledger_driver_filter_args[] = $current_user_id;
    }

    if ($date_col !== '' && isset($ledger_cols['txn_type'])) {
        $qty_expr = isset($ledger_cols['qty']) ? 'COALESCE(SUM(ABS(qty)),0)' : 'COUNT(*)';
        $stats['return_baskets'] = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT {$qty_expr} FROM `{$ledger_table_safe}` WHERE txn_type = 'RETURN' AND DATE(`{$date_col}`) = %s {$ledger_driver_filter_sql}",
            array_merge([$today], $ledger_driver_filter_args)
        ));
    }
}

$active_job = null;
$active_jobs = [];
$today_jobs = [];
$delivered_jobs = [];
$return_rows = [];

if (bdo_driver_table_exists($jobs_table)) {
    $active_sql = "SELECT * FROM `{$jobs_table_safe}`
        WHERE job_type = 'DELIVERY_ORDER'
        AND status IN ('SUCCESS','PENDING','PROCESSING')
        AND delivery_status NOT IN ('DELIVERED','COMPLETED','CANCELLED')
        {$driver_filter_sql}
        ORDER BY
            CASE delivery_status
                WHEN 'OUT_FOR_DELIVERY' THEN 1
                WHEN 'PENDING_DELIVERY' THEN 2
                ELSE 3
            END,
            id DESC
        LIMIT 50";

    $active_rows = !empty($driver_filter_args)
        ? $wpdb->get_results($wpdb->prepare($active_sql, $driver_filter_args))
        : $wpdb->get_results($active_sql);

    if (!empty($active_rows)) {
        $active_job = $active_rows[0];
        foreach ($active_rows as $row) {
            $active_jobs[] = bdo_driver_job_summary($row);
        }
    }
}

if (bdo_driver_table_exists($jobs_table)) {
    $today_sql = "SELECT * FROM `{$jobs_table_safe}`
        WHERE job_type = 'DELIVERY_ORDER'
        AND DATE(created_at) = %s
        {$driver_filter_sql}
        ORDER BY id DESC
        LIMIT 100";

    $today_args = array_merge([$today], $driver_filter_args);
    $today_rows = !empty($today_args)
        ? $wpdb->get_results($wpdb->prepare($today_sql, $today_args))
        : $wpdb->get_results($today_sql);

    foreach ((array) $today_rows as $row) {
        $today_jobs[] = bdo_driver_job_summary($row);
    }

    $delivered_sql = "SELECT * FROM `{$jobs_table_safe}`
        WHERE job_type = 'DELIVERY_ORDER'
        AND delivery_status = 'DELIVERED'
        AND DATE(COALESCE(delivery_completed_at, updated_at, created_at)) = %s
        {$driver_filter_sql}
        ORDER BY COALESCE(delivery_completed_at, updated_at, created_at) DESC
        LIMIT 100";

    $delivered_args = array_merge([$today], $driver_filter_args);
    $delivered_rows = !empty($delivered_args)
        ? $wpdb->get_results($wpdb->prepare($delivered_sql, $delivered_args))
        : $wpdb->get_results($delivered_sql);

    foreach ((array) $delivered_rows as $row) {
        $delivered_jobs[] = bdo_driver_job_summary($row);
    }
}

if (bdo_driver_table_exists($ledger_table)) {
    $ledger_cols_for_list = bdo_driver_table_columns($ledger_table);
    $ledger_driver_filter_sql = '';
    $ledger_driver_filter_args = [];
    if (isset($ledger_cols_for_list['created_by'])) {
        $ledger_driver_filter_sql = ' AND created_by = %d ';
        $ledger_driver_filter_args[] = $current_user_id;
    }

    $date_col_for_list = isset($ledger_cols_for_list['created_at']) ? 'created_at' : (isset($ledger_cols_for_list['txn_date']) ? 'txn_date' : '');
    if ($date_col_for_list !== '' && isset($ledger_cols_for_list['txn_type'])) {
        $return_sql = "SELECT * FROM `{$ledger_table_safe}`
            WHERE txn_type = 'RETURN'
            AND DATE(`{$date_col_for_list}`) = %s
            {$ledger_driver_filter_sql}
            ORDER BY id DESC
            LIMIT 100";
        $return_args = array_merge([$today], $ledger_driver_filter_args);
        $return_results = $wpdb->get_results($wpdb->prepare($return_sql, $return_args), ARRAY_A);
        foreach ((array) $return_results as $row) {
            $ledger_id = (int) ($row['id'] ?? 0);
            $return_rows[] = [
                'id' => $ledger_id,
                'customerName' => (string) ($row['debtor_name'] ?? 'Customer'),
                'qty' => (int) round((float) ($row['qty'] ?? 0)),
                'date' => (string) ($row['created_at'] ?? ($row['txn_date'] ?? '')),
                'proofImage' => bdo_driver_get_basket_return_proof_image($ledger_id),
            ];
        }
    }
}

$active_summary = $active_job ? bdo_driver_job_summary($active_job) : null;
$initial_active_json = wp_json_encode($active_summary ?: new stdClass());
$active_jobs_json = wp_json_encode($active_jobs);
$today_jobs_json = wp_json_encode($today_jobs);
$delivered_jobs_json = wp_json_encode($delivered_jobs);
$return_rows_json = wp_json_encode($return_rows);
$stats_json = wp_json_encode($stats);

$inline_br_receipt_html = '';
$br_ledger_id = isset($_GET['bdo_br_ledger_id']) ? absint($_GET['bdo_br_ledger_id']) : 0;

if ($br_ledger_id > 0 && bdo_driver_table_exists($ledger_table)) {
    $first = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$ledger_table_safe}` WHERE id = %d AND txn_type = 'RETURN' LIMIT 1", $br_ledger_id), ARRAY_A);

    if ($first) {
        $source_ref = $first['source_ref'] ?? '';
        $rows = [];

        if ($source_ref !== '') {
            $rows = $wpdb->get_results($wpdb->prepare("SELECT * FROM `{$ledger_table_safe}` WHERE source_ref = %s AND txn_type = 'RETURN' ORDER BY id ASC", $source_ref), ARRAY_A);
        }

        if (empty($rows)) {
            $rows = [$first];
        }

        $customer_name = $first['debtor_name'] ?? '';
        $txn_date = $first['txn_date'] ?? ($first['created_at'] ?? current_time('Y-m-d'));
        $display_date = date('d/m/Y', strtotime($txn_date));
        $total_basket = 0;
        foreach ($rows as $row) {
            $total_basket += (float) ($row['qty'] ?? 0);
        }

        $proof_image = bdo_driver_get_basket_return_proof_image($br_ledger_id);

        $proof_html = '';
        if ($proof_image !== '') {
            $proof_html = '<div class="bdo-receipt-proof">'
                . '<div class="bdo-receipt-proof-title">Basket Return Proof</div>'
                . '<img class="bdo-receipt-proof-img" src="' . esc_url($proof_image) . '" alt="Basket Return Proof">'
                . '</div>';
        } else {
            $proof_html = '<div class="bdo-receipt-proof">'
                . '<div class="bdo-receipt-proof-title">Basket Return Proof</div>'
                . '<div class="bdo-receipt-proof-empty">No basket proof image found.</div>'
                . '</div>';
        }

        $inline_br_receipt_html = '<div class="bdo-inline-receipt" id="bdoInlineBrReceipt">'
            . '<div class="bdo-receipt-actions"><button type="button" onclick="window.bdoPrintBasketReceipt ? window.bdoPrintBasketReceipt() : window.print()">Print / Save PDF</button><button type="button" onclick="window.bdoShareBasketReceiptPdf && window.bdoShareBasketReceiptPdf()">Share WhatsApp</button><button type="button" onclick="document.getElementById(\'bdoInlineBrReceipt\').style.display=\'none\'">Close</button></div>'
            . '<div class="bdo-receipt-paper">'
            . '<div class="bdo-receipt-company">EXCELLENT VEGE SDN. BHD.</div>'
            . '<div class="bdo-receipt-title">BASKET RETURN RECEIPT</div>'
          `�_<�C��������`Y�
??�\  . '<div class="bdo-receipt-row"><strong>Date</strong><span>' . esc_html($display_date) . '</span></div>'
            . '<div class="bdo-receipt-row"><strong>Customer</strong><span>' . esc_html($customer_name) . '</span></div>'
            . '<div class="bdo-receipt-total"><span>RETURNED BASKET</span><strong>' . esc_html((string) (int) round($total_basket)) . '</strong></div>'
            . $proof_html
            . '<div class="bdo-receipt-footer">Thank you</div>'
            . '</div></div>';
    }
}

$message = isset($_GET['bdo_msg']) ? sanitize_text_field(wp_unslash($_GET['bdo_msg'])) : '';
$error   = isset($_GET['bdo_err']) ? sanitize_text_field(wp_unslash($_GET['bdo_err'])) : '';
$initial_tab = isset($_GET['bdo_tab']) ? sanitize_key(wp_unslash($_GET['bdo_tab'])) : 'home';

$raw_driver_name = $current_user->display_name ?: $current_user->user_login ?: 'Driver';
$driver_name = ucwords(strtolower(trim($raw_driver_name)));
$today_label = date_i18n('D, d M Y', strtotime($today));

$html = <<<'HTML'
<style>
    html, body { background:#eef4ef !important; }
    #wpadminbar, header, footer, .site-header, .site-footer, .elementor-location-header, .elementor-location-footer { display:none !important; }
    html { margin-top:0 !important; }

    #bdo-driver-app {
        --main:#0B4A2D;
        --main-2:#0f6b42;
        --main-soft:#eaf5ef;
        --main-soft-2:#d7efe2;
        --ink:#102019;
        --muted:#617067;
        --line:#d9e5dd;
        --card:#ffffff;
        --danger:#dc2626;
        --amber:#b45309;
        width:100%;
        min-height:100vh;
        background:linear-gradient(180deg,#0B4A2D 0,#0B4A2D 210px,#eef4ef 210px,#eef4ef 100%);
        font-family:Segoe UI,Roboto,Arial,sans-serif;
        color:var(--ink);
        box-sizing:border-box;
    }
    #bdo-driver-app * { box-sizing:border-box; }
    .bdo-wrap { max-width:560px; margin:0 auto; min-height:100vh; padding:16px 14px 96px; }
    .bdo-top { color:#fff; display:flex; justify-content:space-between; align-items:flex-start; gap:12px; padding:4px 2px 16px; }
    .bdo-brand { font-size:22px; font-weight:900; letter-spacing:-.04em; }
    .bdo-brand small { display:block; font-size:12px; color:rgba(255,255,255,.72); font-weight:700; letter-spacing:0; margin-top:3px; }
    
    /* Updated .bdo-date and related styles */
    .bdo-date {
        font-size: 12px;
        color: rgba(255,255,255,.76);
        font-weight: 700;
        text-align: right;
        display: flex;
        flex-direction: column;
        align-items: flex-end;
        gap: 10px;
    }
    .bdo-top-actions {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: 8px;
        flex-wrap: nowrap;
    }
    .bdo-online,
    .bdo-logout {
        min-height: 42px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border-radius: 999px;
        font-size: 13px;
        font-weight: 900;
        line-height: 1;
        white-space: nowrap;
    }
    .bdo-online {
        gap: 7px;
        background: rgba(255,255,255,.13);
        border: 1px solid rgba(255,255,255,.2);
        padding: 0 14px;
        color: #fff;
    }
    .bdo-logout {
        background: rgba(255,255,255,.16);
        border: 1px solid rgba(255,255,255,.28);
        color: #fff !important;
        text-decoration: none !important;
        padding: 0 16px;
    }
    
    .bdo-dot-live { width:8px; height:8px; border-radius:50%; background:#22c55e; box-shadow:0 0 0 4px rgba(34,197,94,.18); }
    .bdo-hero { background:#fff; border-radius:24px; padding:16px; box-shadow:0 18px 40px rgba(7,32,20,.18); border:1px solid rgba(255,255,255,.8); margin-bottom:12px; }
    .bdo-driver-row { display:flex; justify-content:space-between; gap:12px; align-items:center; margin-bottom:14px; }
    .bdo-avatar-row { display:flex; gap:11px; align-items:center; min-width:0; }
    .bdo-avatar { width:48px; height:48px; border-radius:18px; background:var(--main-soft); display:grid; place-items:center; font-size:25px; flex:0 0 auto; }
    .bdo-greet { color:var(--muted); font-size:12px; font-weight:700; }
    .bdo-name { font-size:20px; font-weight:900; letter-spacing:-.03em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    .bdo-mini-status { padding:8px 10px; border-radius:999px; background:var(--main-soft); color:var(--main); font-weight:900; font-size:12px; flex:0 0 auto; }
    .bdo-stats { display:grid; grid-template-columns:repeat(4,1fr); border:1px solid var(--line); border-radius:18px; overflow:hidden; background:#fbfdfb; }
    .bdo-stat { padding:12px 6px; text-align:center; border-right:1px solid var(--line); border:0; background:transparent; cursor:pointer; font-family:inherit; }
    .bdo-stat:last-child { border-right:0; }
    .bdo-stat:hover, .bdo-stat.active { background:var(--main-soft); }
    .bdo-list-filter-title { font-size:13px; color:var(--muted); font-weight:900; margin-bottom:10px; }
    .bdo-stat strong { display:block; font-size:23px; line-height:1; font-weight:950; color:var(--main); letter-spacing:-.04em; }
    .bdo-stat span { display:block; margin-top:5px; font-size:10.5px; line-height:1.15; color:#46564c; font-weight:800; }
    .bdo-panel { display:none; animation:bdoFade .16s ease; }
    .bdo-panel.active { display:block; }
    @keyframes bdoFade { from {opacity:.5; transform:translateY(4px);} to {opacity:1; transform:none;} }
    .bdo-card { background:#fff; border:1px solid var(--line); border-radius:22px; box-shadow:0 10px 26px rgba(10,45,29,.07); overflow:hidden; margin-bottom:12px; }
    .bdo-card-head { padding:14px 15px; display:flex; justify-content:space-between; align-items:center; gap:10px; border-bottom:1px solid #eef4f0; }
    .bdo-card-title { font-weight:950; font-size:16px; letter-spacing:-.02em; }
    .bdo-card-sub { color:var(--muted); font-size:12px; font-weight:700; margin-top:2px; }
    .bdo-card-body { padding:15px; }
    .bdo-quick-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-bottom:12px; }
    .bdo-quick { border:1px solid var(--line); background:#fff; border-radius:20px; padding:14px; text-align:left; cursor:pointer; min-height:95px; box-shadow:0 10px 24px rgba(10,45,29,.05); }
    .bdo-quick-icon { width:38px; height:38px; border-radius:15px; background:var(--main-soft); display:grid; place-items:center; color:var(--main); font-size:20px; margin-bottom:10px; }
    .bdo-quick strong { display:block; font-size:14px; font-weight:950; color:var(--ink); }
    .bdo-quick span { display:block; color:var(--muted); font-size:11.5px; font-weight:700; margin-top:3px; line-height:1.25; }
    .bdo-active-mini { border:1px solid var(--line); border-radius:20px; padding:14px; background:linear-gradient(180deg,#fff,#fbfdfb); }
    .bdo-delivery-list { display:grid; gap:10px; }
    .bdo-delivery-row { border:1px solid var(--line); border-radius:18px; padding:13px; background:#fff; box-shadow:0 8px 22px rgba(10,45,29,.045); }
    .bdo-delivery-top { display:flex; justify-content:space-between; align-items:flex-start; gap:10px; margin-bottom:8px; }
    .bdo-delivery-title { font-size:15px; font-weight:950; letter-spacing:-.01em; }
    .bdo-delivery-sub { font-size:12px; color:var(--muted); font-weight:800; margin-top:3px; }
    .bdo-delivery-metrics { display:grid; grid-template-columns:repeat(3,1fr); gap:6px; margin:10px 0; }
    .bdo-delivery-metrics span { background:#f8fbf9; border:1px solid #e6f0ea; border-radius:12px; padding:8px 6px; text-align:center; color:var(--main); font-weight:950; font-size:12px; }
    .bdo-status-chip { display:inline-flex; align-items:center; gap:6px; padding:7px 10px; border-radius:999px; background:var(--main-soft); color:var(--main); font-size:11px; font-weight:950; }
    .bdo-status-chip.wait { background:#fff7ed; color:var(--amber); }
    .bdo-job-main { margin:12px 0; }
    .bdo-job-main h3 { margin:0; font-size:20px; font-weight:950; letter-spacing:-.03em; }
    .bdo-job-main p { margin:5px 0 0; color:var(--muted); font-size:13px; font-weight:700; line-height:1.35; }
    .bdo-job-metrics { display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin:12px 0; }
    .bdo-metric { background:#f7faf8; border:1px solid #e7f0ea; border-radius:15px; padding:10px 8px; text-align:center; }
    .bdo-metric strong { display:block; font-size:18px; font-weight:950; color:var(--main); line-height:1; }
    .bdo-metric span { display:block; font-size:10.5px; color:var(--muted); font-weight:800; margin-top:5px; }
    .bdo-btn, .bdo-btn-soft, .bdo-btn-danger { width:100%; border:0; border-radius:15px; min-height:48px; padding:12px 14px; font-size:15px; font-weight:950; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; text-decoration:none !important; gap:8px; font-family:inherit; }
    .bdo-btn { background:var(--main); color:#fff !important; box-shadow:0 12px 22px rgba(11,74,45,.18); }
    .bdo-btn:hover { background:var(--main-2); color:#fff !important; }
    .bdo-btn:disabled { background:#94a3b8; box-shadow:none; cursor:not-allowed; }
    .bdo-btn-soft { background:var(--main-soft); color:var(--main) !important; border:1px solid var(--main-soft-2); }
    #bdo-driver-app .bdo-btn-soft:hover, #bdo-driver-app .bdo-btn-soft:focus { background:var(--main-soft-2) !important; color:var(--main) !important; border-color:#b9ddc8 !important; box-shadow:0 8px 18px rgba(11,74,45,.10) !important; }
    .bdo-btn-danger { background:#fff1f2; color:#be123c !important; border:1px solid #fecdd3; }
    .bdo-two { display:grid; grid-template-columns:1fr 1fr; gap:9px; }
    .bdo-field { margin-bottom:12px; }
    .bdo-field label { display:block; font-size:12px; color:var(--muted); font-weight:900; margin-bottom:5px; }
    .bdo-input, .bdo-file { width:100%; min-height:46px; border:1px solid #cddbd2; border-radius:14px; padding:10px 12px; background:#fff; font-size:15px; color:var(--ink); font-family:inherit; }
    .bdo-input:focus { outline:none; border-color:var(--main); box-shadow:0 0 0 3px rgba(11,74,45,.1); }
    .bdo-search-wrap { position:relative; }
    /* REPLACED .bdo-clear and .bdo-clear.show styles */
    #bdo-driver-app .bdo-search-wrap .bdo-input {
        padding-right: 52px !important;
    }

    #bdo-driver-app .bdo-search-wrap .bdo-clear {
        display: none !important;
        position: absolute !important;
        right: 9px !important;
        top: 50% !important;
        transform: translateY(-50%) !important;

        width: 34px !important;
        height: 34px !important;
        min-width: 34px !important;
        min-height: 34px !important;
        padding: 0 !important;
        margin: 0 !important;

        align-items: center !important;
        justify-content: center !important;

        border-radius: 11px !important;
        border: 1px solid var(--line) !important;
        background: #fff !important;
        color: var(--muted) !important;

        font-family: Arial, sans-serif !important;
        font-size: 24px !important;
        font-weight: 400 !important;
        line-height: 1 !important;
        text-align: center !important;

        cursor: pointer !important;
        appearance: none !important;
        -webkit-appearance: none !important;
    }

    #bdo-driver-app .bdo-search-wrap .bdo-clear.show {
        display: inline-flex !important;
    }

    #bdo-driver-app .bdo-search-wrap .bdo-clear:hover,
    #bdo-driver-app .bdo-search-wrap .bdo-clear:focus,
    #bdo-driver-app .bdo-search-wrap .bdo-clear:active {
        background: #f8fbf9 !important;
        color: var(--main) !important;
        outline: none !important;
    }

    /* Customer locked state (after adding items) */
    #bdo-driver-app .bdo-customer-locked {
        background: #f8fbf9 !important;
        border-color: #cddbd2 !important;
        color: #475569 !important;
    }

    .bdo-type-toggle { display:grid; grid-template-columns:1fr 1fr; gap:9px; }
    .bdo-type-btn { min-height:45px; border:1px solid #cddbd2; background:#fff; color:#334155; border-radius:14px; font-weight:950; cursor:pointer; }
    #bdo-driver-app .bdo-type-btn.active { background:var(--main) !important; color:#fff !important; border-color:var(--main) !important; box-shadow:0 8px 18px rgba(11,74,45,.15); }
    .bdo-preview { display:none; background:var(--main-soft); border:1px solid var(--main-soft-2); border-radius:14px; padding:10px; font-size:13px; margin-bottom:10px; color:var(--main); font-weight:800; }
    .bdo-lines { max-height:280px; overflow-y:auto; }
    .bdo-empty { padding:18px; text-align:center; color:var(--muted); font-weight:700; background:#f8fbf9; border-radius:16px; }
    .bdo-line { border:1px solid var(--line); border-radius:16px; padding:11px; margin-bottom:8px; background:#fff; }
    .bdo-line-top { display:flex; justify-content:space-between; gap:10px; align-items:flex-start; }
    .bdo-line-name { font-weight:950; font-size:14px; }
    .bdo-line-del { border:1px solid #fecaca; background:#fff5f5; color:#dc2626; border-radius:12px; font-weight:900; padding:7px 10px; cursor:pointer; }
    .bdo-line-meta { display:grid; grid-template-columns:repeat(4,1fr); gap:6px; margin-top:9px; }
    .bdo-line-chip { background:#f8fbf9; border:1px solid #e6f0ea; border-radius:12px; padding:7px; text-align:center; }
    .bdo-line-chip small { display:block; color:var(--muted); font-size:10px; font-weight:800; }
    .bdo-line-chip strong { display:block; color:var(--main); font-size:13px; font-weight:950; margin-top:2px; }
    /* New fixed item card display for Delivery Proof screen */
    #bdo-driver-app .bdo-pod-items {
        display: grid;
        gap: 9px;
        margin-top: 12px;
    }
    #bdo-driver-app .bdo-pod-item {
        border: 1px solid #e6f0ea;
        background: #fff;
        border-radius: 15px;
        padding: 10px;
    }
    #bdo-driver-app .bdo-pod-item-name {
        font-size: 14px;
        font-weight: 950;
        color: var(--ink);
        line-height: 1.25;
        margin-bottom: 8px;
        word-break: break-word;
    }
    #bdo-driver-app .bdo-pod-item-grid {
        display: grid;
        grid-template-columns: repeat(4, 1fr);
        gap: 6px;
    }
    #bdo-driver-app .bdo-pod-item-grid div {
        background: #f8fbf9;
        border: 1px solid #edf4ef;
        border-radius: 11px;
        padding: 7px 4px;
        text-align: center;
        min-width: 0;
    }
    #bdo-driver-app .bdo-pod-item-grid small {
        display: block;
        color: var(--muted);
        font-size: 9px;
        font-weight: 900;
        text-transform: uppercase;
        line-height: 1;
    }
    #bdo-driver-app .bdo-pod-item-grid strong {
        display: block;
        color: var(--main);
        font-size: 12px;
        font-weight: 950;
        margin-top: 4px;
        line-height: 1.1;
    }
    @media (max-width: 390px) {
        #bdo-driver-app .bdo-pod-item-grid {
            grid-template-columns: repeat(2, 1fr);
        }
    }
    .bdo-proof-box { background:#f8fbf9; border:1px dashed #aec8b9; border-radius:18px; padding:13px; margin-top:12px; }
    .bdo-proof-box strong { display:block; color:var(--main); margin-bottom:5px; }
    .bdo-proof-box p { margin:0 0 10px; color:var(--muted); font-size:12px; font-weight:700; }
    .bdo-picker-modal { display:none; position:fixed; inset:0; z-index:99999; align-items:center; justify-content:center; padding:14px; }
    .bdo-picker-modal.active { display:flex; }
    .bdo-picker-backdrop { position:absolute; inset:0; background:rgba(5,20,13,.58); }
    .bdo-picker-sheet { position:relative; width:100%; max-width:520px; max-height:86vh; background:#fff; border-radius:22px; overflow:hidden; box-shadow:0 24px 60px rgba(0,0,0,.28); }
    .bdo-picker-head { padding:14px; border-bottom:1px solid var(--line); display:flex; justify-content:space-between; align-items:center; }
    .bdo-picker-title { font-weight:950; font-size:17px; }
    /* REPLACED .bdo-picker-close styles */
    #bdo-dri`Y��]#D��������)hOj
??�E<?php
if (!defined('ABSPATH')) exit;

/*
 * VegeBasketDO staff Delivery Order list.
 *
 * Page URLs:
 * Edit: /edit-delivery-order/?docNo=DO-0001&docKey=123
 * View: /view-delivery-order/?docNo=DO-0001&docKey=123
 */

if (!is_user_logged_in()) {
    echo '<div class="wst-dod-alert wst-dod-alert-error">Please log in to view Delivery Order records.</div>';
    return;
}

if (!current_user_can('edit_posts') && !current_user_can('manage_options')) {
    echo '<div class="wst-dod-alert wst-dod-alert-error">You do not have permission to view Delivery Order records.</div>';
    return;
}

if (!function_exists('get_mssql')) {
    echo '<div class="wst-dod-alert wst-dod-alert-error">Delivery Order connection is not available.</div>';
    return;
}

$conn = get_mssql();
if (!$conn) {
    echo '<div class="wst-dod-alert wst-dod-alert-error">Failed to connect to Delivery Order records.</div>';
    return;
}

$edit_page_url = home_url('/edit-delivery-order/');
$view_page_url = home_url('/view-delivery-order/');
$show_technical_errors = current_user_can('manage_options') && defined('WP_DEBUG') && WP_DEBUG;

if (!function_exists('wst_dod_log_error')) {
    function wst_dod_log_error($message) {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log('[VegeBasketDO DO List] ' . $message);
        }
    }
}

if (!function_exists('wst_dod_errors')) {
    function wst_dod_errors() {
        if (!function_exists('sqlsrv_errors')) return 'Unknown SQLSRV error.';

        $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
        if (empty($errors)) return 'Unknown SQLSRV error.';

        $out = array();
        foreach ($errors as $e) {
            $out[] = '[' . ($e['code'] ?? '') . '] ' . ($e['message'] ?? '');
        }

        return implode(' | ', $out);
    }
}

if (!function_exists('wst_dod_valid_date')) {
    function wst_dod_valid_date($value, $fallback) {
        $value = trim((string)$value);
        if ($value === '') return $fallback;

        $dt = DateTime::createFromFormat('Y-m-d', $value, wp_timezone());
        if (!$dt || $dt->format('Y-m-d') !== $value) return $fallback;

        return $value;
    }
}

if (!function_exists('wst_dod_date')) {
    function wst_dod_date($v) {
        if ($v instanceof DateTime) return $v->format('Y-m-d');

        if (is_string($v) && $v !== '') {
            $ts = strtotime($v);
            if ($ts) return date('Y-m-d', $ts);
            return $v;
        }

        return '';
    }
}

if (!function_exists('wst_dod_datetime')) {
    function wst_dod_datetime($v) {
        if ($v instanceof DateTime) return $v->format('Y-m-d H:i:s');

        if (is_string($v) && $v !== '') {
            $ts = strtotime($v);
            if ($ts) return date('Y-m-d H:i:s', $ts);
            return $v;
        }

        return '';
    }
}

if (!function_exists('wst_dod_fmt_qty')) {
    function wst_dod_fmt_qty($v, $decimals = 2) {
        $n = (float)$v;

        if (abs($n - round($n)) < 0.00001) {
            return number_format_i18n($n, 0);
        }

        return number_format_i18n($n, $decimals);
    }
}

if (!function_exists('wst_dod_fmt_weight')) {
    function wst_dod_fmt_weight($v) {
        return number_format_i18n((float)$v, 2);
    }
}

if (!function_exists('wst_dod_label_status')) {
    function wst_dod_label_status($value) {
        $value = strtoupper(trim((string)$value));

        $labels = array(
            'PENDING' => 'Pending AutoCount',
            'PROCESSING' => 'Processing AutoCount',
            'SUCCESS' => 'Created',
            'FAILED' => 'AutoCount Failed',
            'FAILED_FINAL' => 'AutoCount Failed',
            'PENDING_DELIVERY' => 'Pending Delivery',
            'ASSIGNED' => 'Assigned',
            'DRIVER_ACKNOWLEDGED' => 'Driver Received',
            'RECEIVED' => 'Driver Received',
            'OUT_FOR_DELIVERY' => 'Out for Delivery',
            'DELIVERED' => 'Delivered',
            'NEEDS_STAFF_EDIT' => 'Needs Staff Edit',
            'EDIT_PENDING_AUTOCOUNT' => 'Edit Pending',
            'EDITED_IN_AUTOCOUNT' => 'Edited',
            'CANCELLED' => 'Cancelled',
            'ACTIVE' => 'Active',
        );

        return $labels[$value] ?? ($value !== '' ? ucwords(strtolower(str_replace('_', ' ', $value))) : '-');
    }
}

if (!function_exists('wst_dod_status_class')) {
    function wst_dod_status_class($value) {
        $value = strtoupper(trim((string)$value));

        if (in_array($value, array('DELIVERED', 'SUCCESS', 'EDITED_IN_AUTOCOUNT'), true)) {
            return 'wst-dod-badge-good';
        }

        if (in_array($value, array('FAILED', 'CANCELLED'), true)) {
            return 'wst-dod-badge-danger';
        }

        if (in_array($value, array('PENDING', 'EDIT_PENDING_AUTOCOUNT', 'NEEDS_STAFF_EDIT'), true)) {
            return 'wst-dod-badge-warn';
        }

        return 'wst-dod-badge-info';
    }
}

if (!function_exists('wst_dod_status_help')) {
    function wst_dod_status_help($value) {
        $value = strtoupper(trim((string)$value));

        $help = array(
            'PENDING' => 'Order is waiting for AutoCount bridge processing.',
            'PROCESSING' => 'AutoCount bridge is currently processing this order.',
            'SUCCESS' => 'Order was created successfully in AutoCount.',
            'FAILED' => 'AutoCount bridge failed to create or update this order.',
            'FAILED_FINAL' => 'AutoCount bridge failed after all retries.',
            'PENDING_DELIVERY' => 'Order exists but has not been assigned to a driver yet.',
            'ASSIGNED' => 'Order has been assigned to a driver.',
            'DRIVER_ACKNOWLEDGED' => 'Driver confirmed receiving the delivery list or goods.',
            'RECEIVED' => 'Driver confirmed receiving the delivery list or goods.',
            'OUT_FOR_DELIVERY' => 'Driver is currently delivering this order.',
            'DELIVERED' => 'Driver marked this order as delivered.',
            'NEEDS_STAFF_EDIT' => 'Driver reported not enough item. Staff should edit and reprint this DO.',
            'EDIT_PENDING_AUTOCOUNT' => 'Staff edited this order and the AutoCount update is still pending.',
            'EDITED_IN_AUTOCOUNT' => 'The edited order was updated successfully in AutoCount.',
            'CANCELLED' => 'This delivery order was cancelled.',
            'ACTIVE' => 'This delivery order is active in AutoCount.',
        );

        return $help[$value] ?? 'Current delivery order status.';
    }
}

if (!function_exists('wst_dod_wp_table_exists')) {
    function wst_dod_wp_table_exists($table_name) {
        global $wpdb;

        if (!$wpdb) return false;

        return $wpdb->get_var(
            $wpdb->prepare('SHOW TABLES LIKE %s', $table_name)
        ) === $table_name;
    }
}

if (!function_exists('wst_dod_wp_table_columns')) {
    function wst_dod_wp_table_columns($table_name) {
        global $wpdb;

        static $cache = array();
        if (!$wpdb) return array();

        if (isset($cache[$table_name])) {
            return $cache[$table_name];
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table_name);
        $cols = $wpdb->get_col("SHOW COLUMNS FROM `{$safe_table}`", 0);

        $cache[$table_name] = is_array($cols) ? array_flip($cols) : array();

        return $cache[$table_name];
    }
}

if (!function_exists('wst_dod_get_proof_image_by_doc')) {
    function wst_dod_get_proof_image_by_doc($docNo, $docKey) {
        global $wpdb;

        if (!$wpdb) return '';

        $table = $wpdb->prefix . 'ac_do_proof_images';
        if (!wst_dod_wp_table_exists($table)) return '';

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $cols = wst_dod_wp_table_columns($table);

        $orWhere = array();
        $args = array();

        $docNo = trim((string)$docNo);
        $docKey = (int)$docKey;

        if ($docNo !== '' && isset($cols['doc_no'])) {
            $orWhere[] = 'doc_no = %s';
            $args[] = $docNo;
        }

        if ($docKey > 0 && isset($cols['doc_key'])) {
            $orWhere[] = 'doc_key = %d';
            $args[] = $docKey;
        }

        if (empty($orWhere) || !isset($cols['image_url'])) return '';

        $whereSql = '(' . implode(' OR ', $orWhere) . ')';

        if (isset($cols['proof_type'])) {
            $whereSql .= ' AND proof_type = %s';
            $args[] = 'DELIVERY_PROOF';
        }

        if (isset($cols['deleted_at'])) {
            $whereSql .= ' AND deleted_at IS NULL';
        }

        $orderCol = isset($cols['id']) ? 'id' : 'captured_at';

        $sql = "
            SELECT image_url
            FROM `{$safe_table}`
            WHERE {$whereSql}
            ORDER BY `{$orderCol}` DESC
            LIMIT 1
        ";

        $url = $wpdb->get_var($wpdb->prepare($sql, $args));

        return $url ? esc_url_raw((string)$url) : '';
    }
}

if (!function_exists('wst_dod_read_json_array')) {
    function wst_dod_read_json_array($json) {
        $data = json_decode((string)$json, true);
        return is_array($data) ? $data : array();
    }
}

if (!function_exists('wst_dod_doc_key_from_data')) {
    function wst_dod_doc_key_from_data($data) {
        if (!is_array($data)) return 0;

        foreach (array('docKey', 'DocKey', 'dockey') as $key) {
            if (isset($data[$key]) && is_numeric($data[$key])) {
                return (int)$data[$key];
            }
        }

        return 0;
    }
}

if (!function_exists('wst_dod_doc_no_from_data')) {
    function wst_dod_doc_no_from_data($data) {
        if (!is_array($data)) return '';

        foreach (array('docNo', 'DocNo', 'docno', 'sourceDocNo', 'oldDocNo', 'originalDocNo') as $key) {
            if (!empty($data[$key])) {
                return strtoupper(trim((string)$data[$key]));
            }
        }

        return '';
    }
}

if (!function_exists('wst_dod_driver_label_from_job')) {
    function wst_dod_driver_label_from_job($job, $payload) {
        $driver_id = (int)($job['assigned_driver_id'] ?? 0);

        if ($driver_id > 0) {
            $user = get_userdata($driver_id);
            if ($user) {
                $display = trim((string)$user->display_name);
                return $display !== '' ? $display : (string)$user->user_login;
            }
        }

        foreach (array('assignedDriverName', 'driverName', 'assignedDriverLogin', 'driverLogin') as $key) {
            if (!empty($payload[$key])) {
                return trim((string)$payload[$key]);
            }
        }

        return '';
    }
}

if (!function_exists('wst_dod_display_status_from_job')) {
    function wst_dod_display_status_from_job($syncStatus, $deliveryStatus, $jobSubtype, $assignedDriverId) {
        $syncStatus = strtoupper(trim((string)$syncStatus));
        $deliveryStatus = strtoupper(trim((string)$deliveryStatus));
        $jobSubtype = strtoupper(trim((string)$jobSubtype));
        $assignedDriverId = (int)$assignedDriverId;

        if (in_array($syncStatus, array('PENDING', 'PROCESSING', 'FAILED', 'FAILED_FINAL'), true)) {
            return $syncStatus;
        }

        if (
            in_array($jobSubtype, array('EDIT', 'UPDATE'), true)
            && $syncStatus === 'SUCCESS'
            && $deliveryStatus === 'EDIT_PENDING_AUTOCOUNT'
        ) {
            return $assignedDriverId > 0 ? 'ASSIGNED' : 'EDITED_IN_AUTOCOUNT';
        }

        return $deliveryStatus !== '' ? $deliveryStatus : $syncStatus;
    }
}

if (!function_exists('wst_dod_pick_payload_value')) {
    function wst_dod_pick_payload_value($payload, $keys, $fallback = '') {
        if (!is_array($payload)) return $fallback;

        foreach ((array)$keys as $key) {
            if (isset($payload[$key]) && trim((string)$payload[$key]) !== '') {
                return trim((string)$payload[$key]);
            }
        }

        return $fallback;
    }
}

if (!function_exists('wst_dod_item_from_payload_line')) {
    function wst_dod_item_from_payload_line($line) {
        $line = is_array($line) ? $line : array();

        return array(
            'itemCode' => wst_dod_pick_payload_value($line, array('itemCode', 'ItemCode', 'code'), ''),
            'name' => wst_dod_pick_payload_value($line, array('description', 'Description', 'itemName', 'name'), ''),
            'qty' => (float)($line['qty'] ?? $line['Qty'] ?? 0),
            'basket' => (float)($line['basketQty'] ?? $line['basket'] ?? $line['Basket'] ?? $line['UDF_BASKET'] ?? 0),
            'carton' => (float)($line['cartonQty'] ?? $line['carton'] ?? $line['Carton'] ?? $line['UDF_CARTON'] ?? 0),
            'weightKg' => (float)($line['kg'] ?? $line['weightKg'] ?? $line['WeightKG'] ?? $line['UDF_WEIGHTKG'] ?? 0),
        );
    }
}

if (!function_exists('wst_dod_pending_row_from_job')) {
    function wst_dod_pending_row_from_job($job, $payload, $item) {
        $lines = isset($payload['lines']) && is_array($payload['lines']) ? $payload['lines'] : array();
        $items = array_map('wst_dod_item_from_payload_line', $lines);
        $totalBasket = 0.0;
        $totalCarton = 0.0;

        foreach ($items as $line) {
            $totalBasket += (float)($line['basket'] ?? 0);
            $totalCarton += (float)($line['carton'] ?? 0);
        }

        $docDate = wst_dod_pick_payload_value($payload, array('docDate', 'DocDate'), '');
        if ($docDate === '') {
            $docDate = substr((string)($job['created_at'] ?? ''), 0, 10);
        }

        return array(
            'docKey' => 0,
            'docNo' => '',
            'docDate' => $docDate,
            'debtorCode' => wst_dod_pick_payload_value($payload, array('customerCode', 'debtorCode', 'DebtorCode'), ''),
            'debtorName' => wst_dod_pick_payload_value($payload, array('customerName', 'debtorName', 'DebtorName'), ''),
            'autoCountStatus' => (string)($item['syncStatus'] ?? ''),
            'displayStatus' => (string)($item['displayStatus'] ?? ''),
            'driver' => (string)($item['driver'] ?? ''),
            'jobId' => (int)($item['jobId'] ?? 0),
            'jobSubtype' => (string)($item['jobSubtype'] ?? ''),
            'syncStatus' => (string)($item['syncStatus'] ?? ''),
            'deliveryStatus' => (string)($item['deliveryStatus'] ?? ''),
            'jobError' => (string)($item['errorMessage'] ?? ''),
            'createdAt' => wst_dod_datetime($job['created_at'] ?? ''),
            'lastModified' => wst_dod_datetime($job['updated_at'] ?? ''),
            'totalBasket' => $totalBasket,
            'totalCarton' => $totalCarton,
            'items' => $items,
            'proofImage' => '',
            'hasAuthoritativeJob' => true,
            'isPendingJob' => true,
        );
    }
}

if (!function_exists('wst_dod_staff_edit_locked_statuses')) {
    function wst_dod_staff_edit_locked_statuses() {
        return array(
            'DRIVER_ACKNOWLEDGED',
            'RECEIVED',
            'OUT_FOR_DELIVERY',
            'DELIVERED',
        );
    }
}

if (!function_exists('wst_dod_can_staff_edit_row')) {
    function wst_dod_can_staff_edit_row($row) {
        if (!empty($row['isPendingJob'])) return false;
        if (empty($row['hasAuthoritativeJob'])) return false;
        if ((int)($row['docKey'] ?? 0) <= 0) return false;
        if (trim((string)($row['docNo'] ?? '')) === '') return false;

        $locked_statuses = wst_dod_staff_edit_locked_statuses();
        $display_status = strtoupper(trim((string)($row['displayStatus'] ?? '')));
        $delivery_status = strtoupper(trim((string)($row['deliveryStatus'] ?? '')));

        return !in_array($display_status, $locked_statuses, true)
            && !in_array($delivery_status, $locked_statuses, true);
    }
}

if (!function_exists('wst_dod_staff_edit_disabled_reason')) {
    function wst_dod_staff_edit_disabled_reason($row) {
        if (!empty($row['isPendi)hOj?X�QE��������)h�}
??�FngJob'])) {
            return 'AutoCount is still processing this order.';
        }

        if (empty($row['hasAuthoritativeJob'])) {
            return 'This order has no authoritative WordPress delivery status record.';
        }

        if ((int)($row['docKey'] ?? 0) <= 0 || trim((string)($row['docNo'] ?? '')) === '') {
            return 'This order is missing the AutoCount document reference.';
        }

        return 'Driver already confirmed receiving this order, so staff editing is locked.';
    }
}

if (!function_exists('wst_dod_load_job_map')) {
    function wst_dod_load_job_map($customer = '', $status = 'ALL', $dateFrom = '', $dateTo = '') {
        global $wpdb;

        if (!$wpdb) return array('byDocNo' => array(), 'byDocKey' => array(), 'pendingRows' => array());

        $table = $wpdb->prefix . 'ac_jobs';
        if (!wst_dod_wp_table_exists($table)) {
            return array('byDocNo' => array(), 'byDocKey' => array(), 'pendingRows' => array());
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $cols = wst_dod_wp_table_columns($table);

        $select = array('id', 'payload', 'result', 'status', 'created_at', 'updated_at');
        foreach (array('job_subtype', 'delivery_status', 'assigned_driver_id', 'completed_at', 'error_message', 'source_doc_no', 'source_doc_key') as $col) {
            if (isset($cols[$col])) {
                $select[] = $col;
            }
        }

        $rows = $wpdb->get_results(
            "SELECT " . implode(',', array_map(function($c) { return "`{$c}`"; }, $select)) . "
             FROM `{$safe_table}`
             WHERE job_type = 'DELIVERY_ORDER'
             ORDER BY id DESC
             LIMIT 800",
            ARRAY_A
        );

        $map = array('byDocNo' => array(), 'byDocKey' => array(), 'pendingRows' => array());

        foreach ((array)$rows as $job) {
            $payload = wst_dod_read_json_array($job['payload'] ?? '');
            $result = wst_dod_read_json_array($job['result'] ?? '');

            $docNo = isset($job['source_doc_no']) ? strtoupper(trim((string)$job['source_doc_no'])) : '';
            if ($docNo === '') {
                $docNo = wst_dod_doc_no_from_data($result);
            }
            if ($docNo === '') {
                $docNo = wst_dod_doc_no_from_data($payload);
            }

            $docKey = isset($job['source_doc_key']) ? (int)$job['source_doc_key'] : 0;
            if ($docKey <= 0) {
                $docKey = wst_dod_doc_key_from_data($result);
            }
            if ($docKey <= 0) {
                $docKey = wst_dod_doc_key_from_data($payload);
            }

            $deliveryStatus = strtoupper(trim((string)($job['delivery_status'] ?? '')));
            $syncStatus = strtoupper(trim((string)($job['status'] ?? '')));
            $jobSubtype = strtoupper(trim((string)($job['job_subtype'] ?? '')));
            $assignedDriverId = (int)($job['assigned_driver_id'] ?? 0);
            $displayStatus = wst_dod_display_status_from_job($syncStatus, $deliveryStatus, $jobSubtype, $assignedDriverId);

            if (
                in_array($jobSubtype, array('EDIT', 'UPDATE'), true)
                && $syncStatus === 'SUCCESS'
                && $deliveryStatus === 'EDIT_PENDING_AUTOCOUNT'
            ) {
                $displayStatus = $assignedDriverId > 0 ? 'ASSIGNED' : 'EDITED_IN_AUTOCOUNT';

                if (isset($cols['delivery_status'])) {
                    $repair_update = array('delivery_status' => $displayStatus);
                    $repair_formats = array('%s');

                    if (isset($cols['updated_at'])) {
                        $repair_update['updated_at'] = current_time('mysql');
                        $repair_formats[] = '%s';
                    }

                    $wpdb->update(
                        $table,
                        $repair_update,
                        array('id' => (int)($job['id'] ?? 0)),
                        $repair_formats,
                        array('%d')
                    );

                    $deliveryStatus = $displayStatus;
                }
            }

            $item = array(
                'jobId' => (int)($job['id'] ?? 0),
                'jobSubtype' => $jobSubtype,
                'deliveryStatus' => $deliveryStatus,
                'syncStatus' => $syncStatus,
                'displayStatus' => $displayStatus,
                'driver' => wst_dod_driver_label_from_job($job, $payload),
                'createdAt' => (string)($job['created_at'] ?? ''),
                'updatedAt' => (string)($job['updated_at'] ?? ''),
                'completedAt' => (string)($job['completed_at'] ?? ''),
                'errorMessage' => (string)($job['error_message'] ?? ''),
            );

            if ($docNo === '' && $docKey <= 0) {
                $pendingRow = wst_dod_pending_row_from_job($job, $payload, $item);
                $haystack = strtoupper(implode(' ', array(
                    $pendingRow['debtorCode'],
                    $pendingRow['debtorName'],
                    'JOB-' . $pendingRow['jobId'],
                )));
                $pendingDate = substr((string)$pendingRow['docDate'], 0, 10);
                $wantedStatus = strtoupper(trim((string)$status));

                if (
                    ($customer === '' || strpos($haystack, strtoupper($customer)) !== false)
                    && ($dateFrom === '' || $pendingDate >= $dateFrom)
                    && ($dateTo === '' || $pendingDate <= $dateTo)
                    && (
                        $wantedStatus === 'ALL'
                        || strtoupper($pendingRow['displayStatus']) === $wantedStatus
                        || strtoupper($pendingRow['autoCountStatus']) === $wantedStatus
                        || strtoupper($pendingRow['syncStatus']) === $wantedStatus
                        || strtoupper($pendingRow['deliveryStatus']) === $wantedStatus
                    )
                ) {
                    $map['pendingRows'][] = $pendingRow;
                }

                continue;
            }

            if ($docNo !== '' && !isset($map['byDocNo'][$docNo])) {
                $map['byDocNo'][$docNo] = $item;
            }

            if ($docKey > 0 && !isset($map['byDocKey'][$docKey])) {
                $map['byDocKey'][$docKey] = $item;
            }
        }

        return $map;
    }
}

$customer = isset($_GET['customer']) ? trim(sanitize_text_field(wp_unslash($_GET['customer']))) : '';
$status = isset($_GET['status']) ? strtoupper(trim(sanitize_text_field(wp_unslash($_GET['status'])))) : 'ALL';
$limit_input = isset($_GET['limit']) ? (int)$_GET['limit'] : 25;
$allowed_limits = array(25, 50, 100);
$limit = in_array($limit_input, $allowed_limits, true) ? $limit_input : 25;
$status_options = array(
    'ALL' => 'All',
    'PENDING' => 'Pending AutoCount',
    'SUCCESS' => 'Created',
    'FAILED' => 'AutoCount Failed',
    'PENDING_DELIVERY' => 'Pending Delivery',
    'ASSIGNED' => 'Assigned',
    'DRIVER_ACKNOWLEDGED' => 'Driver Received',
    'OUT_FOR_DELIVERY' => 'Out for Delivery',
    'DELIVERED' => 'Delivered',
    'NEEDS_STAFF_EDIT' => 'Needs Staff Edit',
    'EDIT_PENDING_AUTOCOUNT' => 'Edit Pending',
    'EDITED_IN_AUTOCOUNT' => 'Edited',
    'ACTIVE' => 'Active',
    'CANCELLED' => 'Cancelled',
);

if (!isset($status_options[$status])) {
    $status = 'ALL';
}

$todayObj = new DateTime('now', wp_timezone());
$defaultDateTo = $todayObj->format('Y-m-d');

$fromObj = clone $todayObj;
$fromObj->modify('-1 month');
$defaultDateFrom = $fromObj->format('Y-m-d');

$dateFromRaw = isset($_GET['dateFrom']) ? wp_unslash($_GET['dateFrom']) : '';
$dateToRaw = isset($_GET['dateTo']) ? wp_unslash($_GET['dateTo']) : '';
$dateFrom = wst_dod_valid_date(sanitize_text_field($dateFromRaw), $defaultDateFrom);
$dateTo = wst_dod_valid_date(sanitize_text_field($dateToRaw), $defaultDateTo);

$where = array();
$params = array();

if ($customer !== '') {
    $where[] = "(DOH.DebtorCode LIKE ? OR DOH.DebtorName LIKE ? OR DOH.DocNo LIKE ?)";
    $like = '%' . str_replace(array('%', '_', '['), array('[%]', '[_]', '[[]'), $customer) . '%';
    $params[] = $like;
    $params[] = $like;
    $params[] = $like;
}

$where[] = "CAST(DOH.DocDate AS date) >= ?";
$params[] = $dateFrom;

$where[] = "CAST(DOH.DocDate AS date) <= ?";
$params[] = $dateTo;

if ($status === 'ACTIVE') {
    $where[] = "ISNULL(DOH.Cancelled,'F') <> 'T'";
} elseif ($status === 'CANCELLED') {
    $where[] = "ISNULL(DOH.Cancelled,'F') = 'T'";
}

$whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';

$sql = "
    SELECT TOP ($limit)
        DOH.DocKey,
        DOH.DocNo,
        DOH.DocDate,
        DOH.DebtorCode,
        DOH.DebtorName,
        ISNULL(DOH.DocStatus,'') AS DocStatus,
        ISNULL(DOH.Cancelled,'F') AS Cancelled,
        DOH.CreatedTimeStamp,
        DOH.LastModified,
        ISNULL(DOH.UDF_SUMBASKET, 0) AS TotalBasket,
        ISNULL(DOH.UDF_SUMCARTON, 0) AS TotalCarton
    FROM dbo.DO AS DOH
    $whereSql
    ORDER BY DOH.DocDate DESC, DOH.DocKey DESC
";

$stmt = sqlsrv_query($conn, $sql, $params);

if ($stmt === false) {
    $err = wst_dod_errors();
    wst_dod_log_error($err);
    echo '<div class="wst-dod-alert wst-dod-alert-error">Failed to load Delivery Order records.'
        . ($show_technical_errors ? ' ' . esc_html($err) : '')
        . '</div>';
    return;
}

$jobMap = wst_dod_load_job_map($customer, $status, $dateFrom, $dateTo);
$rows = array();

while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
    $docKey = (int)($row['DocKey'] ?? 0);
    $docNo = (string)($row['DocNo'] ?? '');
    $docStatus = strtoupper(trim((string)($row['DocStatus'] ?? '')));
    $cancelled = strtoupper(trim((string)($row['Cancelled'] ?? 'F')));

    $autoCountStatus = ($cancelled === 'T')
        ? 'CANCELLED'
        : (($docStatus === '' || $docStatus === 'A') ? 'ACTIVE' : $docStatus);

    $job = array();
    $docNoKey = strtoupper(trim($docNo));
    if ($docNoKey !== '' && isset($jobMap['byDocNo'][$docNoKey])) {
        $job = $jobMap['byDocNo'][$docNoKey];
    } elseif ($docKey > 0 && isset($jobMap['byDocKey'][$docKey])) {
        $job = $jobMap['byDocKey'][$docKey];
    }

    $displayStatus = !empty($job['displayStatus']) ? $job['displayStatus'] : $autoCountStatus;

    $syncStatus = strtoupper(trim((string)($job['syncStatus'] ?? '')));
    $deliveryStatus = strtoupper(trim((string)($job['deliveryStatus'] ?? '')));

    if (
        $status !== 'ALL'
        && strtoupper($displayStatus) !== $status
        && strtoupper($autoCountStatus) !== $status
        && $syncStatus !== $status
        && $deliveryStatus !== $status
    ) {
        continue;
    }

    $rows[] = array(
        'docKey' => $docKey,
        'docNo' => $docNo,
        'docDate' => wst_dod_date($row['DocDate'] ?? null),
        'debtorCode' => (string)($row['DebtorCode'] ?? ''),
        'debtorName' => (string)($row['DebtorName'] ?? ''),
        'autoCountStatus' => $autoCountStatus,
        'displayStatus' => $displayStatus,
        'driver' => (string)($job['driver'] ?? ''),
        'jobId' => (int)($job['jobId'] ?? 0),
        'jobSubtype' => (string)($job['jobSubtype'] ?? ''),
        'syncStatus' => (string)($job['syncStatus'] ?? ''),
        'deliveryStatus' => (string)($job['deliveryStatus'] ?? ''),
        'jobError' => (string)($job['errorMessage'] ?? ''),
        'createdAt' => wst_dod_datetime($row['CreatedTimeStamp'] ?? null),
        'lastModified' => wst_dod_datetime($row['LastModified'] ?? null),
        'totalBasket' => (float)($row['TotalBasket'] ?? 0),
        'totalCarton' => (float)($row['TotalCarton'] ?? 0),
        'items' => array(),
        'proofImage' => wst_dod_get_proof_image_by_doc($docNo, $docKey),
        'hasAuthoritativeJob' => !empty($job),
    );
}

sqlsrv_free_stmt($stmt);

$rows = array_merge($jobMap['pendingRows'] ?? array(), $rows);
usort($rows, function($a, $b) {
    $aTime = strtotime((string)($a['lastModified'] ?? '')) ?: strtotime((string)($a['createdAt'] ?? '')) ?: strtotime((string)($a['docDate'] ?? '')) ?: 0;
    $bTime = strtotime((string)($b['lastModified'] ?? '')) ?: strtotime((string)($b['createdAt'] ?? '')) ?: strtotime((string)($b['docDate'] ?? '')) ?: 0;

    if ($aTime === $bTime) {
        return (int)($b['jobId'] ?? 0) <=> (int)($a['jobId'] ?? 0);
    }

    return $bTime <=> $aTime;
});

$detailWarning = '';

if (!empty($rows)) {
    $docKeys = array_values(array_filter(array_map(function($r) {
        return (int)$r['docKey'];
    }, $rows)));

    if (!empty($docKeys)) {
        $placeholders = implode(',', array_fill(0, count($docKeys), '?'));

        $detailSql = "
            SELECT
                DTL.DocKey,
                ISNULL(DTL.ItemCode, '') AS ItemCode,
                ISNULL(DTL.Description, '') AS Description,
                ISNULL(DTL.Qty, 0) AS Qty,
                ISNULL(DTL.UDF_BASKET, 0) AS Basket,
                ISNULL(DTL.UDF_CARTON, 0) AS Carton,
                ISNULL(DTL.UDF_WEIGHTKG, 0) AS WeightKG
            FROM dbo.DODTL AS DTL
            WHERE DTL.DocKey IN ($placeholders)
            ORDER BY DTL.DocKey ASC, ISNULL(DTL.Seq, 0) ASC, ISNULL(DTL.ItemCode, '') ASC
        ";

        $detailStmt = sqlsrv_query($conn, $detailSql, $docKeys);

        if ($detailStmt === false) {
            $detailWarning = wst_dod_errors();
            wst_dod_log_error($detailWarning);
        } else {
            $itemsByDocKey = array();

            while ($d = sqlsrv_fetch_array($detailStmt, SQLSRV_FETCH_ASSOC)) {
                $docKey = (int)($d['DocKey'] ?? 0);

                if (!isset($itemsByDocKey[$docKey])) {
                    $itemsByDocKey[$docKey] = array();
                }

                $itemsByDocKey[$docKey][] = array(
                    'itemCode' => (string)($d['ItemCode'] ?? ''),
                    'name' => (string)($d['Description'] ?? ''),
                    'qty' => (float)($d['Qty'] ?? 0),
                    'basket' => (float)($d['Basket'] ?? 0),
                    'carton' => (float)($d['Carton'] ?? 0),
                    'weightKg' => (float)($d['WeightKG'] ?? 0),
                );
            }

            sqlsrv_free_stmt($detailStmt);

            foreach ($rows as $idx => $r) {
                if ((int)($r['docKey'] ?? 0) > 0) {
                    $rows[$idx]['items'] = $itemsByDocKey[$r['docKey']] ?? array();
                }
            }
        }
    }
}
?>

<div class="wst-dod-wrap">
    <div class="wst-dod-filter-card">
        <form method="get" class="wst-dod-form">
            <div class="wst-dod-field wst-dod-search-field">
                <label class="wst-dod-label" for="wstDodCustomer">Customer / Doc No</label>
                <input id="wstDodCustomer" name="customer" class="wst-dod-input" type="search" value="<?php echo esc_attr($customer); ?>" placeholder="Search customer or DO no..." autocomplete="off">
            </div>

            <div class="wst-dod-filter-grid">
                <div class="wst-dod-field">
                    <label class="wst-dod-label" for="wstDodStatus">Status</label>
                    <select id="wstDodStatus" name="status" class="wst-dod-input">
                        <?php foreach ($status_options as $status_value => $status_label): ?>
                            <option value="<?php echo esc_attr($status_value); ?>" <?php selected($status, $status_value); ?>>
                                <?php echo esc_html($status_label); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                </div>

                <div class="wst-dod-field">
                    <label class="wst-dod-label" for="wstDodDateFrom">From</label>
                    <input id="wstDodDateFrom" name="dateFrom" class="wst-dod-input" t)h�}�w�RF��������)h��
??�hype="date" value="<?php echo esc_attr($dateFrom); ?>">
                </div>

                <div class="wst-dod-field">
                    <label class="wst-dod-label" for="wstDodDateTo">To</label>
                    <input id="wstDodDateTo" name="dateTo" class="wst-dod-input" type="date" value="<?php echo esc_attr($dateTo); ?>">
                </div>

                <div class="wst-dod-field">
                    <label class="wst-dod-label" for="wstDodLimit">Rows</label>
                    <select id="wstDodLimit" name="limit" class="wst-dod-input">
                        <?php foreach ($allowed_limits as $allowed_limit): ?>
                            <option value="<?php echo esc_attr($allowed_limit); ?>" <?php selected($limit, $allowed_limit); ?>>
                                <?php echo esc_html($allowed_limit); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                </div>
            </div>

            <button type="submit" class="wst-dod-btn wst-dod-btn-primary">Search</button>
        </form>
    </div>

    <div class="wst-dod-summary">
        <span>Showing <?php echo esc_html(number_format_i18n(count($rows))); ?> rows</span>
    </div>

    <?php if ($detailWarning !== ''): ?>
        <div class="wst-dod-alert wst-dod-alert-warning">
            Item detail could not be loaded.
            <?php if ($show_technical_errors): ?>
                <?php echo esc_html($detailWarning); ?>
            <?php endif; ?>
        </div>
    <?php endif; ?>

    <div class="wst-dod-table-card">
        <div class="wst-dod-table-scroll">
            <table class="wst-dod-table">
                <thead>
                    <tr>
                        <th class="wst-dod-col-date">Date</th>
                        <th class="wst-dod-col-doc">Doc No</th>
                        <th class="wst-dod-col-customer">Customer</th>
                        <th class="wst-dod-col-driver">Driver</th>
                        <th class="wst-dod-col-status">Status</th>
                        <th class="wst-dod-col-summary">Bsk / Ctn</th>
                        <th class="wst-dod-col-items">Items</th>
                        <th class="wst-dod-col-action">Actions</th>
                    </tr>
                </thead>

                <tbody>
                    <?php if (empty($rows)): ?>
                        <tr>
                            <td colspan="8" class="wst-dod-empty">No matching delivery order records.</td>
                        </tr>
                    <?php else: ?>
                        <?php foreach ($rows as $r): ?>
                            <?php
                            $edit_url = add_query_arg(
                                array(
                                    'docNo' => $r['docNo'],
                                    'docKey' => (int)$r['docKey'],
                                ),
                                $edit_page_url
                            );
                            $view_args = !empty($r['isPendingJob'])
                                ? array('job_id' => (int)$r['jobId'])
                                : array('docNo' => $r['docNo'], 'docKey' => (int)$r['docKey']);
                            $view_url = add_query_arg($view_args, $view_page_url);
                            $badgeClass = wst_dod_status_class($r['displayStatus']);
                            $canEdit = wst_dod_can_staff_edit_row($r);
                            $editDisabledReason = $canEdit ? '' : wst_dod_staff_edit_disabled_reason($r);
                            ?>

                            <tr class="wst-dod-main-row">
                                <td class="wst-dod-date">
                                    <div class="wst-dod-date-main"><?php echo esc_html($r['docDate'] ?: '-'); ?></div>
                                    <div class="wst-dod-date-sub">Created: <?php echo esc_html($r['createdAt'] ?: '-'); ?></div>
                                    <div class="wst-dod-date-sub">Updated: <?php echo esc_html($r['lastModified'] ?: '-'); ?></div>
                                </td>

                                <td class="wst-dod-docno">
                                    <?php if (!empty($r['isPendingJob'])): ?>
                                        <span class="wst-dod-muted">JOB-<?php echo esc_html((int)$r['jobId']); ?></span>
                                    <?php else: ?>
                                        <?php echo esc_html($r['docNo'] ?: '-'); ?>
                                    <?php endif; ?>
                                </td>

                                <td class="wst-dod-customer">
                                    <div class="wst-dod-customer-name"><?php echo esc_html($r['debtorName'] ?: '-'); ?></div>
                                    <div class="wst-dod-customer-code"><?php echo esc_html($r['debtorCode'] ?: '-'); ?></div>
                                </td>

                                <td class="wst-dod-driver">
                                    <?php echo esc_html(strtoupper($r['driver'] ?: 'Unassigned')); ?>
                                </td>

                                <td class="wst-dod-status">
                                    <span
                                        class="wst-dod-badge <?php echo esc_attr($badgeClass); ?>"
                                        data-status-help="<?php echo esc_attr(wst_dod_status_help($r['displayStatus'])); ?>"
                                    >
                                        <?php echo esc_html(wst_dod_label_status($r['displayStatus'])); ?>
                                    </span>
                                </td>

                                <td class="wst-dod-summary-cell">
                                    <div>Bsk <strong><?php echo esc_html(wst_dod_fmt_qty($r['totalBasket'])); ?></strong></div>
                                    <div>Ctn <strong><?php echo esc_html(wst_dod_fmt_qty($r['totalCarton'])); ?></strong></div>
                                </td>

                                <td class="wst-dod-items">
                                    <?php if (empty($r['items'])): ?>
                                        <div class="wst-dod-muted">No item detail.</div>
                                    <?php else: ?>
                                        <?php foreach ($r['items'] as $item): ?>
                                            <div class="wst-dod-item">
                                                <div class="wst-dod-item-name">
                                                    <?php echo esc_html($item['name'] !== '' ? $item['name'] : ($item['itemCode'] ?: '-')); ?>
                                                </div>
                                                <div class="wst-dod-item-meta">
                                                    <?php echo esc_html($item['itemCode'] ?: '-'); ?>
                                                    | Qty <?php echo esc_html(wst_dod_fmt_qty($item['qty'])); ?>
                                                    | Basket <?php echo esc_html(wst_dod_fmt_qty($item['basket'])); ?>
                                                    | Carton <?php echo esc_html(wst_dod_fmt_qty($item['carton'])); ?>
                                                    | KG <?php echo esc_html(wst_dod_fmt_weight($item['weightKg'])); ?>
                                                </div>
                                            </div>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                </td>

                                <td class="wst-dod-action">
                                    <a class="wst-dod-action-btn wst-dod-action-view" href="<?php echo esc_url($view_url); ?>">
                                        View
                                    </a>
                                    <?php if ($canEdit): ?>
                                        <a class="wst-dod-action-btn wst-dod-action-edit" href="<?php echo esc_url($edit_url); ?>">
                                            Edit
                                        </a>
                                    <?php else: ?>
                                        <span
                                            class="wst-dod-action-btn wst-dod-action-disabled"
                                            title="<?php echo esc_attr($editDisabledReason); ?>"
                                            aria-label="<?php echo esc_attr($editDisabledReason); ?>"
                                        >Edit</span>
                                    <?php endif; ?>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                    <?php endif; ?>
                </tbody>
            </table>
        </div>
    </div>
</div>

<style>
.wst-dod-wrap{
    --dod-green:#166534;
    --dod-green-dark:#14532d;
    --dod-line:#e5e7eb;
    --dod-text:#0f172a;
    --dod-muted:#64748b;
    width:100%;
    max-width:100%;
    margin:0 auto;
    padding:6px;
    box-sizing:border-box;
    font-family:"Segoe UI", Roboto, Arial, sans-serif;
    color:var(--dod-text);
    background:#f4faf5;
}

.wst-dod-alert{
    padding:12px 14px;
    border-radius:8px;
    margin:8px 0;
    font-size:14px;
    font-weight:700;
}

.wst-dod-alert-error{
    border:1px solid #fecaca;
    background:#fff1f2;
    color:#991b1b;
}

.wst-dod-alert-warning{
    border:1px solid #fed7aa;
    background:#fff7ed;
    color:#9a3412;
}

.wst-dod-filter-card,
.wst-dod-table-card{
    background:#fff;
    border:1px solid var(--dod-line);
    border-radius:8px;
    padding:8px;
    margin-bottom:8px;
    box-sizing:border-box;
}

.wst-dod-form{
    display:flex;
    flex-direction:column;
    gap:8px;
}

.wst-dod-filter-grid{
    display:grid;
    grid-template-columns:repeat(4, minmax(0, 1fr));
    gap:7px;
}

.wst-dod-field{
    min-width:0;
    display:flex;
    flex-direction:column;
    gap:4px;
}

.wst-dod-label{
    font-size:13px;
    line-height:1.1;
    font-weight:800;
    color:#334155;
}

.wst-dod-input{
    width:100%;
    min-height:38px;
    border:1px solid #cbd5e1;
    border-radius:6px;
    padding:7px 9px;
    font-size:14px;
    color:var(--dod-text);
    background:#fff;
    box-sizing:border-box;
}

.wst-dod-input:focus{
    outline:none;
    border-color:var(--dod-green);
    box-shadow:0 0 0 3px rgba(22,101,52,.12);
}

.wst-dod-btn{
    min-height:40px;
    border:none;
    border-radius:8px;
    padding:9px 14px;
    font-size:14px;
    font-weight:900;
    cursor:pointer;
}

.wst-dod-btn-primary{
    background:var(--dod-green);
    color:#fff;
}

.wst-dod-btn-primary:hover{
    background:var(--dod-green-dark);
}

.wst-dod-summary{
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap:10px;
    margin:0 0 8px;
    color:#334155;
    font-size:13px;
    font-weight:800;
}

.wst-dod-table-card{
    padding:0;
    overflow:hidden;
}

.wst-dod-table-scroll{
    display:block;
    width:100%;
    max-width:100%;
    overflow-x:auto;
    overflow-y:hidden;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:thin;
    scrollbar-color:#94a3b8 #e5e7eb;
}

.wst-dod-table-scroll::-webkit-scrollbar{
    height:12px;
}

.wst-dod-table-scroll::-webkit-scrollbar-thumb{
    background:#94a3b8;
    border-radius:999px;
}

.wst-dod-table-scroll::-webkit-scrollbar-track{
    background:#e5e7eb;
    border-radius:999px;
}

.wst-dod-table{
    width:100%;
    min-width:1120px;
    border-collapse:collapse;
    table-layout:fixed;
    background:#fff;
}

.wst-dod-table th{
    background:#f8fafc;
    color:#334155;
    font-size:12px;
    font-weight:900;
    text-align:left;
    padding:8px 7px;
    border-bottom:1px solid var(--dod-line);
    white-space:nowrap;
}

.wst-dod-table td{
    padding:8px 7px;
    vertical-align:top;
    color:var(--dod-text);
    font-size:13px;
    line-height:1.25;
}

.wst-dod-table tbody tr{
    box-shadow:inset 0 -1px 0 #edf2f7;
}

.wst-dod-table tbody tr:nth-child(odd){
    background:#ffffff;
}

.wst-dod-table tbody tr:nth-child(even){
    background:#f1f8f3;
}

.wst-dod-table tbody tr:hover{
    background:#e8f5ec;
}

.wst-dod-col-date{width:13%;}
.wst-dod-col-doc{width:10%;}
.wst-dod-col-customer{width:20%;}
.wst-dod-col-driver{width:11%;}
.wst-dod-col-status{width:11%;}
.wst-dod-col-summary{width:7%;}
.wst-dod-col-items{width:20%;}
.wst-dod-col-action{width:8%;}

.wst-dod-docno{
    font-weight:900;
    white-space:nowrap;
}

.wst-dod-date{
    white-space:normal;
}

.wst-dod-date-main{
    font-weight:900;
    white-space:nowrap;
}

.wst-dod-date-sub{
    margin-top:2px;
    color:#64748b;
    font-size:11px;
    font-weight:800;
    line-height:1.25;
    word-break:break-word;
}

.wst-dod-customer-name{
    font-size:14px;
    font-weight:900;
    line-height:1.15;
    color:#020617;
    word-break:break-word;
}

.wst-dod-customer-code{
    margin-top:2px;
    color:#64748b;
    font-size:11px;
    font-weight:800;
    word-break:break-word;
}

.wst-dod-driver{
    font-weight:800;
    word-break:break-word;
    text-transform:uppercase;
}

.wst-dod-summary-cell{
    white-space:nowrap;
    font-size:12px;
}

.wst-dod-summary-cell strong{
    font-weight:900;
}

.wst-dod-badge{
    position:relative;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border-radius:999px;
    border:1px solid;
    padding:3px 7px;
    max-width:100%;
    font-size:10px;
    line-height:1.1;
    font-weight:900;
    text-transform:uppercase;
    white-space:normal;
}

.wst-dod-badge:hover::after,
.wst-dod-badge:focus::after{
    content:attr(data-status-help);
    position:absolute;
    left:0;
    top:calc(100% + 7px);
    z-index:5;
    width:220px;
    padding:8px 10px;
    border:1px solid #cbd5e1;
    border-radius:8px;
    background:#0f172a;
    color:#fff;
    font-size:12px;
    font-weight:800;
    line-height:1.35;
    text-transform:none;
    white-space:normal;
    box-shadow:0 12px 24px rgba(15,23,42,.2);
}

.wst-dod-badge-good{
    color:#166534;
    background:#dcfce7;
    border-color:#86efac;
}

.wst-dod-badge-info{
    color:#075985;
    background:#e0f2fe;
    border-color:#7dd3fc;
}

.wst-dod-badge-warn{
    color:#92400e;
    background:#fef3c7;
    border-color:#fbbf24;
}

.wst-dod-badge-danger{
    color:#9f1239;
    background:#ffe4e6;
    border-color:#fda4af;
}

.wst-dod-action{
    white-space:nowrap;
    vertical-align:top;
}

.wst-dod-action .wst-dod-action-btn + .wst-dod-action-btn{
    margin-left:1rem;
}

.wst-dod-action-btn{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    min-height:30px;
    padding:6px 7px;
    border-radius:6px;
    border:1px solid;
    font-size:11px;
    font-weight:900;
    line-height:1;
    text-decoration:none;
    flex:0 0 auto;
}

.wst-dod-action-view{
    background:#f8fafc;
    border-color:#cbd5e1;
    color:#334155;
}

.wst-dod-action-edit{
    background:#f0fdf4;
    border-color:#86efac;
    color:#166534;
}

.wst-dod-action-disabled{
    background:#f8fafc;
    border-color:#e2e8f0;
    color:#94a3b8;
    cursor:not-allowed;
}

.wst-dod-action-btn:hover{
    filter:brightness(.97);
}

.wst-dod-item{
    padding:0 0 6px;
    margin-bottom:6px;
}

.wst-dod-item:last-child{
    border-bottom:0;
    margin-bottom:0;
    padding-bottom:0;
}

.wst-dod-item-name{
    font-size:13px;
    font-weight:900;
    line-height:1.2;
    color:#020617;
    text-transform:uppercase;
}

.wst-dod-item-meta{
    margin-top:2)h��۶��G���������E�'
??�P<?php
if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to view Delivery Order records.</div>';
    return;
}

if (!function_exists('get_mssql')) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Function get_mssql() not found.</div>';
    return;
}

$conn = get_mssql();
if (!$conn) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Failed to connect to MSSQL.</div>';
    return;
}

function ac_do_mobile_dt($v) {
    if ($v instanceof DateTime) return $v->format('Y-m-d');
    if (is_string($v) && $v !== '') {
        $ts = strtotime($v);
        if ($ts) return date('Y-m-d', $ts);
        return $v;
    }
    return '';
}

function ac_do_mobile_errors() {
    if (!function_exists('sqlsrv_errors')) return 'Unknown SQLSRV error.';
    $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
    if (empty($errors)) return 'Unknown SQLSRV error.';

    $out = array();
    foreach ($errors as $e) {
        $out[] = '[' . ($e['code'] ?? '') . '] ' . ($e['message'] ?? '');
    }
    return implode(' | ', $out);
}

function ac_do_mobile_fmt($v, $decimals = 2) {
    $n = (float)$v;
    if (abs($n - round($n)) < 0.00001) {
        return number_format_i18n($n, 0);
    }
    return number_format_i18n($n, $decimals);
}

function ac_do_mobile_fmt_weight($v) {
    return number_format_i18n((float)$v, 2);
}

$q        = trim(wp_unslash($_GET['q'] ?? ''));
$status   = strtoupper(trim(wp_unslash($_GET['status'] ?? 'ALL')));
$dateFrom = trim(wp_unslash($_GET['dateFrom'] ?? ''));
$dateTo   = trim(wp_unslash($_GET['dateTo'] ?? ''));
$limit    = max(1, min(300, intval($_GET['limit'] ?? 50)));

$where  = array();
$params = array();

if ($q !== '') {
    $where[] = "DOH.DebtorName LIKE ?";
    $params[] = '%' . $q . '%';
}

if ($dateFrom !== '') {
    $where[] = "CAST(DOH.DocDate AS date) >= ?";
    $params[] = $dateFrom;
}

if ($dateTo !== '') {
    $where[] = "CAST(DOH.DocDate AS date) <= ?";
    $params[] = $dateTo;
}

if ($status === 'ACTIVE') {
    $where[] = "ISNULL(DOH.Cancelled,'F') <> 'T'";
} elseif ($status === 'CANCELLED') {
    $where[] = "ISNULL(DOH.Cancelled,'F') = 'T'";
}

$whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';

$sql = "
    SELECT TOP ($limit)
        DOH.DocKey,
        DOH.DocNo,
        DOH.DocDate,
        DOH.DebtorCode,
        DOH.DebtorName,
        ISNULL(DOH.DocStatus,'') AS DocStatus,
        ISNULL(DOH.Cancelled,'F') AS Cancelled,
        DOH.CreatedTimeStamp,
        DOH.LastModified,
        ISNULL(DOH.UDF_SUMBASKET, 0) AS TotalBasket,
        ISNULL(DOH.UDF_SUMCARTON, 0) AS TotalCarton
    FROM dbo.DO AS DOH
    $whereSql
    ORDER BY DOH.DocDate DESC, DOH.DocNo DESC
";

$stmt = sqlsrv_query($conn, $sql, $params);

if ($stmt === false) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">'
        . esc_html(ac_do_mobile_errors())
        . '</div>';
    return;
}

$rows = array();
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
    $docStatus = strtoupper(trim((string)($row['DocStatus'] ?? '')));
    $cancelled = strtoupper(trim((string)($row['Cancelled'] ?? 'F')));
    $statusText = ($cancelled === 'T') ? 'CANCELLED' : (($docStatus === '' || $docStatus === 'A') ? 'ACTIVE' : $docStatus);

    $rows[] = array(
        'docKey'       => (int)($row['DocKey'] ?? 0),
        'docNo'        => (string)($row['DocNo'] ?? ''),
        'docDate'      => ($row['DocDate'] instanceof DateTime) ? $row['DocDate']->format('Y-m-d') : (string)($row['DocDate'] ?? ''),
        'debtorCode'   => (string)($row['DebtorCode'] ?? ''),
        'debtorName'   => (string)($row['DebtorName'] ?? ''),
        'status'       => $statusText,
        'createdAt'    => ac_do_mobile_dt($row['CreatedTimeStamp'] ?? null),
        'lastModified' => ac_do_mobile_dt($row['LastModified'] ?? null),
        'totalBasket'  => (float)($row['TotalBasket'] ?? 0),
        'totalCarton'  => (float)($row['TotalCarton'] ?? 0),
        'items'        => array(),
    );
}
sqlsrv_free_stmt($stmt);

if (!empty($rows)) {
    $docKeys = array_map(function($r){ return (int)$r['docKey']; }, $rows);
    $placeholders = implode(',', array_fill(0, count($docKeys), '?'));

    $detailSql = "
        SELECT
            DTL.DocKey,
            ISNULL(DTL.ItemCode, '') AS ItemCode,
            ISNULL(DTL.Description, '') AS Description,
            ISNULL(DTL.Qty, 0) AS Qty,
            ISNULL(DTL.UDF_BASKET, 0) AS Basket,
            ISNULL(DTL.UDF_CARTON, 0) AS Carton,
            ISNULL(DTL.UDF_WEIGHTKG, 0) AS WeightKG
        FROM dbo.DODTL AS DTL
        WHERE DTL.DocKey IN ($placeholders)
        ORDER BY DTL.DocKey ASC, ISNULL(DTL.ItemCode, '') ASC
    ";

    $detailStmt = sqlsrv_query($conn, $detailSql, $docKeys);

    if ($detailStmt !== false) {
        $itemsByDocKey = array();

        while ($d = sqlsrv_fetch_array($detailStmt, SQLSRV_FETCH_ASSOC)) {
            $docKey = (int)($d['DocKey'] ?? 0);

            if (!isset($itemsByDocKey[$docKey])) {
                $itemsByDocKey[$docKey] = array();
            }

            $itemsByDocKey[$docKey][] = array(
                'itemCode' => (string)($d['ItemCode'] ?? ''),
                'name'     => (string)($d['Description'] ?? ''),
                'qty'      => (float)($d['Qty'] ?? 0),
                'basket'   => (float)($d['Basket'] ?? 0),
                'carton'   => (float)($d['Carton'] ?? 0),
                'weightKg' => (float)($d['WeightKG'] ?? 0),
            );
        }
        sqlsrv_free_stmt($detailStmt);

        foreach ($rows as $idx => $r) {
            $rows[$idx]['items'] = $itemsByDocKey[$r['docKey']] ?? array();
        }
    }
}
?>

<div class="do-table-wrap">

    <div class="do-table-filter">
        <form method="get" class="do-table-form">
            <div class="do-table-field do-table-field-full">
                <label class="do-table-label">Customer Name</label>
                <input name="q" class="do-table-input" type="text" placeholder="Search customer name..." value="<?php echo esc_attr($q); ?>">
            </div>

            <div class="do-table-filter-row">
                <div class="do-table-field">
                    <label class="do-table-label">Status</label>
                    <select name="status" class="do-table-input">
                        <option value="ALL" <?php selected($status, 'ALL'); ?>>All</option>
                        <option value="ACTIVE" <?php selected($status, 'ACTIVE'); ?>>Active</option>
                        <option value="CANCELLED" <?php selected($status, 'CANCELLED'); ?>>Cancelled</option>
                    </select>
                </div>

                <div class="do-table-field">
                    <label class="do-table-label">From</label>
                    <input name="dateFrom" class="do-table-input" type="date" value="<?php echo esc_attr($dateFrom); ?>">
                </div>

                <div class="do-table-field">
                    <label class="do-table-label">To</label>
                    <input name="dateTo" class="do-table-input" type="date" value="<?php echo esc_attr($dateTo); ?>">
                </div>
            </div>

            <div class="do-table-actions">
                <button type="submit" class="do-table-btn">Search</button>
            </div>
        </form>
    </div>

    <div class="do-table-summary">
        <span>Total rows</span>
        <strong><?php echo number_format_i18n(count($rows)); ?></strong>
    </div>

    <?php if (empty($rows)): ?>
        <div class="do-table-empty">
            No matching delivery order records.
        </div>
    <?php else: ?>
        <div class="do-table-shell">
            <div class="do-table-scroll">
                <table class="do-table-grid">
                    <thead>
                        <tr>
                            <th>Customer</th>
                            <th>Status</th>
                            <th>Doc No</th>
                            <th>Date</th>
                            <th>Items</th>
                            <th>Created</th>
                            <th>Updated</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($rows as $r): ?>
                            <?php
                            $badgeClass = 'do-table-badge-default';
                            if ($r['status'] === 'ACTIVE') $badgeClass = 'do-table-badge-active';
                            if ($r['status'] === 'CANCELLED') $badgeClass = 'do-table-badge-cancelled';
                            ?>
                            <tr>
                                <td class="do-table-customer">
                                    <div class="do-table-name"><?php echo esc_html($r['debtorName'] ?: '-'); ?></div>
                                    <div class="do-table-code"><?php echo esc_html($r['debtorCode'] ?: '-'); ?></div>
                                </td>
                                <td>
                                    <span class="do-table-badge <?php echo esc_attr($badgeClass); ?>">
                                        <?php echo esc_html($r['status']); ?>
                                    </span>
                                </td>
                                <td class="do-table-strong"><?php echo esc_html($r['docNo'] ?: '-'); ?></td>
                                <td class="do-table-date"><?php echo esc_html($r['docDate'] ?: '-'); ?></td>
                                <td class="do-table-items">
                                    <?php if (empty($r['items'])): ?>
                                        <div class="do-table-muted">No item detail.</div>
                                    <?php else: ?>
                                        <?php foreach ($r['items'] as $item): ?>
                                            <div class="do-table-itemline">
                                                <div class="do-table-itemname">
                                                    <?php echo esc_html($item['name'] !== '' ? $item['name'] : ($item['itemCode'] ?: '-')); ?>
                                                </div>
                                                <div class="do-table-itemmeta">
                                                    <?php echo esc_html($item['itemCode'] ?: '-'); ?>
                                                    · Qty <?php echo esc_html(ac_do_mobile_fmt($item['qty'])); ?>
                                                    · Basket <?php echo esc_html(ac_do_mobile_fmt($item['basket'])); ?>
                                                    · Carton <?php echo esc_html(ac_do_mobile_fmt($item['carton'])); ?>
                                                    · KG <?php echo esc_html(ac_do_mobile_fmt_weight($item['weightKg'])); ?>
                                                </div>
                                            </div>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                </td>
                                <td><?php echo esc_html($r['createdAt'] ?: '-'); ?></td>
                                <td><?php echo esc_html($r['lastModified'] ?: '-'); ?></td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
    <?php endif; ?>
</div>

<style>
.do-table-wrap{
    --do-line:#e5e7eb;
    --do-line-strong:#d1d5db;
    --do-text:#0f172a;
    --do-muted:#64748b;
    --do-green:#166534;
    --do-green-dark:#14532d;

    width:100%;
    max-width:100%;
    margin:0 auto;
    padding:.5rem;
    font-family:"Segoe UI", Roboto, system-ui, sans-serif;
    color:var(--do-text);
    background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
    border-radius:.65rem;
    box-sizing:border-box;
}

.do-table-head{
    margin-bottom:.35rem;
}

.do-table-head h2{
    margin:0;
    font-size:1.2rem;
    font-weight:800;
    line-height:1.05;
}

.do-table-filter,
.do-table-shell,
.do-table-empty{
    background:#fff;
    border:1px solid var(--do-line);
    border-radius:.55rem;
}

.do-table-filter{
    padding:.5rem;
    margin-bottom:.35rem;
}

.do-table-form{
    display:flex;
    flex-direction:column;
    gap:.35rem;
}

.do-table-field{
    min-width:0;
    display:flex;
    flex-direction:column;
    gap:.14rem;
}

.do-table-field-full{
    width:100%;
}

.do-table-filter-row{
    display:grid;
    grid-template-columns:.9fr 1fr 1fr;
    gap:.35rem;
    align-items:end;
}

.do-table-label{
    font-size:.66rem;
    font-weight:700;
    color:#475569;
    line-height:1.05;
}

.do-table-input{
    width:100%;
    min-height:2.05rem;
    border:1px solid var(--do-line-strong);
    border-radius:.45rem;
    padding:.4rem .55rem;
    font-size:.82rem;
    box-sizing:border-box;
    background:#fff;
    color:var(--do-text);
    transition:border-color .18s ease, box-shadow .18s ease;
}

.do-table-input:focus{
    outline:none;
    border-color:var(--do-green);
    box-shadow:0 0 0 .14rem rgba(22,101,52,.10);
}

.do-table-actions{
    display:flex;
}

.do-table-btn{
    width:100%;
    min-height:2.08rem;
    padding:.42rem .62rem;
    border:none;
    border-radius:.45rem;
    background:var(--do-green);
    color:#fff;
    font-weight:800;
    font-size:.84rem;
    cursor:pointer;
    transition:background .18s ease, box-shadow .18s ease;
}

.do-table-btn:hover{
    background:var(--do-green-dark);
    box-shadow:0 3px 10px rgba(22,101,52,.14);
}

.do-table-summary{
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap:.4rem;
    margin:.08rem 0 .35rem;
    padding:.02rem;
    font-size:.7rem;
    color:#334155;
}

.do-table-empty{
    text-align:center;
    color:#64748b;
    padding:.75rem .5rem;
    font-size:.74rem;
}

.do-table-shell{
    overflow:hidden;
}

.do-table-scroll{
    overflow-x:auto;
    -webkit-overflow-scrolling:touch;
}

.do-table-grid{
    width:100%;
    min-width:48rem;
    border-collapse:collapse;
    font-size:.66rem;
    background:#fff;
}

.do-table-grid th,
.do-table-grid td{
    padding:.38rem .32rem;
    border-bottom:1px solid #edf2f7;
    vertical-align:top;
    text-align:left;
}

.do-table-grid thead th{
    background:#f8fafc;
    font-size:.56rem;
    font-weight:800;
    color:#475569;
    white-space:nowrap;
    position:sticky;
    top:0;
    z-index:1;
}

.do-table-grid tbody tr:hover{
    background:#f9fffb;
}

.do-table-customer{
    min-width:12rem;
}

.do-table-name{
    font-size:.7rem;
    font-weight:800;
    color:var(--do-text);
    line-height:1.08;
}

.do-table-code{
    margin-top:.04rem;
    font-size:.54rem;
    font-weight:600;
    color:var(--do-muted);
    line-height:1.04;
}

.do-table-strong{
    font-weight:800;
    color:var(--do-text);
    white-space:nowrap;
}

.do-table-date{
    white-space:nowrap;
    min-width:6.5rem;
}

.do-table-items{
    min-width:20rem;
}

.do-table-itemline{
    margin-bottom:.18rem;
}

.do-table-itemline:last-child{
    margin-bottom:0;
}

.do-table-itemname{
    font-size:.62rem;
    font-weight:800;
    color:var(--do-text);
    line-height:1.08;
}

.do-table-itemmeta{
    margin-top:.03rem;
    font-size:.54rem;
    font-weight:600;
    color:var(--do-muted);
    line-height:1.14;
    word-break:break-word;
}

.do-table-muted{
    font-size:.56rem;
    color:var(--do-muted);
}

.do-tab�E�'��VH��������+!�y
??�I<?php
/**
 * VegeBasketDO staff-only Delivery Order edit page.
 *
 * Drop this snippet on /edit-delivery-order/.
 * Open with: /edit-delivery-order/?docNo=DO-000074&docKey=409
 */

if (!defined('ABSPATH')) {
    exit;
}

if (!is_user_logged_in()) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">Please log in to edit Delivery Orders.</div>';
    return;
}

$wst_doe_user  = wp_get_current_user();
$wst_doe_roles = is_array($wst_doe_user->roles ?? null) ? $wst_doe_user->roles : array();
$wst_doe_staff = current_user_can('manage_options') || in_array('editor', $wst_doe_roles, true);

if (!$wst_doe_staff) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">You do not have permission to edit Delivery Orders.</div>';
    return;
}

if (!function_exists('get_mssql')) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">Delivery Order connection is not available.</div>';
    return;
}

global $wpdb;

if (!defined('WST_DOE_MAX_LINES')) {
    define('WST_DOE_MAX_LINES', 80);
}

if (!defined('WST_DOE_MAX_UNIT_QTY')) {
    define('WST_DOE_MAX_UNIT_QTY', 9999);
}

if (!defined('WST_DOE_MAX_KG_PER_UNIT')) {
    define('WST_DOE_MAX_KG_PER_UNIT', 9999);
}

if (!defined('WST_DOE_MAX_TOTAL_KG')) {
    define('WST_DOE_MAX_TOTAL_KG', 999999);
}

if (!function_exists('wst_doe_table_exists')) {
    function wst_doe_table_exists($table_name) {
        global $wpdb;
        return $wpdb && $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table_name)) === $table_name;
    }
}

if (!function_exists('wst_doe_table_columns')) {
    function wst_doe_table_columns($table_name) {
        global $wpdb;
        static $cache = array();

        if (isset($cache[$table_name])) {
            return $cache[$table_name];
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', (string) $table_name);
        $cols = $wpdb ? $wpdb->get_col("SHOW COLUMNS FROM `{$safe_table}`", 0) : array();
        $cache[$table_name] = is_array($cols) ? array_flip($cols) : array();

        return $cache[$table_name];
    }
}

if (!function_exists('wst_doe_json_array')) {
    function wst_doe_json_array($json) {
        $decoded = json_decode((string) $json, true);
        return is_array($decoded) ? $decoded : array();
    }
}

if (!function_exists('wst_doe_pick')) {
    function wst_doe_pick($arr, $keys, $fallback = '') {
        if (!is_array($arr)) {
            return $fallback;
        }

        foreach ($keys as $key) {
            if (isset($arr[$key]) && $arr[$key] !== '' && $arr[$key] !== null) {
                return $arr[$key];
            }
        }

        return $fallback;
    }
}

if (!function_exists('wst_doe_clean_doc_no')) {
    function wst_doe_clean_doc_no($value) {
        $value = strtoupper(trim(sanitize_text_field((string) $value)));
        return preg_match('/^[A-Z0-9][A-Z0-9\-\/]{1,49}$/', $value) ? $value : '';
    }
}

if (!function_exists('wst_doe_valid_date')) {
    function wst_doe_valid_date($value, $fallback = '') {
        $value = trim((string) $value);
        $dt = DateTime::createFromFormat('Y-m-d', $value, wp_timezone());
        return ($dt && $dt->format('Y-m-d') === $value) ? $value : $fallback;
    }
}

if (!function_exists('wst_doe_float')) {
    function wst_doe_float($value) {
        $value = is_string($value) ? str_replace(',', '', $value) : $value;
        return is_numeric($value) ? (float) $value : 0.0;
    }
}

if (!function_exists('wst_doe_sql_errors')) {
    function wst_doe_sql_errors() {
        if (!function_exists('sqlsrv_errors')) {
            return 'Unknown SQL Server error.';
        }

        $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
        if (empty($errors)) {
            return 'Unknown SQL Server error.';
        }

        $out = array();
        foreach ($errors as $error) {
            $out[] = '[' . ($error['code'] ?? '') . '] ' . ($error['message'] ?? '');
        }

        return implode(' | ', $out);
    }
}

if (!function_exists('wst_doe_doc_match')) {
    function wst_doe_doc_match($payload, $result, $doc_no, $doc_key) {
        $found_no = strtoupper(trim((string) wst_doe_pick($result, array('docNo', 'DocNo', 'doc_no'), '')));
        if ($found_no === '') {
            $found_no = strtoupper(trim((string) wst_doe_pick($payload, array('docNo', 'DocNo', 'sourceDocNo', 'oldDocNo', 'originalDocNo'), '')));
        }

        $found_key = (int) wst_doe_pick($result, array('docKey', 'DocKey', 'doc_key'), 0);
        if ($found_key <= 0) {
            $found_key = (int) wst_doe_pick($payload, array('docKey', 'DocKey', 'doc_key'), 0);
        }

        if ($doc_no !== '' && $doc_key > 0) {
            return $found_no === $doc_no && $found_key === $doc_key;
        }

        return ($doc_no !== '' && $found_no === $doc_no) || ($doc_key > 0 && $found_key === $doc_key);
    }
}

if (!function_exists('wst_doe_job_matches_doc')) {
    function wst_doe_job_matches_doc($row, $doc_no, $doc_key) {
        $payload = wst_doe_json_array($row['payload'] ?? '');
        $result = wst_doe_json_array($row['result'] ?? '');
        return wst_doe_doc_match($payload, $result, $doc_no, $doc_key);
    }
}

if (!function_exists('wst_doe_jobs_ref_columns_ready')) {
    function wst_doe_jobs_ref_columns_ready($cols) {
        foreach (array('source_doc_no', 'source_doc_key', 'delivery_status', 'job_subtype', 'status') as $col) {
            if (!isset($cols[$col])) {
                return false;
            }
        }

        return true;
    }
}

if (!function_exists('wst_doe_editable_statuses')) {
    function wst_doe_editable_statuses() {
        return array(
            'ASSIGNED',
            'PENDING_DELIVERY',
            'NEEDS_STAFF_EDIT',
        );
    }
}

if (!function_exists('wst_doe_get_related_job')) {
    function wst_doe_get_related_job($doc_no, $doc_key) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_jobs';
        if (!wst_doe_table_exists($table)) {
            return new WP_Error('jobs_table_missing', 'AutoCount jobs table was not found. Editing is blocked.');
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $cols = wst_doe_table_columns($table);
        if (!wst_doe_jobs_ref_columns_ready($cols)) {
            return new WP_Error('jobs_ref_columns_missing', 'AutoCount job reference columns are missing. Editing is blocked until the bridge schema is upgraded.');
        }

        $select = array('id', 'payload', 'result', 'status', 'created_by', 'updated_at');
        foreach (array('job_subtype', 'delivery_status', 'assigned_driver_id', 'error_message') as $col) {
            if (isset($cols[$col])) {
                $select[] = $col;
            }
        }
        $select[] = 'source_doc_no';
        $select[] = 'source_doc_key';

        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT " . implode(',', array_map(function($col) { return "`{$col}`"; }, $select)) . "
                 FROM `{$safe_table}`
                 WHERE job_type = %s
                   AND source_doc_no = %s
                   AND source_doc_key = %d
                 ORDER BY id DESC
                 LIMIT 25",
                'DELIVERY_ORDER',
                $doc_no,
                (int) $doc_key
            ),
            ARRAY_A
        );

        if (empty($rows)) {
            return new WP_Error('authoritative_job_missing', 'No authoritative AutoCount job record was found for this Delivery Order. Editing is blocked.');
        }

        $update_statuses = array('EDIT_PENDING_AUTOCOUNT', 'EDITED_IN_AUTOCOUNT');
        foreach ((array) $rows as $row) {
            $delivery_status = strtoupper(trim((string) ($row['delivery_status'] ?? '')));
            $job_subtype = strtoupper(trim((string) ($row['job_subtype'] ?? '')));

            if (!in_array($job_subtype, array('UPDATE', 'EDIT'), true) || !in_array($delivery_status, $update_statuses, true)) {
                $row['_payload'] = wst_doe_json_array($row['payload'] ?? '');
                $row['_result'] = wst_doe_json_array($row['result'] ?? '');
                return $row;
            }
        }

        return new WP_Error('authoritative_lifecycle_missing', 'Only edit-job records were found for this Delivery Order. Editing is blocked because the delivery lifecycle status cannot be confirmed.');
    }
}

if (!function_exists('wst_doe_get_pending_update_job_id')) {
    function wst_doe_get_pending_update_job_id($doc_no, $doc_key) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_jobs';
        if (!wst_doe_table_exists($table)) {
            return 0;
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $cols = wst_doe_table_columns($table);
        if (!wst_doe_jobs_ref_columns_ready($cols)) {
            return -1;
        }

        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT id, payload, result
                 FROM `{$safe_table}`
                 WHERE job_type = %s
                   AND job_subtype IN ('UPDATE', 'EDIT')
                   AND status IN ('PENDING', 'PROCESSING', 'RETRY')
                   AND source_doc_no = %s
                   AND source_doc_key = %d
                 ORDER BY id DESC
                 LIMIT 50",
                'DELIVERY_ORDER',
                $doc_no,
                (int) $doc_key
            ),
            ARRAY_A
        );

        foreach ((array) $rows as $row) {
            return absint($row['id'] ?? 0);
        }

        return 0;
    }
}

if (!function_exists('wst_doe_saved_job_matches_current_do')) {
    function wst_doe_saved_job_matches_current_do($job_id, $doc_no, $doc_key) {
        global $wpdb;

        $job_id = absint($job_id);
        if ($job_id <= 0) {
            return false;
        }

        $table = $wpdb->prefix . 'ac_jobs';
        if (!wst_doe_table_exists($table)) {
            return false;
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $cols = wst_doe_table_columns($table);
        if (!wst_doe_jobs_ref_columns_ready($cols)) {
            return false;
        }

        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT id, payload, result, created_by, source_doc_no, source_doc_key
                 FROM `{$safe_table}`
                 WHERE id = %d
                   AND job_type = %s
                   AND job_subtype IN ('UPDATE', 'EDIT')
                   AND source_doc_no = %s
                   AND source_doc_key = %d
                 LIMIT 1",
                $job_id,
                'DELIVERY_ORDER',
                $doc_no,
                (int) $doc_key
            ),
            ARRAY_A
        );

        if (!$row || absint($row['created_by'] ?? 0) !== get_current_user_id()) {
            return false;
        }

        return true;
    }
}

if (!function_exists('wst_doe_driver_label')) {
    function wst_doe_driver_label($driver_id, $fallback = '') {
        $driver_id = absint($driver_id);
        if ($driver_id > 0) {
            $user = get_userdata($driver_id);
            if ($user) {
                $display = trim((string) $user->display_name);
                return $display !== '' ? $display : (string) $user->user_login;
            }
        }

        return trim((string) $fallback);
    }
}

if (!function_exists('wst_doe_is_driver_user')) {
    function wst_doe_is_driver_user($driver_id) {
        $driver_id = absint($driver_id);
        if ($driver_id <= 0) {
            return false;
        }

        $user = get_userdata($driver_id);
        return $user && is_array($user->roles) && in_array('driver', $user->roles, true);
    }
}

if (!function_exists('wst_doe_edit_locked_statuses')) {
    function wst_doe_edit_locked_statuses() {
        return array(
            'DRIVER_ACKNOWLEDGED',
            'RECEIVED',
            'OUT_FOR_DELIVERY',
            'DELIVERED',
        );
    }
}

if (!function_exists('wst_doe_is_edit_locked_status')) {
    function wst_doe_is_edit_locked_status($delivery_status) {
        $delivery_status = strtoupper(trim((string) $delivery_status));
        return in_array($delivery_status, wst_doe_edit_locked_statuses(), true);
    }
}

if (!function_exists('wst_doe_load_order')) {
    function wst_doe_load_order($conn, $doc_no, $doc_key) {
        $header_sql = "
            SELECT TOP 1
                DOH.DocKey,
                DOH.DocNo,
                DOH.DocDate,
                ISNULL(DOH.DebtorCode, '') AS DebtorCode,
                ISNULL(DOH.DebtorName, '') AS DebtorName,
                ISNULL(DOH.SalesAgent, '') AS SalesAgent
            FROM dbo.DO AS DOH
            WHERE DOH.DocNo = ? AND DOH.DocKey = ?
        ";
        $stmt = sqlsrv_query($conn, $header_sql, array($doc_no, $doc_key), array('QueryTimeout' => 10));
        if ($stmt === false) {
            return new WP_Error('do_header_failed', wst_doe_sql_errors());
        }

        $header = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
        sqlsrv_free_stmt($stmt);

        if (!$header) {
            return new WP_Error('do_not_found', 'Delivery Order was not found for this DocNo/DocKey.');
        }

        $detail_sql = "
            SELECT
                ISNULL(DTL.ItemCode, '') AS ItemCode,
                ISNULL(DTL.Description, '') AS Description,
                ISNULL(DTL.UOM, '') AS UOM,
                ISNULL(DTL.Qty, 0) AS Qty,
                ISNULL(DTL.UnitPrice, 0) AS UnitPrice,
                ISNULL(DTL.UDF_BASKET, 0) AS Basket,
                ISNULL(DTL.UDF_CARTON, 0) AS Carton,
                ISNULL(DTL.UDF_WEIGHTKG, 0) AS WeightKG,
                ISNULL(DTL.Location, '') AS Location
            FROM dbo.DODTL AS DTL
            WHERE DTL.DocKey = ?
            ORDER BY ISNULL(DTL.Seq, 0) ASC, ISNULL(DTL.ItemCode, '') ASC
        ";
        $detail_stmt = sqlsrv_query($conn, $detail_sql, array($doc_key), array('QueryTimeout' => 10));
        if ($detail_stmt === false) {
            return new WP_Error('do_detail_failed', wst_doe_sql_errors());
        }

        $lines = array();
        while ($line = sqlsrv_fetch_array($detail_stmt, SQLSRV_FETCH_ASSOC)) {
            $basket = wst_doe_float($line['Basket'] ?? 0);
            $carton = wst_doe_float($line['Carton'] ?? 0);
            $total_kg = wst_doe_float($line['Qty'] ?? 0);
            $pack_type = $carton > 0 ? 'CARTON' : 'BASKET';
            $unit_qty = $pack_type === 'CARTON' ? $carton : $basket;
            if ($unit_qty <= 0) {
                $unit_qty = 1;
            }
            $kg_per_unit = wst_doe_float($line['WeightKG'] ?? 0);
            if ($kg_per_unit <= 0 && $total_kg > 0 && $unit_qty > 0) {
                $kg_per_unit = $total_kg / $unit_qty;
            }

            $lines[] = array(
                'itemCode' => trim((string) ($line['ItemCode'] ?? '')),
                'itemName' => trim((string) ($line['Description'] ?? '')),
                'uom' => trim((string) ($line['UOM'] ?? 'KG')),
                'unitPrice' => wst_doe_float($line['UnitPrice'] ?? 0),
                'packType' => $pack_type,
                'unitQty' => $unit_qty,
                'kg' => $kg_per_unit,
                'totalKg' => $total_kg > 0 ? $total_kg : ($unit_qty * $kg_per_unit),
                'location' => trim((string) ($line['Location'] ?? '')),
            );
        }
        sqlsrv_free_stmt($detail_stmt);

        return array(
            'docKey' => (int) ($header['DocKey'] ?? 0),
            'docNo' => trim((string) ($header['DocNo'] ?? '')),
            'docDate' => ($header['DocDate'] ?? null) instanceof DateTime ? $header['DocDate']->format('Y-m-d') : wst_doe_valid_date((string) ($header['DocDate'] ?? ''), current_time('Y-m-d')),
            'debtorCode' => trim((string) ($header['DebtorCode'] ?? '')),
          +!�y�o�I��������+!̌
??�M  'debtorName' => trim((string) ($header['DebtorName'] ?? '')),
            'salesAgent' => trim((string) ($header['SalesAgent'] ?? '')),
            'lines' => $lines,
        );
    }
}

if (!function_exists('wst_doe_original_line_map')) {
    function wst_doe_original_line_map($order) {
        $map = array();
        foreach ((array) ($order['lines'] ?? array()) as $line) {
            $code = strtoupper(trim((string) ($line['itemCode'] ?? '')));
            if ($code !== '' && !isset($map[$code])) {
                $map[$code] = $line;
            }
        }
        return $map;
    }
}

if (!function_exists('wst_doe_validate_item')) {
    function wst_doe_validate_item($conn, $item_code) {
        static $cache = array();

        $item_code = trim((string) $item_code);
        if ($item_code === '') {
            return new WP_Error('item_required', 'Item code is required.');
        }

        $cache_key = strtoupper($item_code);
        if (isset($cache[$cache_key])) {
            return $cache[$cache_key];
        }

        $sql = "
            SELECT TOP 1
                i.ItemCode,
                ISNULL(i.Description, '') AS Description,
                ISNULL(i.BaseUOM, '') AS BaseUOM,
                ISNULL(iu.Price, 0) AS Price
            FROM Item i
            INNER JOIN ItemUOM iu
                ON iu.ItemCode = i.ItemCode
                AND iu.UOM = i.BaseUOM
            WHERE i.ItemCode = ?
              AND i.IsActive = 'T'
        ";
        $stmt = sqlsrv_query($conn, $sql, array($item_code), array('QueryTimeout' => 10));
        if ($stmt === false) {
            return new WP_Error('item_lookup_failed', wst_doe_sql_errors());
        }

        $row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
        sqlsrv_free_stmt($stmt);

        if (!$row) {
            return new WP_Error('invalid_item', 'Invalid item, inactive item, or missing BaseUOM row: ' . $item_code);
        }

        $cache[$cache_key] = array(
            'itemCode' => trim((string) ($row['ItemCode'] ?? $item_code)),
            'description' => trim((string) ($row['Description'] ?? $item_code)),
            'uom' => trim((string) ($row['BaseUOM'] ?? 'KG')) ?: 'KG',
            'defaultPrice' => wst_doe_float($row['Price'] ?? 0),
        );

        return $cache[$cache_key];
    }
}

if (!function_exists('wst_doe_queue_update_job')) {
    function wst_doe_queue_update_job($payload, $client_request_id, $assigned_driver_id, $original_job_id) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_jobs';
        if (!wst_doe_table_exists($table)) {
            return new WP_Error('jobs_table_missing', 'AutoCount jobs table was not found.');
        }

        $cols = wst_doe_table_columns($table);
        if (!wst_doe_jobs_ref_columns_ready($cols) || !isset($cols['pending_update_key'])) {
            return new WP_Error('jobs_ref_columns_missing', 'AutoCount job reference columns are missing. Editing is blocked until the bridge schema is upgraded.');
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $now = current_time('mysql');
        $doc_no = wst_doe_clean_doc_no($payload['docNo'] ?? '');
        $doc_key = absint($payload['docKey'] ?? 0);
        if ($doc_no === '' || $doc_key <= 0) {
            return new WP_Error('missing_do_identity', 'Delivery Order update requires DocNo and DocKey.');
        }

        if (class_exists('AutoCount_Bridge_Core') && method_exists('AutoCount_Bridge_Core', 'enqueue_protected_delivery_order_update_job')) {
            $queued = AutoCount_Bridge_Core::enqueue_protected_delivery_order_update_job(
                $payload,
                5,
                $client_request_id,
                'wp-ui-edit',
                array('assigned_driver_id' => $assigned_driver_id)
            );

            if (is_array($queued) && !empty($queued['success'])) {
                return (int)($queued['id'] ?? 0);
            }

            return new WP_Error(
                'queue_failed',
                is_array($queued) && !empty($queued['message'])
                    ? (string)$queued['message']
                    : 'Failed to queue protected AutoCount update job.'
            );
        }

        $pending_update_key = 'DELIVERY_ORDER_UPDATE:' . $doc_no . ':' . $doc_key;
        $wpdb->query('START TRANSACTION');

        $authoritative_job = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT id, delivery_status, assigned_driver_id
                 FROM `{$safe_table}`
                 WHERE job_type = %s
                   AND source_doc_no = %s
                   AND source_doc_key = %d
                   AND NOT (
                       job_subtype IN ('UPDATE', 'EDIT')
                       AND delivery_status IN ('EDIT_PENDING_AUTOCOUNT', 'EDITED_IN_AUTOCOUNT')
                   )
                 ORDER BY id DESC
                 LIMIT 1
                 FOR UPDATE",
                'DELIVERY_ORDER',
                $doc_no,
                $doc_key
            ),
            ARRAY_A
        );

        if (!$authoritative_job) {
            $wpdb->query('ROLLBACK');
            return new WP_Error('authoritative_job_missing', 'No authoritative delivery status was found. Editing is blocked.');
        }

        $current_delivery_status = strtoupper(trim((string) ($authoritative_job['delivery_status'] ?? '')));
        if (!in_array($current_delivery_status, wst_doe_editable_statuses(), true)) {
            $wpdb->query('ROLLBACK');
            return new WP_Error('delivery_status_locked', 'This Delivery Order can no longer be edited in its current delivery status: ' . $current_delivery_status . '.');
        }

        $pending_job_id = wst_doe_get_pending_update_job_id($doc_no, $doc_key);
        if ($pending_job_id !== 0) {
            $wpdb->query('ROLLBACK');
            if ($pending_job_id < 0) {
                return new WP_Error('jobs_ref_columns_missing', 'AutoCount job reference columns are missing. Editing is blocked until the bridge schema is upgraded.');
            }

            return new WP_Error('pending_update_exists', 'AutoCount update job #' . $pending_job_id . ' is already pending for this Delivery Order. Wait for it to finish before queueing another edit.');
        }

        $client_request_id = substr(preg_replace('/[^a-zA-Z0-9\-_:.]/', '', (string) $client_request_id), 0, 64);
        if ($client_request_id === '') {
            $client_request_id = 'do-update-' . wp_generate_uuid4();
        }

        $payload['_meta'] = array(
            'requestedBy' => get_current_user_id(),
            'requestedAt' => $now,
            'source' => 'wp-ui-edit',
            'ip' => isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '',
        );

        if ($assigned_driver_id > 0) {
            $payload['_assignment'] = array(
                'assignedDriverId' => $assigned_driver_id,
                'assignedAt' => $now,
                'assignedBy' => get_current_user_id(),
            );
        }

        $insert = array(
            'client_request_id' => $client_request_id,
            'job_type' => 'DELIVERY_ORDER',
            'job_subtype' => 'UPDATE',
            'priority' => 5,
            'payload' => wp_json_encode($payload),
            'status' => 'PENDING',
            'created_by' => get_current_user_id(),
            'source' => 'wp-ui-edit',
            'max_retries' => 3,
            'source_doc_no' => $doc_no,
            'source_doc_key' => $doc_key,
            'pending_update_key' => $pending_update_key,
        );
        $formats = array('%s', '%s', '%s', '%d', '%s', '%s', '%d', '%s', '%d', '%s', '%d', '%s');

        if (isset($cols['assigned_driver_id']) && $assigned_driver_id > 0) {
            $insert['assigned_driver_id'] = $assigned_driver_id;
            $formats[] = '%d';
        }
        if (isset($cols['assigned_at']) && $assigned_driver_id > 0) {
            $insert['assigned_at'] = $now;
            $formats[] = '%s';
        }
        if (isset($cols['assigned_by']) && $assigned_driver_id > 0) {
            $insert['assigned_by'] = get_current_user_id();
            $formats[] = '%d';
        }
        if (isset($cols['delivery_status'])) {
            $insert['delivery_status'] = 'EDIT_PENDING_AUTOCOUNT';
            $formats[] = '%s';
        }
        if (isset($cols['created_at'])) {
            $insert['created_at'] = $now;
            $formats[] = '%s';
        }
        if (isset($cols['updated_at'])) {
            $insert['updated_at'] = $now;
            $formats[] = '%s';
        }

        $ok = $wpdb->insert($table, $insert, $formats);
        if (!$ok) {
            $existing = $wpdb->get_var($wpdb->prepare("SELECT id FROM `{$safe_table}` WHERE client_request_id = %s LIMIT 1", $client_request_id));
            if ($existing) {
                $wpdb->query('COMMIT');
                return (int) $existing;
            }

            $existing_pending = $wpdb->get_var($wpdb->prepare("SELECT id FROM `{$safe_table}` WHERE pending_update_key = %s LIMIT 1", $pending_update_key));
            if ($existing_pending) {
                $wpdb->query('ROLLBACK');
                return new WP_Error('pending_update_exists', 'AutoCount update job #' . (int) $existing_pending . ' is already pending for this Delivery Order. Wait for it to finish before queueing another edit.');
            }

            $wpdb->query('ROLLBACK');
            return new WP_Error('queue_failed', 'Failed to queue AutoCount update job.');
        }

        $new_job_id = (int) $wpdb->insert_id;

        $wpdb->query('COMMIT');

        return $new_job_id;
    }
}

$wst_doe_doc_no = isset($_GET['docNo']) ? wst_doe_clean_doc_no(wp_unslash($_GET['docNo'])) : '';
$wst_doe_doc_key = isset($_GET['docKey']) ? absint($_GET['docKey']) : 0;
$wst_doe_error = '';
$wst_doe_saved_job_id = isset($_GET['wst_doe_saved']) ? absint($_GET['wst_doe_saved']) : 0;
$wst_doe_list_url = home_url('/delivery-order-records/');

if ($wst_doe_doc_no === '' || $wst_doe_doc_key <= 0) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">Invalid Delivery Order link. DocNo and DocKey are required.</div>';
    return;
}

$wst_doe_conn = get_mssql();
if (!$wst_doe_conn) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">Failed to connect to Delivery Order records.</div>';
    return;
}

$wst_doe_order = wst_doe_load_order($wst_doe_conn, $wst_doe_doc_no, $wst_doe_doc_key);
if (is_wp_error($wst_doe_order)) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">' . esc_html($wst_doe_order->get_error_message()) . '</div>';
    return;
}

$wst_doe_job = wst_doe_get_related_job($wst_doe_doc_no, $wst_doe_doc_key);
if (is_wp_error($wst_doe_job)) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">' . esc_html($wst_doe_job->get_error_message()) . '</div>';
    return;
}

$wst_doe_saved_job_id = wst_doe_saved_job_matches_current_do($wst_doe_saved_job_id, $wst_doe_doc_no, $wst_doe_doc_key) ? $wst_doe_saved_job_id : 0;
$wst_doe_original_job_id = absint($wst_doe_job['id'] ?? 0);
$wst_doe_driver_id = absint($wst_doe_job['assigned_driver_id'] ?? 0);
$wst_doe_payload = isset($wst_doe_job['_payload']) && is_array($wst_doe_job['_payload']) ? $wst_doe_job['_payload'] : array();
$wst_doe_driver_fallback = wst_doe_pick($wst_doe_payload, array('assignedDriverName', 'driverName', 'assignedDriverLogin', 'driverLogin'), '');
$wst_doe_driver_label = wst_doe_driver_label($wst_doe_driver_id, $wst_doe_driver_fallback);
$wst_doe_delivery_status = strtoupper(trim((string) ($wst_doe_job['delivery_status'] ?? '')));

if (wst_doe_is_edit_locked_status($wst_doe_delivery_status)) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">This Delivery Order can no longer be edited because the current delivery status is ' . esc_html(str_replace('_', ' ', $wst_doe_delivery_status)) . '.</div>';
    return;
}

if (!in_array($wst_doe_delivery_status, wst_doe_editable_statuses(), true)) {
    echo '<div class="wst-doe-alert wst-doe-alert-error">This Delivery Order is not in an editable delivery status.</div>';
    return;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['wst_doe_action'])) {
    $posted_action = sanitize_key(wp_unslash($_POST['wst_doe_action']));

    if ($posted_action !== 'queue_update') {
        $wst_doe_error = 'Invalid action.';
    } elseif (!isset($_POST['wst_doe_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['wst_doe_nonce'])), 'wst_doe_queue_update')) {
        $wst_doe_error = 'Security check failed. Please reload and try again.';
    } else {
        $posted_type = isset($_POST['wst_doe_type']) ? strtoupper(sanitize_text_field(wp_unslash($_POST['wst_doe_type']))) : '';
        $posted_subtype = isset($_POST['wst_doe_subtype']) ? strtoupper(sanitize_text_field(wp_unslash($_POST['wst_doe_subtype']))) : '';
        $posted_doc_no = isset($_POST['wst_doe_doc_no']) ? wst_doe_clean_doc_no(wp_unslash($_POST['wst_doe_doc_no'])) : '';
        $posted_doc_key = isset($_POST['wst_doe_doc_key']) ? absint($_POST['wst_doe_doc_key']) : 0;
        $posted_driver_id = isset($_POST['wst_doe_driver_id']) ? absint($_POST['wst_doe_driver_id']) : $wst_doe_driver_id;
        $posted_debtor_code = sanitize_text_field((string) ($wst_doe_order['debtorCode'] ?? ''));
        $posted_debtor_name = sanitize_text_field((string) ($wst_doe_order['debtorName'] ?? ''));
        $posted_sales_agent = sanitize_text_field((string) ($wst_doe_order['salesAgent'] ?? ''));
        $posted_doc_date = wst_doe_valid_date((string) ($wst_doe_order['docDate'] ?? ''), current_time('Y-m-d'));
        $posted_remark = 'Staff edited existing Delivery Order.';

        if ($posted_type !== 'DELIVERY_ORDER' || $posted_subtype !== 'UPDATE') {
            $wst_doe_error = 'Invalid update request type.';
        } elseif ($posted_doc_no !== $wst_doe_doc_no || $posted_doc_key !== $wst_doe_doc_key) {
            $wst_doe_error = 'Delivery Order identity mismatch. Please reload and try again.';
        } elseif ($posted_debtor_code === '') {
            $wst_doe_error = 'Customer code is required.';
        } elseif (!wst_doe_is_driver_user($posted_driver_id)) {
            $wst_doe_error = 'Select a valid driver before queueing the update.';
        } else {
            $posted_lines_raw = isset($_POST['wst_doe_lines']) && is_array($_POST['wst_doe_lines']) ? wp_unslash($_POST['wst_doe_lines']) : array();
            $payload_lines = array();
            $original_lines = wst_doe_original_line_map($wst_doe_order);

            if (count($posted_lines_raw) > WST_DOE_MAX_LINES) {
                $wst_doe_error = 'Too many item lines. Maximum allowed is ' . WST_DOE_MAX_LINES . '.';
            }

            foreach ($posted_lines_raw as $line) {
                if ($wst_doe_error !== '') {
                    break;
                }

                if (!is_array($line)) {
                    continue;
                }

                $item_code = sanitize_text_field($line['item_code'] ?? '');
                $pack_type = strtoupper(sanitize_text_field($line['pack_type'] ?? 'BASKET'));
                $unit_qty = wst_doe_float($line['unit_qty'] ?? 0);
                $kg = wst_doe_float($line['kg'] ?? 0);

                if ($pack_type !== 'BASKET' && $pack_type !== 'CARTON') {
                    $pack_type = 'BASKET';
                }

                $total_kg = round($unit_qty * $kg, 4);

                if ($item_code === '' || $unit_qty <= 0 || $kg <= 0 || $total_kg <= 0) {
                    continue;
                }

                if ($unit_qty > WST_DOE_MAX_UNIT_QTY || $kg > WST_DOE_MAX_KG_PER_UNIT || $total_kg > WST_DOE_MAX_TOTAL_KG) {
                    $wst_doe_error = 'Item line quantity or weight is too large.';
                    break;
                }

                $validated_item = wst_doe_validate_item($ws+!̌��EJ���������ظ
??�K<?php
/**
 * BASKET STAFF RETURN LIST
 *
 * Staff-facing basket summary and movement history page.
 * Basket return receipts use the same A5 visual style as the driver basket receipt.
 */

if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to view Basket Summary.</div>';
    return;
}

$rest_nonce         = wp_create_nonce('wp_rest');
$rest_summary_url   = rest_url('ac/v1/basket/summary');
$rest_ledger_url    = rest_url('ac/v1/basket/ledger');
$ajax_url           = admin_url('admin-ajax.php');
$debtor_nonce       = wp_create_nonce('ac_cs_debtor_search');
$basket_proof_nonce = wp_create_nonce('ac_bs_basket_proof');
$receipt_logo_url   = 'https://website.ipohserver.com/VegeBasketDO/wp-content/uploads/2026/05/Untitled-design-15.png';
$show_debtor_code   = false;
?>

<div id="ac-basket-summary-root"
     class="ac-bs-wrap bs-container"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-rest-summary-url="<?php echo esc_attr($rest_summary_url); ?>"
     data-rest-ledger-url="<?php echo esc_attr($rest_ledger_url); ?>"
     data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
     data-debtor-nonce="<?php echo esc_attr($debtor_nonce); ?>"
     data-basket-proof-nonce="<?php echo esc_attr($basket_proof_nonce); ?>"
     data-receipt-logo-url="<?php echo esc_url($receipt_logo_url); ?>"
     data-show-debtor-code="<?php echo $show_debtor_code ? '1' : '0'; ?>">

  <div class="bs-head">
    <h1>Basket Summary</h1>
  </div>

  <div class="bs-card">
    <div class="bs-grid">
      <div class="bs-field">
        <label class="bs-label">Customer</label>
        <div class="bs-search-wrap">
          <input type="text" id="ac_bs_customer_input" class="bs-input" placeholder="Search customer..." autocomplete="off" readonly>
          <button type="button" id="ac_bs_customer_clear" class="bs-field-clear" aria-label="Clear customer">x</button>
          <input type="hidden" id="ac_bs_debtor_code" value="">
          <input type="hidden" id="ac_bs_debtor_name" value="">
        </div>
      </div>

      <div class="bs-field">
        <label class="bs-label" for="ac_bs_date_from">Date From</label>
        <input id="ac_bs_date_from" type="date" class="bs-input">
      </div>

      <div class="bs-field">
        <label class="bs-label" for="ac_bs_date_to">Date To</label>
        <input id="ac_bs_date_to" type="date" class="bs-input">
      </div>

      <div class="bs-actions">
        <button id="ac_bs_refresh" class="bs-btn" type="button">Refresh Summary</button>
      </div>
    </div>

    <div id="ac_bs_status" class="bs-status"></div>
  </div>

  <div class="bs-card">
    <div class="bs-totals" id="ac_bs_totals"></div>

    <div class="bs-table-wrap">
      <table class="bs-table">
        <thead>
          <tr>
            <th style="width:60px;">No</th>
            <th style="width:140px;">Customer Code</th>
            <th>Customer Name</th>
            <th style="width:120px;">Basket Sent</th>
            <th style="width:130px;">Basket Returned</th>
            <th style="width:150px;">Outstanding Basket</th>
            <th style="width:130px;">Last Activity</th>
            <th style="width:90px;">Action</th>
          </tr>
        </thead>
        <tbody id="ac_bs_rows_table">
          <tr><td colspan="8" class="bs-empty-cell">No data</td></tr>
        </tbody>
      </table>
    </div>
  </div>

  <div class="bs-ledger-modal" id="ac_bs_ledger_modal" aria-hidden="true">
    <div class="bs-ledger-backdrop" id="ac_bs_ledger_backdrop"></div>
    <div class="bs-ledger-dialog">
      <div class="bs-ledger-head">
        <h2 class="bs-subtitle" id="ac_bs_ledger_title">Basket Movement History</h2>
        <button type="button" class="bs-ledger-close" id="ac_bs_ledger_close" aria-label="Close">x</button>
      </div>

      <div class="bs-ledger-body">
        <div class="bs-table-wrap">
          <table class="bs-table bs-ledger-table">
            <thead>
              <tr>
                <th style="width:60px;">No</th>
                <th style="width:120px;">Date</th>
                <th style="width:120px;">Movement</th>
                <th style="width:90px;">Qty</th>
                <th style="width:150px;">From</th>
                <th style="width:140px;">Document No.</th>
                <th>Note</th>
                <th style="width:150px;">Receipt</th>
              </tr>
            </thead>
            <tbody id="ac_bs_ledger_table">
              <tr><td colspan="8" class="bs-empty-cell">Select a customer to view basket movement history.</td></tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </div>

  <div id="ac_bs_receipt_mount"></div>

  <div class="bs-picker-modal" id="ac_bs_picker_modal" aria-hidden="true">
    <div class="bs-picker-backdrop" id="ac_bs_picker_backdrop"></div>
    <div class="bs-picker-sheet">
      <div class="bs-picker-head">
        <div class="bs-picker-title" id="ac_bs_picker_title">Select Customer</div>
        <button type="button" class="bs-picker-close" id="ac_bs_picker_close" aria-label="Close">x</button>
      </div>

      <div class="bs-picker-body">
        <input type="text" id="ac_bs_picker_search" class="bs-input bs-picker-search" placeholder="Search customer..." autocomplete="off">
        <div class="bs-picker-results" id="ac_bs_picker_results"></div>
      </div>
    </div>
  </div>
</div>

<style>
.bs-container{
  --bs-border:#dbe4ee;
  --bs-border-strong:#c4d0dd;
  --bs-text:#0f172a;
  --bs-muted:#475569;
  --bs-green:#0B4A2D;
  --bs-green-soft:#f0fdf4;
  max-width:1360px;
  margin:0 auto;
  padding:16px;
  font-family:"Segoe UI",Roboto,Arial,sans-serif;
  background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
  border-radius:16px;
  color:var(--bs-text);
  box-sizing:border-box;
}
.bs-head{display:none;}
.bs-card{background:#fff;border:1px solid var(--bs-border);border-radius:14px;box-shadow:0 4px 16px rgba(15,23,42,.05);padding:16px;margin-bottom:14px;box-sizing:border-box;}
.bs-grid{display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:10px;align-items:end;}
.bs-label{display:block;font-size:13px;color:var(--bs-muted);margin-bottom:6px;font-weight:700;}
.bs-input{width:100%;min-height:46px;font-size:15px;padding:10px 12px;border-radius:10px;border:1px solid var(--bs-border-strong);box-sizing:border-box;background:#fff;color:#111;}
.bs-input:focus,.bs-btn:focus,.bs-view-btn:focus{outline:none;border-color:var(--bs-green);box-shadow:0 0 0 3px rgba(11,74,45,.12);}
#ac-basket-summary-root #ac_bs_refresh.bs-btn{min-width:170px;border:0!important;border-radius:12px!important;background:linear-gradient(90deg,#16a34a,#166534)!important;color:#fff!important;font-size:15px!important;font-weight:800!important;padding:10px 18px!important;cursor:pointer;box-shadow:0 8px 18px rgba(22,101,52,.18)!important;}
#ac-basket-summary-root #ac_bs_refresh.bs-btn:hover{background:linear-gradient(90deg,#15803d,#14532d)!important;transform:translateY(-1px);}
.bs-status{display:none;margin-top:10px;font-size:14px;color:#334155;}
.bs-totals{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-bottom:14px;}
.bs-total-box{border:1px solid #e5e7eb;background:#f8fafc;border-radius:10px;padding:10px;}
.bs-total-label{font-size:12px;color:#64748b;font-weight:700;}
.bs-total-value{font-size:22px;font-weight:800;color:#0f172a;margin-top:4px;}
.bs-table-wrap{width:100%;overflow:auto;border:1px solid #e5e7eb;border-radius:12px;}
.bs-table{width:100%;min-width:980px;border-collapse:collapse;background:#fff;}
.bs-table thead th{background:#f8fafc;color:#334155;font-size:13px;font-weight:800;text-align:left;padding:10px 12px;border-bottom:1px solid #e5e7eb;white-space:nowrap;}
.bs-table tbody td{padding:10px 12px;font-size:14px;color:#0f172a;border-bottom:1px solid #eef2f7;vertical-align:middle;}
.bs-table tbody tr:nth-child(even){background:#fcfdfc;}
.bs-table tbody tr:hover{background:#f6fbf7;}
.bs-empty-cell{color:#64748b;text-align:center;padding:18px!important;}
.bs-view-btn{-webkit-appearance:none!important;appearance:none!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;min-height:34px!important;border:1px solid #16a34a!important;border-radius:8px!important;background:#f0fdf4!important;color:#166534!important;font-size:12px!important;font-weight:800!important;line-height:1.15!important;padding:6px 10px!important;text-shadow:none!important;box-shadow:none!important;cursor:pointer!important;}
.bs-view-btn:hover,.bs-view-btn:focus{border-color:#166534!important;background:#166534!important;color:#fff!important;text-decoration:none!important;box-shadow:0 0 0 3px rgba(22,101,52,.16)!important;}
.bs-receipt-btn{min-width:120px!important;border:0!important;background:#166534!important;color:#fff!important;white-space:normal!important;text-align:center!important;box-shadow:0 6px 14px rgba(22,101,52,.18)!important;}
.bs-row-selected,.bs-row-selected td{background:#ecfdf3!important;}
.bs-chip{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;padding:4px 10px;font-size:12px;font-weight:800;border:1px solid;white-space:nowrap;}
.bs-chip.ok{color:#166534;background:#dcfce7;border-color:#86efac;}
.bs-chip.warn{color:#9a3412;background:#ffedd5;border-color:#fdba74;}
.bs-chip.neg{color:#991b1b;background:#fee2e2;border-color:#fca5a5;}
.bs-type-send{color:#166534;font-weight:800;}
.bs-type-return{color:#9a3412;font-weight:800;}
.bs-search-wrap{position:relative;}
.bs-search-wrap .bs-input{padding-right:2.35rem;}
.bs-field-clear{position:absolute;top:50%;right:.35rem;transform:translateY(-50%);width:1.65rem;height:1.65rem;border:1px solid var(--bs-border);background:#fff;color:#64748b;border-radius:.42rem;display:none;align-items:center;justify-content:center;font-size:.9rem;cursor:pointer;padding:0;}
.bs-field-clear.show{display:inline-flex;}
.bs-ledger-modal,.bs-picker-modal{position:fixed;inset:0;z-index:99990;display:none;align-items:center;justify-content:center;padding:1rem;box-sizing:border-box;}
.bs-ledger-modal.active,.bs-picker-modal.active{display:flex;}
.bs-ledger-backdrop,.bs-picker-backdrop{position:absolute;inset:0;background:rgba(15,23,42,.55);}
.bs-ledger-dialog{position:relative;width:min(1040px, calc(100vw - 2rem));max-height:calc(100vh - 2rem);background:#fff;border-radius:14px;box-shadow:0 24px 60px rgba(15,23,42,.28);display:flex;flex-direction:column;overflow:hidden;}
.bs-ledger-head,.bs-picker-head{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:14px 16px;border-bottom:1px solid #e5e7eb;background:#f8fafc;}
.bs-ledger-head .bs-subtitle{margin:0;font-size:18px;line-height:1.2;}
.bs-ledger-close,.bs-picker-close{width:34px;height:34px;border:1px solid #dbe4ee!important;border-radius:8px!important;background:#fff!important;color:#334155!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;padding:0!important;font-size:16px!important;font-weight:900!important;cursor:pointer;line-height:1!important;}
.bs-ledger-body{padding:10px;overflow:auto;}
.bs-ledger-table{min-width:760px!important;table-layout:fixed;}
.bs-ledger-table th,.bs-ledger-table td{padding:8px 10px!important;font-size:13px!important;line-height:1.25!important;vertical-align:middle!important;}
.bs-ledger-loading-cell{padding:36px 18px!important;text-align:center!important;background:#fff!important;}
.bs-ledger-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:#334155;font-weight:800;}
.bs-ledger-spinner{width:34px;height:34px;border:4px solid #dbe4ee;border-top-color:#166534;border-radius:50%;animation:bsSpin .8s linear infinite;}
@keyframes bsSpin{to{transform:rotate(360deg);}}
body.bs-ledger-open,body.bs-br-open{overflow:hidden;}
.bs-picker-sheet{position:relative;width:100%;max-width:36rem;background:#fff;border-radius:.75rem;box-shadow:0 1rem 2rem rgba(0,0,0,.18);overflow:hidden;}
.bs-picker-title{font-size:.95rem;font-weight:800;}
.bs-picker-body{padding:.7rem .8rem .8rem;display:flex;flex-direction:column;gap:.45rem;}
.bs-picker-results{max-height:16rem;overflow-y:auto;display:flex;flex-direction:column;gap:.35rem;}
.bs-picker-note{text-align:center;padding:.55rem;color:var(--bs-muted);font-size:.8rem;}
.bs-picker-item{display:block;width:100%;text-align:left;padding:.62rem .7rem;border:1px solid var(--bs-border);border-radius:.55rem;background:#fff;color:var(--bs-text);cursor:pointer;}
.bs-picker-item-main{display:block;font-weight:800;font-size:.88rem;color:var(--bs-text);}
.bs-picker-item-sub{display:block;font-size:.72rem;color:var(--bs-muted);margin-top:.08rem;}

/* Driver-style Basket Return Receipt */
.bs-br-overlay{position:fixed;inset:0;z-index:100001;background:rgba(15,23,42,.55);overflow:auto;padding:24px;display:flex;align-items:flex-start;justify-content:center;font-family:Arial,Helvetica,sans-serif;color:#111;}
.bs-br-modal{width:min(560px, calc(100vw - 40px));background:#f3f4f6;border-radius:16px;padding:14px;box-shadow:0 24px 70px rgba(0,0,0,.35);}
.bs-br-actions{position:sticky;top:0;z-index:5;display:grid;grid-template-columns:1fr 1fr 1fr;gap:9px;margin:0 0 12px;background:#f3f4f6;padding-bottom:8px;}
.bs-br-actions button{border:0!important;border-radius:15px!important;min-height:46px!important;padding:10px 14px!important;font-size:14px!important;font-weight:950!important;cursor:pointer!important;font-family:inherit!important;}
.bs-br-print,.bs-br-share{background:#e9f7ee!important;color:#0B4A2D!important;border:1px solid #cce8d6!important;}
.bs-br-close{background:#0B4A2D!important;color:#fff!important;}
.bs-br-card{border:1px solid #dbe4ee;border-radius:18px;padding:14px;background:#fff;box-shadow:0 8px 22px rgba(10,45,29,.045);}
.bs-br-paper{border:1px solid #d1d5db;background:#fff;padding:16px;color:#111;font-family:Arial,sans-serif;box-sizing:border-box;}
.bs-br-head{display:flex;justify-content:space-between;align-items:center;gap:12px;border-bottom:2px solid #111;padding-bottom:10px;margin-bottom:12px;}
.bs-br-logo{width:176px;height:64px;object-fit:contain;object-position:left center;display:block;}
.bs-br-title{text-align:right;font-size:12px;font-weight:900;letter-spacing:.08em;}
.bs-br-no{text-align:right;font-size:15px;font-weight:900;margin-top:4px;}
.bs-br-info{display:grid;grid-template-columns:1fr 1fr;gap:12px;border-bottom:1px solid #e5e7eb;padding:8px 0 10px;}
.bs-br-field{min-width:0;}
.bs-br-field span{display:block;font-size:12px;font-weight:800;color:#111;margin-bottom:4px;}
.bs-br-field strong{display:block;font-size:15px;font-weight:900;line-height:1.2;word-break:break-word;color:#111;}
.bs-br-qty{font-size:34px;font-weight:950;text-align:center;color:#111;padding:18px 0;}
.bs-br-proof{margin-top:6px;border:1px dashed #cbd5e1;padding:10px;text-align:center;font-size:12px;font-weight:800;color:#111;min-height:58px;}
.bs-br-proof img{display:block;width:100%;max-height:330px;object-fit:contain;margin-top:8px;}
.bs-br-loading{padding:24px;text-align:center;font-weight:900;color:#334155;}
#bsBrPrintArea{display:none!important;}

@media (max-width:1024px){
  .bs-container{max-width:none;margin:0;border-radius:0;padding:.6rem;}
  .bs-card{padding:.6rem;margin-bottom:.5rem;border-radius:.75rem;box-shadow:0 1px 3px rgba(15,23,42,.04);}
  .bs-head{display:flex;align-items:center;margin-bottom:12px;}
  .bs-head h1{font-size:1.35rem;}
  .bs-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:.45rem;}
  .bs-actions{grid-column:1 / -1;}
  .bs-btn{width:100%;min-height:2.35rem;border-radius:.55rem;}
  .bs-table{min-width:880px;}
  .bs-table thead th,.bs-table tbody td{font-size:.78rem;padding:.5rem .6rem;}
  .bs-totals{gap:.45rem;margin-bottom:.6rem;}
  .bs-total-value{font-size:1rem;}
}
@media (max-width:680px){
  .bs-container{padding:.45rem;}
  .bs-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:.4rem;}
  .bs-inpu�ظ�gțK����������
??�Lt{min-height:2.2rem;padding:.45rem .58rem;font-size:.9rem;border-radius:.5rem;}
  .bs-totals{gap:.3rem;margin-bottom:.45rem;}
  .bs-total-label{font-size:.56rem;}
  .bs-total-value{font-size:.84rem;}
  .bs-br-overlay{padding:12px;}
  .bs-br-modal{width:100%;border-radius:12px;padding:12px;}
  .bs-br-info,.bs-br-actions{grid-template-columns:1fr;}
}
@media print{
  html,body{background:#fff!important;width:148mm;min-height:0!important;height:auto!important;overflow:hidden!important;}
  body > *:not(#bsBrPrintArea){display:none!important;}
  body *{visibility:hidden!important;}
  #bsBrPrintArea,#bsBrPrintArea *{visibility:visible!important;}
  #bsBrPrintArea{display:block!important;position:absolute!important;left:0!important;top:0!important;width:100%!important;max-height:190mm!important;overflow:hidden!important;page-break-after:avoid!important;break-after:avoid!important;}
  #bsBrPrintArea .bs-br-card{border:0!important;box-shadow:none!important;margin:0!important;padding:0!important;page-break-inside:avoid!important;break-inside:avoid!important;}
  #bsBrPrintArea .bs-br-paper{height:188mm!important;overflow:hidden!important;page-break-inside:avoid!important;break-inside:avoid!important;}
  .bs-br-actions{display:none!important;}
  @page{size:A5 portrait;margin:6mm;}
}
</style>

<script>
(function(){
  const wrap = document.getElementById('ac-basket-summary-root');
  if (!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE       = wrap.dataset.restNonce || '';
  const REST_SUMMARY_URL = wrap.dataset.restSummaryUrl || '';
  const REST_LEDGER_URL  = wrap.dataset.restLedgerUrl || '';
  const AJAX_URL         = wrap.dataset.ajaxUrl || '';
  const DEBTOR_NONCE     = wrap.dataset.debtorNonce || '';
  const SHOW_DEBTOR_CODE = wrap.dataset.showDebtorCode === '1';
  const RECEIPT_LOGO_URL = wrap.dataset.receiptLogoUrl || '';

  const $ = id => wrap.querySelector('#' + id);
  const pickerState = { items: [], fetchFn: null, onPick: null };
  const ledgerCache = {};
  let pickerTimer = null;
  let currentLedgerRows = [];
  let currentLedgerCustomer = { code: '', name: '' };
  let currentReceiptForShare = null;
  let brJsPdfPromise = null;

  function fmtQty(n){
    const x = Number(n);
    return Number.isFinite(x) ? Math.round(x) : '0';
  }

  function esc(s){
    if (s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
  }

  function showError(title, message){
    if (window.Swal && typeof Swal.fire === 'function') {
      Swal.fire({ icon: 'error', title: title || 'Error', text: message || 'Something went wrong' });
    } else {
      alert((title || 'Error') + '\n' + (message || 'Something went wrong'));
    }
  }

  function chipClass(outstanding){
    const v = Number(outstanding) || 0;
    if (v < 0) return 'neg';
    if (v > 0) return 'warn';
    return 'ok';
  }

  async function apiGet(url){
    const res = await fetch(url, {
      method:'GET',
      credentials:'same-origin',
      headers: { 'Accept':'application/json', 'X-WP-Nonce': REST_NONCE },
      cache:'no-store'
    });

    if (!res.ok) {
      let errMsg = 'HTTP ' + res.status;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  function buildSummaryUrl(){
    const url = new URL(REST_SUMMARY_URL, window.location.origin);
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo   = ($('ac_bs_date_to').value || '').trim();
    const debtorCode = ($('ac_bs_debtor_code').value || '').trim();
    const debtorName = ($('ac_bs_debtor_name').value || '').trim();

    if (debtorCode) url.searchParams.set('debtorCode', debtorCode);
    if (!debtorCode && debtorName) url.searchParams.set('q', debtorName);
    if (dateFrom) url.searchParams.set('dateFrom', dateFrom);
    if (dateTo) url.searchParams.set('dateTo', dateTo);
    url.searchParams.set('limit', '500');
    return url.toString();
  }

  function buildLedgerUrl(debtorCode){
    const url = new URL(REST_LEDGER_URL, window.location.origin);
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo   = ($('ac_bs_date_to').value || '').trim();

    url.searchParams.set('debtorCode', debtorCode);
    if (dateFrom) url.searchParams.set('dateFrom', dateFrom);
    if (dateTo) url.searchParams.set('dateTo', dateTo);
    url.searchParams.set('limit', '200');
    return url.toString();
  }

  function renderTotals(rows){
    const totalsEl = $('ac_bs_totals');
    if (!rows.length) {
      if (totalsEl) totalsEl.style.display = 'none';
      return;
    }

    if (totalsEl) totalsEl.style.display = 'grid';

    const totals = rows.reduce((acc, r) => ({
      send: acc.send + Number(r.sendQty || 0),
      returned: acc.returned + Number(r.returnQty || 0),
      outstanding: acc.outstanding + Number(r.outstandingQty || 0)
    }), { send: 0, returned: 0, outstanding: 0 });

    totalsEl.innerHTML =
      '<div class="bs-total-box"><div class="bs-total-label">Basket Sent</div><div class="bs-total-value">' + fmtQty(totals.send) + '</div></div>' +
      '<div class="bs-total-box"><div class="bs-total-label">Basket Returned</div><div class="bs-total-value">' + fmtQty(totals.returned) + '</div></div>' +
      '<div class="bs-total-box"><div class="bs-total-label">Outstanding Basket</div><div class="bs-total-value">' + fmtQty(totals.outstanding) + '</div></div>';
  }

  function renderSummary(rows){
    wrap._lastRows = rows;
    renderTotals(rows);
    const table = $('ac_bs_rows_table');

    if (!rows.length) {
      table.innerHTML = '<tr><td colspan="8" class="bs-empty-cell">No basket records found.</td></tr>';
      return;
    }

    table.innerHTML = rows.map((r, i) => {
      const code = r.debtorCode || '';
      const name = r.debtorName || '';
      const send = Number(r.sendQty || 0);
      const ret  = Number(r.returnQty || 0);
      const out  = Number(r.outstandingQty || 0);
      const lastDate = r.lastTxnDate || '-';
      const cls = chipClass(out);

      return `<tr data-summary-row="1" data-debtor-code="${esc(code)}">
        <td>${i+1}</td>
        <td>${esc(code)}</td>
        <td>${esc(name)}</td>
        <td>${fmtQty(send)}</td>
        <td>${fmtQty(ret)}</td>
        <td><span class="bs-chip ${cls}">${fmtQty(out)}</span></td>
        <td>${esc(lastDate)}</td>
        <td><button class="bs-view-btn" type="button" data-debtor-code="${esc(code)}" data-debtor-name="${esc(name)}">View</button></td>
      </tr>`;
    }).join('');
  }

  function getRowTxnType(row){
    return String(row?.txnType || row?.txn_type || '').toUpperCase();
  }

  function getRowProofImage(row){
    if (!row) return '';
    const possible = [
      row.proofImage, row.proof_image, row.proofImageUrl, row.proof_image_url,
      row.imageUrl, row.image_url, row.proofUrl, row.proof_url,
      row.returnProofImage, row.return_proof_image, row.basketProofImage,
      row.basket_proof_image, row.attachmentUrl, row.attachment_url
    ];

    for (const v of possible) {
      if (v && String(v).trim() !== '') return String(v).trim();
    }

    if (Array.isArray(row.proofImages) && row.proofImages.length) {
      const first = row.proofImages[0];
      return first.imageUrl || first.image_url || first.url || '';
    }

    if (Array.isArray(row.images) && row.images.length) {
      const first = row.images[0];
      return first.imageUrl || first.image_url || first.url || '';
    }

    return '';
  }

  async function fetchBasketProofImage(row){
    const existing = getRowProofImage(row);
    if (existing) return existing;

    const nonce = wrap.dataset.basketProofNonce || '';
    if (!AJAX_URL || !nonce || !row) return '';

    const fd = new FormData();
    fd.append('action', 'ac_bs_get_basket_proof');
    fd.append('nonce', nonce);
    fd.append('ledgerId', row.id || row.ledgerId || row.ledger_id || row.basketLedgerId || row.basket_ledger_id || row.returnLedgerId || row.return_ledger_id || '');
    fd.append('sourceRef', row.sourceRef || row.source_ref || row.refNo || row.ref_no || row.docNo || row.doc_no || '');
    fd.append('debtorCode', row.debtorCode || row.debtor_code || currentLedgerCustomer.code || '');
    fd.append('debtorName', row.debtorName || row.debtor_name || currentLedgerCustomer.name || '');
    fd.append('txnDate', row.txnDate || row.txn_date || row.date || '');

    try {
      const res = await fetch(AJAX_URL, { method:'POST', credentials:'same-origin', body:fd, cache:'no-store' });
      const data = await res.json();
      if (data && data.success && data.data && data.data.imageUrl) return data.data.imageUrl;
    } catch(e) {
      console.error('Basket proof AJAX failed:', e);
    }

    return '';
  }

  function receiptRef(row){
    return row?.sourceRef || row?.source_ref || row?.refNo || row?.ref_no || row?.docNo || row?.doc_no || row?.id || 'basket-return';
  }

  function receiptFileName(receipt){
    const ref = String(receipt?.ref || receipt?.id || 'basket-return').replace(/[^A-Za-z0-9_-]/g, '-');
    return `Basket-Return-${ref}.pdf`;
  }

  function getRowDriverName(row){
    const possible = [
      row?.driverLogin, row?.driver_login,
      row?.assignedDriverLogin, row?.assigned_driver_login,
      row?.driverName, row?.driver_name,
      row?.createdByLogin, row?.created_by_login,
      row?.createdByUserLogin, row?.created_by_user_login,
      row?.userLogin, row?.user_login,
      row?.createdByName, row?.created_by_name,
      row?.userName, row?.user_name,
      row?.vehiclePlate, row?.vehicle_plate
    ];

    for (const value of possible) {
      const s = String(value || '').trim();
      if (s) return s.toUpperCase();
    }

    return '';
  }

  function basketReceiptCardHtml(receipt){
    const proofHtml = receipt.proofImage
      ? `Image Proof<img src="${esc(receipt.proofImage)}" alt="Basket return proof" decoding="sync">`
      : 'No image proof uploaded';

    return `
      <div class="bs-br-card">
        <div class="bs-br-paper">
          <div class="bs-br-head">
            <div><img class="bs-br-logo" src="${esc(RECEIPT_LOGO_URL)}" alt="Company logo"></div>
            <div>
              <div class="bs-br-title">BASKET RETURN</div>
              <div class="bs-br-no">${esc(receipt.ref)}</div>
            </div>
          </div>
          <div class="bs-br-info">
            <div class="bs-br-field"><span>Customer</span><strong>${esc(receipt.customerName || 'Customer')}</strong></div>
            <div class="bs-br-field"><span>Driver</span><strong>${esc(receipt.driverName || '')}</strong></div>
          </div>
          <div class="bs-br-qty">${esc(receipt.qty)} BASKETS</div>
          <div class="bs-br-proof">${proofHtml}</div>
        </div>
      </div>`;
  }

  function basketReceiptHtml(row, proofImage){
    const receipt = {
      ref: receiptRef(row),
      customerName: row?.debtorName || row?.debtor_name || currentLedgerCustomer.name || currentLedgerCustomer.code || 'Customer',
      driverName: getRowDriverName(row),
      qty: fmtQty(row?.qty || 0),
      proofImage: proofImage || ''
    };

    return `
      <div class="bs-br-overlay" id="acBsBasketReceiptOverlay">
        <div class="bs-br-modal" role="dialog" aria-modal="true" aria-label="Basket Return Receipt">
          <div class="bs-br-actions">
            <button type="button" class="bs-br-print" data-print-current-basket-receipt="1">Print / Save PDF</button>
            <button type="button" class="bs-br-share" data-share-current-basket-receipt="1">Share PDF</button>
            <button type="button" class="bs-br-close" onclick="document.getElementById('ac_bs_receipt_mount').innerHTML='';document.body.classList.remove('bs-br-open')">Close</button>
          </div>
          ${basketReceiptCardHtml(receipt)}
        </div>
      </div>`;
  }

  function basketReceiptLoadingHtml(){
    return `
      <div class="bs-br-overlay" id="acBsBasketReceiptOverlay">
        <div class="bs-br-modal" role="dialog" aria-modal="true" aria-label="Loading Basket Return Receipt">
          <div class="bs-br-actions">
            <button type="button" class="bs-br-close" onclick="document.getElementById('ac_bs_receipt_mount').innerHTML='';document.body.classList.remove('bs-br-open')">Close</button>
          </div>
          <div class="bs-br-card"><div class="bs-br-paper"><div class="bs-br-loading">Loading basket receipt...</div></div></div>
        </div>
      </div>`;
  }

  async function openBasketReceiptByIndex(idx){
    const row = currentLedgerRows[Number(idx)];
    if (!row || getRowTxnType(row) !== 'RETURN') {
      showError('Receipt not available', 'Basket receipt is only available for returned basket movement.');
      return;
    }

    const mount = $('ac_bs_receipt_mount');
    if (!mount) return;

    document.body.classList.add('bs-br-open');
    mount.innerHTML = basketReceiptLoadingHtml();
    const proofImage = await fetchBasketProofImage(row);
    currentReceiptForShare = {
      id: row.id || row.ledgerId || row.ledger_id || idx,
      ref: receiptRef(row),
      customerName: row?.debtorName || row?.debtor_name || currentLedgerCustomer.name || currentLedgerCustomer.code || 'Customer',
      driverName: getRowDriverName(row),
      qty: fmtQty(row?.qty || 0),
      proofUrl: proofImage || ''
    };
    mount.innerHTML = basketReceiptHtml(row, proofImage);
  }

  function loadJsPdf(){
    if (window.jspdf && window.jspdf.jsPDF) return Promise.resolve(window.jspdf.jsPDF);
    if (brJsPdfPromise) return brJsPdfPromise;

    brJsPdfPromise = new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
      script.onload = () => window.jspdf && window.jspdf.jsPDF ? resolve(window.jspdf.jsPDF) : reject(new Error('PDF library did not load.'));
      script.onerror = () => reject(new Error('PDF library could not be loaded.'));
      document.head.appendChild(script);
    });

    return brJsPdfPromise;
  }

  function loadCanvasImage(url){
    if (!url) return Promise.resolve(null);
    return new Promise(resolve => {
      const img = new Image();
      img.crossOrigin = 'anonymous';
      img.onload = () => resolve(img);
      img.onerror = () => resolve(null);
      img.src = url;
    });
  }

  function drawCanvasText(ctx, text, x, y, size=18, color='#111', weight='400', align='left'){
    ctx.fillStyle = color;
    ctx.font = `${weight} ${size}px Arial, sans-serif`;
    ctx.textAlign = align;
    ctx.textBaseline = 'top';
    ctx.fillText(String(text || ''), x, y);
  }

  function drawWrappedCanvasText(ctx, text, x, y, maxWidth, lineHeight, size=18, color='#111', weight='400'){
    const words = String(text || '').split(/\s+/).filter(Boolean);
    let line = '';

    words.forEach(word => {
      const testLine = line ? `${line} ${word}` : word;
      if (ctx.measureText(testLine).width > maxWidth && line) {
        drawCanvasText(ctx, line, x, y, size, color, weight);
        line = word;
        y += lineHeight;
      } else {
        line = testLine;
      }
    });

    if (line) drawCanvasText(ctx, line, x, y, size, color, weight);
    return y + lineHeight;
  }

  function drawCanvasImageContained(ctx, image, x, y, maxW, maxH){
    if (!image) return;
    const ratio = Math.min(maxW / image.width, maxH / image.height);
    const imgW = image.width * ratio;
    const imgH = image.height * ratio;
    ctx.drawImage(image, x + (maxW - imgW) / 2, y + (maxH - imgH) / 2, imgW, imgH);
  }

  function makeReceiptCanvas(receipt, proofImage=null, logoImage=null){
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d��DI��L��������� )
??�S');
    canvas.width = 1240;
    canvas.height = 1754;

    ctx.fillStyle = '#ffffff';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.strokeStyle = '#111111';
    ctx.lineWidth = 2;
    ctx.strokeRect(82, 70, 1076, 1614);

    if (logoImage) {
      drawCanvasImageContained(ctx, logoImage, 138, 112, 360, 128);
    } else {
      drawCanvasText(ctx, 'BASKET RETURN', 140, 130, 30, '#111', '900');
    }

    drawCanvasText(ctx, 'BASKET RETURN', 1100, 130, 24, '#111', '900', 'right');
    drawCanvasText(ctx, receipt.ref || ('BR-' + receipt.id), 1100, 172, 22, '#111', '900', 'right');

    ctx.strokeStyle = '#111111';
    ctx.lineWidth = 4;
    ctx.beginPath();
    ctx.moveTo(140, 278);
    ctx.lineTo(1100, 278);
    ctx.stroke();

    let y = 350;
    drawCanvasText(ctx, 'Customer', 160, y, 22, '#555', '700');
    drawWrappedCanvasText(ctx, receipt.customerName || 'Customer', 160, y + 34, 410, 30, 24, '#111', '800');
    drawCanvasText(ctx, 'Driver', 660, y, 22, '#555', '700');
    drawWrappedCanvasText(ctx, receipt.driverName || '', 660, y + 34, 410, 30, 24, '#111', '800');

    y += 105;
    ctx.strokeStyle = '#e5e7eb';
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(160, y);
    ctx.lineTo(1080, y);
    ctx.stroke();

    drawCanvasText(ctx, `${receipt.qty} BASKETS`, 620, y + 54, 74, '#111', '900', 'center');
    y += 210;

    ctx.strokeStyle = '#cbd5e1';
    ctx.setLineDash([12, 10]);
    ctx.strokeRect(160, y, 920, 560);
    ctx.setLineDash([]);

    if (proofImage) {
      drawCanvasText(ctx, 'Image Proof', 620, y + 28, 22, '#334155', '800', 'center');
      drawCanvasImageContained(ctx, proofImage, 210, y + 82, 820, 420);
    } else {
      drawCanvasText(ctx, 'No image proof uploaded', 620, y + 255, 28, '#64748b', '800', 'center');
    }

    return canvas;
  }

  function buildBasketReceiptPdf(receipt){
    return Promise.all([loadJsPdf(), loadCanvasImage(receipt.proofUrl), loadCanvasImage(RECEIPT_LOGO_URL)])
      .then(([jsPDF, proofImage, logoImage]) => {
        const pdf = new jsPDF({orientation:'portrait', unit:'mm', format:'a5'});
        const canvas = makeReceiptCanvas(receipt, proofImage, logoImage);
        pdf.addImage(canvas.toDataURL('image/jpeg', 0.95), 'JPEG', 0, 0, 148, 210);
        return pdf.output('blob');
      });
  }

  function downloadBlob(blob, fileName){
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  }

  function preloadReceiptImage(url){
    if (!url) return Promise.resolve();

    return new Promise(resolve => {
      const img = new Image();
      const done = () => resolve();
      img.onload = done;
      img.onerror = done;
      img.src = url;

      if (img.complete) resolve();
      setTimeout(done, 2500);
    });
  }

  function waitForElementImages(el){
    const images = Array.from(el.querySelectorAll('img'));
    if (!images.length) return Promise.resolve();

    return Promise.all(images.map(img => new Promise(resolve => {
      if (img.complete && img.naturalWidth > 0) {
        resolve();
        return;
      }

      const done = () => resolve();
      img.addEventListener('load', done, { once:true });
      img.addEventListener('error', done, { once:true });
      setTimeout(done, 2500);
    }))).then(() => undefined);
  }

  function printCurrentBasketReceipt(){
    const receipt = currentReceiptForShare;
    if (!receipt) {
      showError('Receipt not found', 'Please reopen the basket receipt and try again.');
      return;
    }

    buildBasketReceiptPdf(receipt)
      .then(blob => {
        const fileName = receiptFileName(receipt);
        const url = URL.createObjectURL(blob);
        const opened = window.open(url, '_blank', 'noopener');

        if (!opened) {
          downloadBlob(blob, fileName);
          if (window.Swal && typeof Swal.fire === 'function') {
            Swal.fire({ icon:'info', title:'PDF downloaded', text:'Open the downloaded PDF to print or share.' });
          }
        }

        setTimeout(() => URL.revokeObjectURL(url), 60000);
      })
      .catch(() => {
        showError('Unable to prepare PDF', 'Please try again.');
      });
  }

  function shareCurrentBasketReceipt(button){
    const receipt = currentReceiptForShare;
    if (!receipt) {
      showError('Receipt not found', 'Please reopen the basket receipt and try again.');
      return;
    }

    const originalText = button ? button.textContent : '';
    if (button) {
      button.disabled = true;
      button.textContent = 'Preparing...';
    }

    buildBasketReceiptPdf(receipt)
      .then(blob => {
        const fileName = receiptFileName(receipt);
        const file = new File([blob], fileName, {type:'application/pdf'});

        if (!navigator.share || !navigator.canShare || !navigator.canShare({files:[file]})) {
          downloadBlob(blob, fileName);
          if (window.Swal && typeof Swal.fire === 'function') {
            Swal.fire({ icon:'info', title:'PDF downloaded', text:'Attach the downloaded PDF in WhatsApp.' });
          }
          return null;
        }

        return navigator.share({
          title: fileName.replace(/\.pdf$/i, ''),
          text: 'Basket Return PDF',
          files: [file]
        });
      })
      .catch(error => {
        if (error && error.name === 'AbortError') return;
        showError('Unable to prepare PDF', 'Please print or save PDF, then share it in WhatsApp.');
      })
      .finally(() => {
        if (button) {
          button.disabled = false;
          button.textContent = originalText || 'Share PDF';
        }
      });
  }

  function markSelectedDebtorRow(debtorCode){
    wrap.querySelectorAll('[data-summary-row="1"]').forEach(row => {
      row.classList.toggle('bs-row-selected', (row.dataset.debtorCode || '') === debtorCode);
    });
  }

  async function loadSummary(){
    if (!REST_SUMMARY_URL) {
      showError('Missing configuration', 'Summary endpoint missing.');
      return;
    }

    try {
      const data = await apiGet(buildSummaryUrl());
      const rows = Array.isArray(data && data.rows) ? data.rows : [];
      Object.keys(ledgerCache).forEach(k => delete ledgerCache[k]);
      renderSummary(rows);

      const receiptMount = $('ac_bs_receipt_mount');
      if (receiptMount) receiptMount.innerHTML = '';

      currentLedgerRows = [];
      currentLedgerCustomer = { code: '', name: '' };
      $('ac_bs_ledger_title').textContent = 'Basket Movement History';
      $('ac_bs_ledger_table').innerHTML = '<tr><td colspan="8" class="bs-empty-cell">Select a customer to view basket movement history.</td></tr>';
    } catch(err) {
      showError('Failed to load summary', err && err.message ? err.message : 'Please try again.');
      $('ac_bs_rows_table').innerHTML = '<tr><td colspan="8" class="bs-empty-cell">Failed to load summary.</td></tr>';
    }
  }

  function showLedgerLoading(debtorCode, debtorName){
    currentLedgerRows = [];
    currentLedgerCustomer = { code: debtorCode || '', name: debtorName || '' };
    $('ac_bs_ledger_title').textContent = 'Basket Movement History: ' + (debtorName || debtorCode);
    $('ac_bs_ledger_table').innerHTML = `
      <tr>
        <td colspan="8" class="bs-ledger-loading-cell">
          <div class="bs-ledger-loading">
            <div class="bs-ledger-spinner"></div>
            <div>Loading basket movement...</div>
            <div style="font-size:12px;color:#64748b;">${esc(debtorName || debtorCode || 'Customer')}</div>
          </div>
        </td>
      </tr>`;
  }

  function renderLedger(debtorCode, debtorName, rows){
    currentLedgerRows = Array.isArray(rows) ? rows : [];
    currentLedgerCustomer = { code: debtorCode || '', name: debtorName || '' };
    $('ac_bs_ledger_title').textContent = 'Basket Movement History: ' + (debtorName || debtorCode);

    const table = $('ac_bs_ledger_table');
    if (!currentLedgerRows.length) {
      table.innerHTML = '<tr><td colspan="8" class="bs-empty-cell">No basket movement found for this customer.</td></tr>';
      return;
    }

    table.innerHTML = currentLedgerRows.map((r, i) => {
      const txnType = getRowTxnType(r);
      const label = txnType === 'RETURN' ? 'Returned' : 'Sent Out';
      const typeClass = txnType === 'RETURN' ? 'bs-type-return' : 'bs-type-send';
      const receiptBtn = txnType === 'RETURN'
        ? `<button class="bs-view-btn bs-receipt-btn" type="button" data-basket-receipt-idx="${i}">View Basket Receipt</button>`
        : '<span style="color:#94a3b8;">-</span>';

      return `<tr>
        <td>${i+1}</td>
        <td>${esc(r.txnDate || r.txn_date || '-')}</td>
        <td><span class="${typeClass}">${esc(label)}</span></td>
        <td>${fmtQty(r.qty || 0)}</td>
        <td>${esc(r.sourceType || r.source_type || '-')}</td>
        <td>${esc(r.sourceRef || r.source_ref || '-')}</td>
        <td>${esc(r.remark || r.note || '-')}</td>
        <td>${receiptBtn}</td>
      </tr>`;
    }).join('');
  }

  async function loadLedger(debtorCode, debtorName){
    if (!REST_LEDGER_URL) {
      showError('Missing configuration', 'Ledger endpoint missing.');
      return;
    }

    const cacheKey = debtorCode + '|' + ($('ac_bs_date_from').value || '') + '|' + ($('ac_bs_date_to').value || '');
    showLedgerLoading(debtorCode, debtorName);
    markSelectedDebtorRow(debtorCode);
    openLedgerModal();

    if (ledgerCache[cacheKey]) {
      renderLedger(debtorCode, debtorName, ledgerCache[cacheKey]);
      return;
    }

    try {
      const data = await apiGet(buildLedgerUrl(debtorCode));
      const rows = Array.isArray(data && data.rows) ? data.rows : [];
      ledgerCache[cacheKey] = rows;
      renderLedger(debtorCode, debtorName, rows);
    } catch(err) {
      showError('Failed to load movement history', err && err.message ? err.message : 'Please try again.');
      $('ac_bs_ledger_table').innerHTML = '<tr><td colspan="8" class="bs-empty-cell">Failed to load movement history.</td></tr>';
    }
  }

  function updateCustomerClearButton(){
    const hasValue = !!(($('ac_bs_customer_input')?.value || '').trim());
    $('ac_bs_customer_clear')?.classList.toggle('show', hasValue);
  }

  function clearCustomerSelection(){
    $('ac_bs_customer_input').value = '';
    $('ac_bs_debtor_code').value = '';
    $('ac_bs_debtor_name').value = '';
    updateCustomerClearButton();
    loadSummary();
  }

  async function searchCustomersLive(q){
    if (!AJAX_URL || !DEBTOR_NONCE) return [];

    const url = AJAX_URL + '?action=ac_cs_debtor_search&nonce=' + encodeURIComponent(DEBTOR_NONCE) + '&q=' + encodeURIComponent(q);
    const res = await fetch(url, { credentials:'same-origin', cache:'no-store' });
    const data = await res.json();
    if (!data.success) throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');

    const items = data.data?.items || [];
    return items.map(it => {
      const name = it.name || it.debtorName || '';
      const code = it.code || it.debtorCode || '';
      return { label: name || code, meta: (SHOW_DEBTOR_CODE && code) ? code : '', raw: { name, code } };
    });
  }

  function renderPickerNote(msg){
    $('ac_bs_picker_results').innerHTML = '<div class="bs-picker-note">' + esc(msg) + '</div>';
  }

  function renderPickerItems(items){
    if (!items.length) {
      renderPickerNote('No result found');
      return;
    }

    $('ac_bs_picker_results').innerHTML = items.map((it, idx) => `<button type="button" class="bs-picker-item" data-picker-idx="${idx}">
      <span class="bs-picker-item-main">${esc(it.label || '')}</span>
      ${it.meta ? '<span class="bs-picker-item-sub">' + esc(it.meta) + '</span>' : ''}
    </button>`).join('');
  }

  function runPickerSearch(q){
    const query = (q || '').trim();
    clearTimeout(pickerTimer);

    if (query.length < 1) {
      pickerState.items = [];
      renderPickerNote('Type to search');
      return;
    }

    pickerTimer = setTimeout(async function(){
      renderPickerNote('Searching...');
      try {
        pickerState.items = await pickerState.fetchFn(query) || [];
        renderPickerItems(pickerState.items);
      } catch(e) {
        pickerState.items = [];
        renderPickerNote('Failed to load');
      }
    }, 220);
  }

  function openPicker(opts){
    pickerState.items = [];
    pickerState.fetchFn = opts.fetchFn;
    pickerState.onPick = opts.onPick;
    $('ac_bs_picker_title').textContent = opts.title || 'Search';
    $('ac_bs_picker_search').placeholder = opts.placeholder || 'Type to search...';
    $('ac_bs_picker_search').value = '';
    $('ac_bs_picker_modal').classList.add('active');
    renderPickerNote('Type to search');
    setTimeout(() => $('ac_bs_picker_search').focus(), 80);
  }

  function closePicker(){
    $('ac_bs_picker_modal').classList.remove('active');
    $('ac_bs_picker_search').value = '';
    $('ac_bs_picker_results').innerHTML = '';
    pickerState.items = [];
    pickerState.fetchFn = null;
    pickerState.onPick = null;
  }

  function openCustomerPicker(){
    openPicker({
      title:'Select Customer',
      placeholder:'Search customer...',
      fetchFn: searchCustomersLive,
      onPick: function(picked){
        if (!picked) return;
        $('ac_bs_customer_input').value = picked.name || picked.code || '';
        $('ac_bs_debtor_code').value = picked.code || '';
        $('ac_bs_debtor_name').value = picked.name || '';
        updateCustomerClearButton();
        closePicker();
        loadSummary();
      }
    });
  }

  function setDefaultDateRange(){
    const dateFromEl = $('ac_bs_date_from');
    const dateToEl = $('ac_bs_date_to');
    if (!dateFromEl || !dateToEl) return;

    const today = new Date();
    const oneMonthAgo = new Date(today);
    oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);

    function toYmd(d){
      return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
    }

    if (!dateToEl.value) dateToEl.value = toYmd(today);
    if (!dateFromEl.value) dateFromEl.value = toYmd(oneMonthAgo);
  }

  function openLedgerModal(){
    const modal = $('ac_bs_ledger_modal');
    if (!modal) return;
    modal.classList.add('active');
    modal.setAttribute('aria-hidden', 'false');
    document.body.classList.add('bs-ledger-open');
  }

  function closeLedgerModal(){
    const modal = $('ac_bs_ledger_modal');
    if (!modal) return;
    modal.classList.remove('active');
    modal.setAttribute('aria-hidden', 'true');
    document.body.classList.remove('bs-ledger-open');
  }

  $('ac_bs_refresh').addEventListener('click', loadSummary);
  $('ac_bs_rows_table').addEventListener('click', e => {
    const btn = e.target.closest('[data-debtor-code]');
    if (!btn) return;
    const code = btn.dataset.debtorCode || '';
    const name = btn.dataset.debtorName || '';
    if (code) loadLedger(code, name);
  });
  $('ac_bs_ledger_table').addEventListener('click', e => {
    const btn = e.target.closest('[data-basket-receipt-idx]');
    if (!btn) return;
    e.preventDefault();
    e.stopPropagation();
    openBasketReceiptByIndex(btn.dataset.basketReceiptIdx);
  });
  $('ac_bs_receipt_mount').addEventListener('click', e => {
    const printBtn = e.target.closest('[data-print-current-basket-receipt]');
    if (printBtn) {
      e.preventDefault();
      printCurrentBasketReceipt();
      return;
    }

    const btn = e.target.closest('[data-share-current-basket-receipt]');
    if (!btn) return;
    e.preventDefault();
    shareCurrentBasketReceipt(btn);
  });
  $('ac_bs_customer_input').addEventListener('click', openCustomerPicker);
  $('ac_bs_customer_clear').a� )��kZM��������+"�
??�it_doe_conn, $item_code);
                if (is_wp_error($validated_item)) {
                    $wst_doe_error = $validated_item->get_error_message();
                    break;
                }

                $canonical_item_code = (string) ($validated_item['itemCode'] ?? $item_code);
                $canonical_item_name = (string) ($validated_item['description'] ?? $canonical_item_code);
                $original_line = $original_lines[strtoupper($canonical_item_code)] ?? array();
                $uom = trim((string) ($original_line['uom'] ?? ($validated_item['uom'] ?? 'KG'))) ?: 'KG';
                $unit_price = isset($original_line['unitPrice']) ? wst_doe_float($original_line['unitPrice']) : wst_doe_float($validated_item['defaultPrice'] ?? 0);
                $line_location = trim((string) ($original_line['location'] ?? '')) ?: 'HQ';
                $line_amount = round($unit_price * $total_kg, 2);

                $payload_lines[] = array(
                    'itemCode' => $canonical_item_code,
                    'description' => $canonical_item_name,
                    'itemName' => $canonical_item_name,
                    'ItemName' => $canonical_item_name,
                    'itemDesc' => $canonical_item_name,
                    'uom' => $uom,
                    'unitPrice' => $unit_price,
                    'amount' => $line_amount,
                    'taxCode' => 'SR-0',
                    'taxRate' => 0,
                    'packType' => $pack_type,
                    'qty' => $total_kg,
                    'kg' => $kg,
                    'totalKg' => $total_kg,
                    'unitQty' => $unit_qty,
                    'basketQty' => $pack_type === 'BASKET' ? $unit_qty : 0,
                    'cartonQty' => $pack_type === 'CARTON' ? $unit_qty : 0,
                    'location' => $line_location,
                );
            }

            if ($wst_doe_error !== '') {
                // Error already set while validating posted lines.
            } elseif (empty($payload_lines)) {
                $wst_doe_error = 'Add at least one valid item line.';
            } else {
                $client_request_id = 'do-update-' . $posted_doc_no . '-' . gmdate('YmdHis') . '-' . wp_generate_password(6, false, false);
                $payload = array(
                    'type' => 'DELIVERY_ORDER',
                    'subtype' => 'UPDATE',
                    'docNo' => $posted_doc_no,
                    'DocNo' => $posted_doc_no,
                    'docKey' => $posted_doc_key,
                    'DocKey' => $posted_doc_key,
                    'sourceDocNo' => $posted_doc_no,
                    'docDate' => $posted_doc_date,
                    'debtorCode' => $posted_debtor_code,
                    'DebtorCode' => $posted_debtor_code,
                    'debtorName' => $posted_debtor_name,
                    'DebtorName' => $posted_debtor_name,
                    'salesAgent' => $posted_sales_agent,
                    'SalesAgent' => $posted_sales_agent,
                    'location' => 'HQ',
                    'Location' => 'HQ',
                    'remark' => $posted_remark !== '' ? $posted_remark : 'Staff edited existing Delivery Order.',
                    'overwriteLines' => true,
                    'assignedDriverId' => $posted_driver_id,
                    'driverId' => $posted_driver_id,
                    'lines' => $payload_lines,
                    'client_request_id' => $client_request_id,
                );

                $queued = wst_doe_queue_update_job($payload, $client_request_id, $posted_driver_id, $wst_doe_original_job_id);
                if (is_wp_error($queued)) {
                    $wst_doe_error = $queued->get_error_message();
                } else {
                    $redirect = add_query_arg(
                        array(
                            'wst_doe_saved' => (int) $queued,
                            'docNo' => rawurlencode($wst_doe_doc_no),
                        ),
                        $wst_doe_list_url
                    );
                    wp_safe_redirect($redirect);
                    exit;
                }
            }
        }
    }
}

$wst_doe_drivers = get_users(array(
    'role' => 'driver',
    'orderby' => 'display_name',
    'order' => 'ASC',
));
$wst_doe_driver_picker_items = array_map(function($driver) {
    $driver_login = trim((string) $driver->user_login);
    $driver_label = strtoupper($driver_login);

    return array(
        'id' => (int) $driver->ID,
        'name' => $driver_label,
        'login' => $driver_login,
        'label' => $driver_label,
    );
}, $wst_doe_drivers);
$wst_doe_nonce = wp_create_nonce('wst_doe_queue_update');
$wst_doe_item_nonce = wp_create_nonce('ac_itemcode_suggest');
$wst_doe_ajax_url = admin_url('admin-ajax.php');
?>

<div id="acd-resp-root" class="acd-resp-root"
     data-ajax-url="<?php echo esc_attr($wst_doe_ajax_url); ?>"
     data-item-nonce="<?php echo esc_attr($wst_doe_item_nonce); ?>"
     data-list-url="<?php echo esc_attr($wst_doe_list_url); ?>"
     data-saved-job="<?php echo esc_attr($wst_doe_saved_job_id); ?>"
     data-doc-no="<?php echo esc_attr($wst_doe_doc_no); ?>"
     data-customer-name="<?php echo esc_attr($wst_doe_order['debtorName'] !== '' ? $wst_doe_order['debtorName'] : $wst_doe_order['debtorCode']); ?>"
     data-original-driver-id="<?php echo esc_attr((string) $wst_doe_driver_id); ?>"
     data-original-driver-label="<?php echo esc_attr($wst_doe_driver_label !== '' ? strtoupper($wst_doe_driver_label) : 'NO DRIVER'); ?>"
     data-drivers="<?php echo esc_attr(wp_json_encode($wst_doe_driver_picker_items)); ?>">

    <?php if ($wst_doe_error !== ''): ?>
        <div class="acd-resp-alert acd-resp-alert-error"><?php echo esc_html($wst_doe_error); ?></div>
    <?php endif; ?>

    <form method="post" id="acd_resp_do_form">
        <input type="hidden" name="wst_doe_action" value="queue_update">
        <input type="hidden" name="wst_doe_nonce" value="<?php echo esc_attr($wst_doe_nonce); ?>">
        <input type="hidden" name="wst_doe_type" value="DELIVERY_ORDER">
        <input type="hidden" name="wst_doe_subtype" value="UPDATE">
        <input type="hidden" name="wst_doe_doc_no" value="<?php echo esc_attr($wst_doe_doc_no); ?>">
        <input type="hidden" name="wst_doe_doc_key" value="<?php echo esc_attr($wst_doe_doc_key); ?>">
        <input type="hidden" id="acd_resp_do_customer" value="<?php echo esc_attr($wst_doe_order['debtorCode']); ?>">
        <input type="hidden" id="acd_resp_do_customer_name" value="<?php echo esc_attr($wst_doe_order['debtorName']); ?>">
        <input type="hidden" id="acd_resp_do_sales_agent" value="<?php echo esc_attr($wst_doe_order['salesAgent']); ?>">
        <input type="hidden" id="acd_resp_do_driver" name="wst_doe_driver_id" value="<?php echo esc_attr((string) $wst_doe_driver_id); ?>">
        <input type="hidden" id="acd_resp_do_driver_login" value="">
        <div id="acd_resp_do_hidden_lines"></div>

        <div class="acd-resp-edit-banner">
            <div>
                <span>Edit Delivery Order</span>
                <strong><?php echo esc_html($wst_doe_doc_no); ?></strong>
                <small>DocKey <?php echo esc_html((string) $wst_doe_doc_key); ?></small>
            </div>
            <div>
                <span><?php echo esc_html($wst_doe_delivery_status !== '' ? str_replace('_', ' ', $wst_doe_delivery_status) : 'Current DO'); ?></span>
                <strong><?php echo esc_html($wst_doe_driver_label !== '' ? strtoupper($wst_doe_driver_label) : 'NO DRIVER'); ?></strong>
            </div>
        </div>

        <?php if ($wst_doe_delivery_status === 'NEEDS_STAFF_EDIT'): ?>
            <div class="acd-resp-alert acd-resp-alert-warning">Driver sent this DO back because item is not enough. Save here to queue AutoCount update and continue the correction workflow.</div>
        <?php endif; ?>

        <div id="acd-resp-delivery-tab" class="acd-resp-tab-pane active" data-tab="delivery">
            <div class="acd-resp-do-grid">
                <div class="acd-resp-card acd-resp-entry-card">
                    <div class="acd-resp-card-header"><h3>Add Item</h3></div>
                    <div class="acd-resp-card-body">
                        <div class="acd-resp-field">
                            <label>Customer</label>
                            <div class="acd-resp-search-wrap acd-resp-readonly-wrap" id="acdRespDebtorWrapper">
                                <input type="text" id="acdRespDebtorInput" class="acd-resp-input acd-resp-input-readonly" value="<?php echo esc_attr($wst_doe_order['debtorName'] !== '' ? $wst_doe_order['debtorName'] : $wst_doe_order['debtorCode']); ?>" autocomplete="off" readonly aria-readonly="true">
                            </div>
                        </div>
                        <div class="acd-resp-field">
                            <label>Driver</label>
                            <div class="acd-resp-search-wrap">
                                <input type="text" id="acd_resp_do_driver_name" class="acd-resp-input" value="<?php echo esc_attr($wst_doe_driver_label !== '' ? strtoupper($wst_doe_driver_label) : ''); ?>" placeholder="Select driver..." autocomplete="off" readonly>
                                <button type="button" id="acdRespDriverClear" class="acd-resp-field-clear" aria-label="Clear driver">&times;</button>
                            </div>
                        </div>
                        <div class="acd-resp-field">
                            <label>Item Name</label>
                            <div class="acd-resp-search-wrap">
                                <input type="text" id="acd_resp_do_item_name" class="acd-resp-input" placeholder="Search item..." autocomplete="off" readonly>
                                <button type="button" id="acdRespItemClear" class="acd-resp-field-clear" aria-label="Clear item">&times;</button>
                                <input type="hidden" id="acd_resp_do_item" value=""><input type="hidden" id="acd_resp_do_item_display" value="">
                            </div>
                        </div>
                        <div class="acd-resp-field">
                            <label>Type</label>
                            <div class="acd-resp-type-toggle" id="acd_resp_do_pack_type_toggle">
                                <button type="button" class="acd-resp-type-btn active" data-pack-type="BASKET">Basket</button>
                                <button type="button" class="acd-resp-type-btn" data-pack-type="CARTON">Carton</button>
                            </div>
                            <select id="acd_resp_do_pack_type" style="display:none;"><option value="BASKET" selected>Basket</option><option value="CARTON">Carton</option></select>
                        </div>
                        <div class="acd-resp-row-2">
                            <div class="acd-resp-field"><label>Qty</label><input type="number" id="acd_resp_do_qty" class="acd-resp-input" value="" step="1" min="0" placeholder="Qty"></div>
                            <div class="acd-resp-field"><label>Weight (KG)</label><input type="number" id="acd_resp_do_kg" class="acd-resp-input" value="" step="0.01" min="0" placeholder="Weight (KG)"></div>
                        </div>
                        <div class="acd-resp-preview" id="acd_resp_do_line_preview" style="display:none;"></div>
                        <button type="button" id="acd_resp_do_addline" class="acd-resp-btn-primary">Add Item</button>
                    </div>
                </div>
            </div>
            <div class="acd-resp-card acd-resp-items-card">
                <div class="acd-resp-card-header acd-resp-card-header-stack">
                    <div class="acd-resp-lines-head"><h3>Items Detail</h3><span id="acd_resp_do_lines_count_badge" class="acd-resp-lines-badge">0</span></div>
                    <button type="submit" id="acd_resp_do_submit" class="acd-resp-btn-primary acd-resp-save-btn">Queue AutoCount Update</button>
                    <div id="acd_resp_do_success_actions" class="acd-resp-success-actions" style="display:none;" aria-live="polite">
                        <div class="acd-resp-success-text"><span>Update queued</span>: <strong id="acd_resp_do_success_docno"><?php echo esc_html($wst_doe_doc_no); ?></strong></div>
                        <div class="acd-resp-success-btns"><a id="acd_resp_do_status_btn" class="acd-resp-action-btn acd-resp-action-soft" href="<?php echo esc_url($wst_doe_list_url); ?>">View Status / Reprint</a></div>
                    </div>
                </div>
                <div class="acd-resp-card-body">
                    <div class="acd-resp-lines-header"><span>Item</span><span>Type</span><span>Qty</span><span>KG</span><span>Total KG</span><span aria-label="Action">&#9998;</span></div>
                    <div id="acd_resp_do_lines" class="acd-resp-lines-container"><div class="acd-resp-empty">No items added</div></div>
                </div>
            </div>
        </div>
    </form>

    <div class="acd-resp-picker-modal" id="acd_resp_do_picker_modal" aria-hidden="true">
        <div class="acd-resp-picker-backdrop" id="acd_resp_do_picker_backdrop"></div>
        <div class="acd-resp-picker-sheet">
            <div class="acd-resp-picker-head"><div class="acd-resp-picker-title" id="acd_resp_do_picker_title">Search</div><button type="button" class="acd-resp-picker-close" id="acd_resp_do_picker_close" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>
            <div class="acd-resp-picker-body"><input type="text" id="acd_resp_do_picker_search" class="acd-resp-input acd-resp-picker-search" placeholder="Type to search..." autocomplete="off"><div class="acd-resp-picker-results" id="acd_resp_do_picker_results"></div></div>
        </div>
    </div>
</div>

<script type="application/json" id="acd_resp_existing_lines_json"><?php echo wp_json_encode(array_values(array_map(function($line) { return array('itemCode'=>(string)($line['itemCode'] ?? ''),'itemName'=>(string)($line['itemName'] ?? ''),'packType'=>(string)($line['packType'] ?? 'BASKET'),'qty'=>(float)($line['unitQty'] ?? 0),'kg'=>(float)($line['kg'] ?? 0),'total'=>(float)($line['totalKg'] ?? 0)); }, $wst_doe_order['lines']))); ?></script>
<style>
#acd-resp-root{--acd-bg:#f8fafc;--acd-card-bg:#fff;--acd-border:#dbe4ee;--acd-border-strong:#c4d0dd;--acd-text:#0f172a;--acd-muted:#475569;--acd-green:#166534;--acd-green-light:#dcfce7;--acd-green-soft:#f0fdf4;--acd-green-dark:#14532d;--acd-danger:#dc2626;--acd-radius:.75rem;--acd-shadow:0 .75rem 1.75rem rgba(15,23,42,.08);font-family:'Segoe UI',Roboto,system-ui,sans-serif;color:var(--acd-text);background:var(--acd-bg);font-size:1rem;margin:0;padding:0;max-width:none}#acd-resp-root *{box-sizing:border-box}.acd-resp-alert{padding:.8rem .95rem;border-radius:.75rem;margin:0 0 .8rem;font-size:.92rem;font-weight:800}.acd-resp-alert-error{border:1px solid #fecaca;background:#fff1f2;color:#991b1b}.acd-resp-alert-warning{border:1px solid #fed7aa;background:#fff7ed;color:#9a3412}.acd-resp-edit-banner{display:flex;align-items:stretch;justify-content:space-between;gap:.8rem;background:#fff;border:1px solid var(--acd-border);border-radius:.9rem;box-shadow:var(--acd-shadow);padding:.85rem .95rem;margin:0 0 .9rem}.acd-resp-edit-banner>div{display:flex;flex-direction:column;gap:.15rem}.acd-resp-edit-banner>div:last-child{text-align:right}.acd-resp-edit-banner span{font-size:.78rem;font-weight:900;color:var(--acd-muted);text-transform:uppercase}.acd-resp-edit-banner strong{font-size:1.25rem;line-height:1.1;color:#052e16}.acd-resp-edit-banner small{font-size:.82rem;font-weight:800;color:var(--acd-muted)}
#acd-resp-root .acd-resp-do-grid{display:block}.acd-resp-tab-pane{display:block;padding:0}.acd-resp-card{background:var(--acd-card-bg);border:1px solid var(--acd-border);border-radius:var(--acd-radius);box-shadow:var(--acd-shadow);overflow:hidden}.acd-resp-items-card{ma+"�4I�N��������	
/
??�^<?php
/**
 * RESPONSIVE COMBINED: Delivery Order + Basket Return
 * - Desktop: two-column layout for customer + add item
 * - Tablet/mobile: stacked cards with mobile chips for items
 * - Avatar header removed
 * - Modal pickers for customers and items (centered on all devices)
 * - Full button styles restored with strong CSS overrides
 * - Form resets only when user clicks "Clear / New DO"
 * - Sticky tab bar, customer dropdown inside Add Item card
 * - Customer sync between Delivery Order and Basket Return
 * - Basket Return keeps customer after successful save
 * - LINKS REDIRECT: Receipt page = /do-receipt/
 * - UPDATED: Delivery Order payload now includes basketQty, cartonQty, unitQty
 * - UPDATED: Bulk-first delivery order entry groups rows by customer + driver
 * - UPDATED (merge): Duplicate rows merge only when customer+driver+item+type+KG are equal.
 * - KG supports decimals (0.01 step), total KG displayed with 2 decimals.
 */

if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
    echo '<div style="padding:1rem;border:1px solid #fecaca;background:#fff1f2;border-radius:.75rem;color:#991b1b;text-align:center;">
            Please log in to continue.
          </div>';
    return;
}

$current_user = wp_get_current_user();
$current_roles = is_array($current_user->roles ?? null) ? $current_user->roles : [];
$can_create_do = current_user_can('manage_options') || in_array('editor', $current_roles, true);
if (!$can_create_do) {
    echo '<div style="padding:1rem;border:1px solid #fecaca;background:#fff1f2;border-radius:.75rem;color:#991b1b;text-align:center;">
            You do not have permission to create delivery orders.
          </div>';
    return;
}

// -------------------------------------------------------------------
// Shared REST & AJAX data
// -------------------------------------------------------------------
$rest_nonce          = wp_create_nonce('wp_rest');
$ajax_url            = admin_url('admin-ajax.php');
$debtor_nonce        = wp_create_nonce('ac_cs_debtor_search');
$item_suggest_nonce  = wp_create_nonce('ac_itemcode_suggest');
$default_location    = 'HQ';
$today_date          = current_time('Y-m-d');

// Delivery Order endpoints
$receipt_page_base   = 'https://website.ipohserver.com/VegeBasketDO/do-receipt/';
$records_page_base   = 'https://website.ipohserver.com/VegeBasketDO/delivery-order-records/';
$REST_JOB_POST       = rest_url('ac/v1/job');
$REST_JOB_BASE       = rest_url('ac/v1/job/');
$REST_RECEIPT_TOKEN  = rest_url('ac/v1/do-receipt-token');

// Basket Return endpoint
$rest_return_post    = rest_url('ac/v1/basket/return');

$show_debtor_code    = false;
$show_item_code      = false;
// Drivers are WordPress users with role=driver.
$driver_users        = get_users([
    'role'    => 'driver',
    'orderby' => 'display_name',
    'order'   => 'ASC',
]);
$driver_picker_items = array_map(function($driver) {
    $driver_login = trim((string) $driver->user_login);
    $driver_label = strtoupper($driver_login);

    return [
        'id' => (int) $driver->ID,
        'name' => $driver_label,
        'login' => $driver_login,
        'label' => $driver_label,
    ];
}, $driver_users);
?>

<div id="acd-resp-root" class="acd-resp-root"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-receipt-base="<?php echo esc_attr($receipt_page_base); ?>"
     data-records-base="<?php echo esc_attr($records_page_base); ?>"
     data-rest-job-post="<?php echo esc_attr($REST_JOB_POST); ?>"
     data-rest-job-base="<?php echo esc_attr($REST_JOB_BASE); ?>"
     data-rest-receipt-token="<?php echo esc_attr($REST_RECEIPT_TOKEN); ?>"
     data-rest-return-post="<?php echo esc_attr($rest_return_post); ?>"
     data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
     data-debtor-nonce="<?php echo esc_attr($debtor_nonce); ?>"
     data-item-nonce="<?php echo esc_attr($item_suggest_nonce); ?>"
     data-show-debtor-code="<?php echo $show_debtor_code ? '1' : '0'; ?>"
     data-show-item-code="<?php echo $show_item_code ? '1' : '0'; ?>"
     data-default-location="<?php echo esc_attr($default_location); ?>"
     data-today="<?php echo esc_attr($today_date); ?>"
     data-drivers="<?php echo esc_attr(wp_json_encode($driver_picker_items)); ?>">

    <!-- Tab Bar (mobile: always two-column grid; desktop: flex with underline) -->
    <div class="acd-resp-tab-bar">
        <button type="button" class="acd-resp-tab-btn active" data-tab="delivery">📦 Delivery Order</button>
        <button type="button" class="acd-resp-tab-btn" data-tab="basket">🧺 Basket Return</button>
    </div>

    <!-- ==================== DELIVERY TAB ==================== -->
    <div id="acd-resp-delivery-tab" class="acd-resp-tab-pane active" data-tab="delivery">
        <!-- Quick entry form -->
        <div class="acd-resp-do-grid">
            <!-- Add Item Card -->
            <div class="acd-resp-card acd-resp-entry-card">
                <div class="acd-resp-card-header">
                    <h3>Add Item</h3>
                </div>
                <div class="acd-resp-card-body">
                    <input type="hidden" id="acd_resp_do_date" value="<?php echo esc_attr($today_date); ?>">

                    <!-- Customer dropdown -->
                    <div class="acd-resp-field">
                        <label>Customer</label>
                        <div class="acd-resp-search-wrap" id="acdRespDebtorWrapper"
                             data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
                             data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
                            <input type="text" id="acdRespDebtorInput" class="acd-resp-input" placeholder="Search customer..." autocomplete="off" readonly>
                            <button type="button" id="acdRespDebtorClear" class="acd-resp-field-clear" aria-label="Clear customer">✕</button>
                            <input type="hidden" id="acd_resp_do_customer" value="">
                            <input type="hidden" id="acd_resp_do_customer_name" value="">
                            <input type="hidden" id="acd_resp_do_sales_agent" value="">
                            <input type="hidden" id="acd_resp_do_location" value="<?php echo esc_attr($default_location); ?>">
                        </div>
                    </div>

                    <div class="acd-resp-field">
                        <label>Driver</label>
                        <div class="acd-resp-search-wrap">
                            <input type="text" id="acd_resp_do_driver_name" class="acd-resp-input" placeholder="Select driver..." autocomplete="off" readonly>
                            <button type="button" id="acdRespDriverClear" class="acd-resp-field-clear" aria-label="Clear driver">✕</button>
                            <input type="hidden" id="acd_resp_do_driver" value="">
                            <input type="hidden" id="acd_resp_do_driver_login" value="">
                        </div>
                    </div>

                    <div class="acd-resp-field">
                        <label>Item Name</label>
                        <div class="acd-resp-search-wrap">
                            <input type="text" id="acd_resp_do_item_name" class="acd-resp-input" placeholder="Search item..." autocomplete="off" readonly>
                            <button type="button" id="acdRespItemClear" class="acd-resp-field-clear" aria-label="Clear item">✕</button>
                            <input type="hidden" id="acd_resp_do_item" value="">
                            <input type="hidden" id="acd_resp_do_item_display" value="">
                            <input type="hidden" id="acd_resp_do_item_price" value="0">
                        </div>
                    </div>

                    <div class="acd-resp-field">
                        <label>Type</label>
                        <div class="acd-resp-type-toggle" id="acd_resp_do_pack_type_toggle">
                            <button type="button" class="acd-resp-type-btn active" data-pack-type="BASKET">Basket</button>
                            <button type="button" class="acd-resp-type-btn" data-pack-type="CARTON">Carton</button>
                        </div>
                        <select id="acd_resp_do_pack_type" style="display:none;">
                            <option value="BASKET" selected>Basket</option>
                            <option value="CARTON">Carton</option>
                        </select>
                    </div>

                    <div class="acd-resp-row-2">
                        <div class="acd-resp-field">
                            <label>Qty</label>
                            <input type="number" id="acd_resp_do_qty" class="acd-resp-input" value="" step="1" min="0" placeholder="Qty">
                        </div>
                        <div class="acd-resp-field">
                            <label>Weight (KG)</label>
                            <input type="number" id="acd_resp_do_kg" class="acd-resp-input" value="" step="0.01" min="0" placeholder="Weight (KG)">
                        </div>
                    </div>

                    <div class="acd-resp-preview" id="acd_resp_do_line_preview" style="display:none;"></div>
                    <button type="button" id="acd_resp_do_addline" class="acd-resp-btn-primary">Add Item</button>
                </div>
            </div>
        </div>

        <!-- Items Detail Card (full width) -->
        <div class="acd-resp-card acd-resp-items-card">
            <div class="acd-resp-card-header acd-resp-card-header-stack">
                <div class="acd-resp-lines-head">
                    <h3>Items Detail</h3>
                    <span id="acd_resp_do_lines_count_badge" class="acd-resp-lines-badge">0</span>
                </div>

                <button type="button" id="acd_resp_do_submit" class="acd-resp-btn-primary acd-resp-save-btn">Save Delivery Order</button>

                <!-- Success actions panel (hidden initially) -->
                <div id="acd_resp_do_success_actions" class="acd-resp-success-actions" style="display:none;" aria-live="polite">
                    <div class="acd-resp-success-text">
                        <span id="acd_resp_do_success_label">Saved batch</span>:
                        <strong id="acd_resp_do_success_docno">-</strong>
                    </div>
                    <div class="acd-resp-success-btns">
                        <a id="acd_resp_do_receipt_btn"
                           class="acd-resp-action-btn acd-resp-action-soft"
                           href="#"
                           target="_blank"
                           rel="noopener"
                           style="display:none;">
                            View Status
                        </a>
                        <button type="button"
                                id="acd_resp_do_clear_new_btn"
                                class="acd-resp-action-btn acd-resp-action-danger">
                            Clear / New DO
                        </button>
                    </div>
                </div>
            </div>
            <div class="acd-resp-card-body">
                <!-- Item details table header -->
                <div class="acd-resp-lines-header">
                    <span>Item</span>
                    <span>Customer</span>
                    <span>Driver</span>
                    <span>Type</span>
                    <span>Qty</span>
                    <span>KG</span>
                    <span>Total KG</span>
                    <span>Price</span>
                    <span>Total Price</span>
                    <span aria-label="Action">&#9998;</span>
                </div>
                <!-- Lines container -->
                <div id="acd_resp_do_lines" class="acd-resp-lines-container">
                    <div class="acd-resp-empty">No items added</div>
                </div>
            </div>
        </div>
    </div>

    <!-- ==================== BASKET TAB ==================== -->
    <div id="acd-resp-basket-tab" class="acd-resp-tab-pane" data-tab="basket">
        <div class="acd-resp-stack">
            <div class="acd-resp-card acd-resp-basket-card">
                <div class="acd-resp-card-header">
                    <h3>Basket Return</h3>
                </div>
                <div class="acd-resp-card-body">
                    <input type="hidden" id="acd_resp_br_date" value="<?php echo esc_attr($today_date); ?>">

                    <div class="acd-resp-field">
                        <label>Customer</label>
                        <div class="acd-resp-search-wrap" id="acdRespBrDebtorWrapper"
                             data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
                             data-nonce="<?php echo esc_attr($debtor_nonce); ?>">
                            <input type="text" id="acdRespBrDebtorInput" class="acd-resp-input" placeholder="Search customer..." autocomplete="off" readonly>
                            <button type="button" id="acdRespBrDebtorClear" class="acd-resp-field-clear" aria-label="Clear customer">✕</button>
                            <input type="hidden" id="acd_resp_br_debtor_code" value="">
                            <input type="hidden" id="acd_resp_br_debtor_name" value="">
                        </div>
                    </div>

                    <div class="acd-resp-field">
                        <label>Basket Return Qty</label>
                        <input type="number" id="acd_resp_br_qty" class="acd-resp-input" value="" min="1" step="1" placeholder="Basket Return Qty">
                    </div>

                    <div class="acd-resp-field">
                        <label>Proof Image <span class="acd-resp-label-note">Optional</span></label>
                        <input type="file" id="acd_resp_br_proof" class="acd-resp-file-input" accept="image/jpeg,image/png,image/webp" capture="environment">
                    </div>

                    <button type="button" id="acd_resp_br_submit" class="acd-resp-btn-primary">Save Basket Return</button>
                    <div id="acd_resp_br_status" class="acd-resp-status" aria-live="polite"></div>
                </div>
            </div>
        </div>

        <!-- Basket Customer Picker Modal -->
        <div class="acd-resp-picker-modal" id="acd_resp_br_picker_modal" aria-hidden="true">
            <div class="acd-resp-picker-backdrop" id="acd_resp_br_picker_backdrop"></div>
            <div class="acd-resp-picker-sheet">
                <div class="acd-resp-picker-head">
                    <div class="acd-resp-picker-title" id="acd_resp_br_picker_title">Select Customer</div>
                    <button type="button" class="acd-resp-picker-close" id="acd_resp_br_picker_close" aria-label="Close">
                        <span aria-hidden="true">×</span>
                    </button>
                </div>
                <div class="acd-resp-picker-body">
                    <input type="text" id="acd_resp_br_picker_search" class="acd-resp-input acd-resp-picker-search" placeholder="Type to search..." autocomplete="off">
                    <div class="acd-resp-picker-results" id="acd_resp_br_picker_results"></div>
                </div>
            </div>
        </div>
    </div>

    <!-- Shared Picker Modal for Delivery Order -->
    <div class="acd-resp-picker-modal" id="acd_resp_do_picker_modal" aria-hidden="true">
        <div class="acd-resp-picker-backdrop" id="acd_resp_do_picker_backdrop"></div>
        <div class="acd-resp-picker-sheet">
            <div class="acd-resp-picker-head">
                <div class="acd-resp-picker-title" id="acd_resp_do_picker_title">Search</div>
                <button type="button" class="acd-resp-picker-close" id="acd_resp_do_picker_close" aria-label="Close">
                    <span aria-hidden="true">×</span>
                </button>
            </div>
            <div class="acd-resp	
/=F�O��������)���
??�Q<?php
/**
 * BasketDO Assigned Driver Dashboard.
 * Paste into XYZ Insert PHP Code Snippet PHP code box.
 * Paste as-is. Do not create a shortcode inside this code.
 *
 * Workflow:
 * - Staff creates the DO and assigns it to a driver.
 * - Driver sees only jobs assigned to their WordPress user.
 * - Driver confirms received before marking delivered.
 * - Driver cannot create or edit delivery orders from this page.
 */

if (!defined('ABSPATH')) {
    exit;
}

if (!is_user_logged_in()) {
    echo '<div style="max-width:420px;margin:40px auto;padding:22px;border:1px solid #fecaca;border-radius:18px;background:#fff1f2;color:#991b1b;font-family:Arial,sans-serif;text-align:center;">Please log in to continue.</div>';
    return;
}

global $wpdb;

add_filter('show_admin_bar', '__return_false');

$current_user    = wp_get_current_user();
$current_user_id = get_current_user_id();
$user_roles      = (array) $current_user->roles;
$is_driver       = in_array('driver', $user_roles, true);
$is_admin        = current_user_can('manage_options');

if (!$is_driver && !$is_admin) {
    echo '<div style="max-width:420px;margin:40px auto;padding:22px;border:1px solid #fecaca;border-radius:18px;background:#fff1f2;color:#991b1b;font-family:Arial,sans-serif;text-align:center;">This page is for driver accounts only.</div>';
    return;
}

if (!function_exists('bdo_drv_page_url')) {
    function bdo_drv_page_url() {
        $scheme = is_ssl() ? 'https://' : 'http://';
        $host   = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : '';
        $uri    = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';

        return remove_query_arg(['bdo_msg', 'bdo_err', 'bdo_job_id', 'bdo_br_id', 'bdo_tab', 'bdo_filter'], $scheme . $host . $uri);
    }
}

if (!function_exists('bdo_drv_table_exists')) {
    function bdo_drv_table_exists($table_name) {
        global $wpdb;

        return $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table_name)) === $table_name;
    }
}

if (!function_exists('bdo_drv_table_columns')) {
    function bdo_drv_table_columns($table_name) {
        global $wpdb;
        static $cache = [];

        if (isset($cache[$table_name])) {
            return $cache[$table_name];
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table_name);
        $cols       = $wpdb->get_col("SHOW COLUMNS FROM `{$safe_table}`", 0);

        $cache[$table_name] = is_array($cols) ? array_flip($cols) : [];
        return $cache[$table_name];
    }
}

if (!function_exists('bdo_drv_pick')) {
    function bdo_drv_pick($arr, $keys, $fallback = '') {
        if (!is_array($arr)) {
            return $fallback;
        }

        foreach ($keys as $key) {
            if (isset($arr[$key]) && $arr[$key] !== '' && $arr[$key] !== null) {
                return $arr[$key];
            }
        }

        return $fallback;
    }
}

if (!function_exists('bdo_drv_json')) {
    function bdo_drv_json($value) {
        if (is_array($value)) {
            return $value;
        }

        if (!is_string($value) || trim($value) === '') {
            return [];
        }

        $decoded = json_decode($value, true);
        return is_array($decoded) ? $decoded : [];
    }
}

if (!function_exists('bdo_drv_sum_lines')) {
    function bdo_drv_sum_lines($payload) {
        $summary = [
            'items'   => 0,
            'baskets' => 0,
            'cartons' => 0,
            'kg'      => 0,
            'lines'   => [],
        ];

        $lines = [];
        if (isset($payload['lines']) && is_array($payload['lines'])) {
            $lines = $payload['lines'];
        } elseif (isset($payload['Lines']) && is_array($payload['Lines'])) {
            $lines = $payload['Lines'];
        }

        foreach ($lines as $line) {
            if (!is_array($line)) {
                continue;
            }

            $pack_type = strtoupper(trim((string) bdo_drv_pick($line, ['packType', 'PackType'], '')));
            $qty       = (float) bdo_drv_pick($line, ['qty', 'Qty', 'quantity', 'Quantity'], 0);
            $unit_qty  = (float) bdo_drv_pick($line, ['unitQty', 'UnitQty'], 0);
            $basket    = (float) bdo_drv_pick($line, ['basketQty', 'basket_qty', 'BasketQty', 'UDF_BASKET'], 0);
            $carton    = (float) bdo_drv_pick($line, ['cartonQty', 'carton_qty', 'CartonQty', 'UDF_CARTON'], 0);
            $kg_each   = (float) bdo_drv_pick($line, ['kg', 'Kg', 'UDF_WEIGHTKG'], 0);
            $total_kg  = (float) bdo_drv_pick($line, ['totalKg', 'TotalKg', 'totalKG'], 0);

            if ($basket <= 0 && $pack_type === 'BASKET') {
                $basket = $unit_qty > 0 ? $unit_qty : $qty;
            }

            if ($carton <= 0 && $pack_type === 'CARTON') {
                $carton = $unit_qty > 0 ? $unit_qty : $qty;
            }

            if ($total_kg <= 0) {
                $total_kg = $kg_each > 0 && $unit_qty > 0 ? $kg_each * $unit_qty : ($kg_each > 0 ? $kg_each : $qty);
            }

            $item_code = (string) bdo_drv_pick($line, ['itemCode', 'ItemCode'], '');
            $item_name = (string) bdo_drv_pick($line, ['itemName', 'ItemName', 'description', 'Description', 'itemDesc'], $item_code);

            $summary['items']++;
            $summary['baskets'] += $basket;
            $summary['cartons'] += $carton;
            $summary['kg'] += $total_kg;
            $summary['lines'][] = [
                'itemCode' => $item_code,
                'itemName' => $item_name ?: 'Item',
                'packType' => $pack_type ?: '-',
                'qty'      => (int) round($unit_qty > 0 ? $unit_qty : $qty),
                'basket'   => (int) round($basket),
                'carton'   => (int) round($carton),
                'kg'       => (int) round($total_kg),
                'unitKg'   => (int) round($kg_each > 0 ? $kg_each : $total_kg),
                'totalKg'  => (int) round($total_kg),
            ];
        }

        $summary['baskets'] = (int) round($summary['baskets']);
        $summary['cartons'] = (int) round($summary['cartons']);
        $summary['kg']      = (int) round($summary['kg']);

        return $summary;
    }
}

if (!function_exists('bdo_drv_upload_image')) {
    function bdo_drv_upload_image($field_name, $required = true) {
        if (empty($_FILES[$field_name]) || empty($_FILES[$field_name]['name'])) {
            if ($required) {
                return new WP_Error('missing_file', 'Photo proof is required.');
            }

            return [
                'attachment_id' => 0,
                'image_url'     => '',
                'image_path'    => '',
                'mime_type'     => '',
                'file_size'     => 0,
                'uploaded'      => false,
            ];
        }

        $file = $_FILES[$field_name];

        if (!empty($file['error'])) {
            if ((int) $file['error'] === UPLOAD_ERR_NO_FILE && !$required) {
                return [
                    'attachment_id' => 0,
                    'image_url'     => '',
                    'image_path'    => '',
                    'mime_type'     => '',
                    'file_size'     => 0,
                    'uploaded'      => false,
                ];
            }

            return new WP_Error('upload_error', 'Upload failed. Error code: ' . (int) $file['error']);
        }

        if (empty($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) {
            return new WP_Error('invalid_upload', 'Invalid upload request.');
        }

        $max_size = 4 * 1024 * 1024;
        if (!empty($file['size']) && (int) $file['size'] > $max_size) {
            return new WP_Error('file_too_large', 'Photo is too large. Please retake or choose a smaller photo.');
        }

        $allowed_mimes = [
            'jpg|jpeg|jpe' => 'image/jpeg',
            'png'          => 'image/png',
            'webp'         => 'image/webp',
        ];

        require_once ABSPATH . 'wp-admin/includes/file.php';

        $checked = wp_check_filetype_and_ext($file['tmp_name'], $file['name'], $allowed_mimes);
        if (empty($checked['type']) || !in_array($checked['type'], $allowed_mimes, true)) {
            return new WP_Error('invalid_file_type', 'Only JPG, PNG, or WebP photos are allowed.');
        }

        $uploaded = wp_handle_upload($file, [
            'test_form' => false,
            'mimes'     => $allowed_mimes,
        ]);

        if (isset($uploaded['error'])) {
            return new WP_Error('upload_error', $uploaded['error']);
        }

        $file_path = (string) ($uploaded['file'] ?? '');

        return [
            'attachment_id' => 0,
            'image_url'     => esc_url_raw((string) ($uploaded['url'] ?? '')),
            'image_path'    => $file_path,
            'mime_type'     => sanitize_mime_type((string) ($uploaded['type'] ?? '')),
            'file_size'     => ($file_path && file_exists($file_path)) ? (int) filesize($file_path) : 0,
            'uploaded'      => true,
        ];
    }
}

if (!function_exists('bdo_drv_insert_proof')) {
    function bdo_drv_insert_proof($data) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_do_proof_images';
        if (!bdo_drv_table_exists($table)) {
            return false;
        }

        $cols = bdo_drv_table_columns($table);
        $base = [
            'job_id'           => isset($data['job_id']) ? (int) $data['job_id'] : 0,
            'doc_no'           => isset($data['doc_no']) ? sanitize_text_field((string) $data['doc_no']) : '',
            'doc_key'          => isset($data['doc_key']) ? (int) $data['doc_key'] : 0,
            'receipt_token_id' => null,
            'proof_type'       => 'DELIVERY_PROOF',
            'attachment_id'    => isset($data['attachment_id']) ? (int) $data['attachment_id'] : 0,
            'image_url'        => isset($data['image_url']) ? esc_url_raw((string) $data['image_url']) : '',
            'image_path'       => isset($data['image_path']) ? sanitize_text_field((string) $data['image_path']) : '',
            'mime_type'        => isset($data['mime_type']) ? sanitize_mime_type((string) $data['mime_type']) : '',
            'file_size'        => isset($data['file_size']) ? (int) $data['file_size'] : 0,
            'captured_by'      => get_current_user_id(),
            'captured_at'      => current_time('mysql'),
            'created_at'       => current_time('mysql'),
        ];

        $insert = [];
        foreach ($base as $key => $value) {
            if (isset($cols[$key])) {
                $insert[$key] = $value;
            }
        }

        return !empty($insert) && $wpdb->insert($table, $insert) ? (int) $wpdb->insert_id : false;
    }
}

if (!function_exists('bdo_drv_get_do_proof_url')) {
    function bdo_drv_get_do_proof_url($job_id, $doc_key, $doc_no) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_do_proof_images';
        if (!bdo_drv_table_exists($table)) {
            return '';
        }

        $cols  = bdo_drv_table_columns($table);
        $where = [];
        $args  = [];

        if ((int) $job_id > 0 && isset($cols['job_id'])) {
            $where[] = 'job_id = %d';
            $args[] = (int) $job_id;
        }

        if ((int) $doc_key > 0 && isset($cols['doc_key'])) {
            $where[] = 'doc_key = %d';
            $args[] = (int) $doc_key;
        }

        $doc_no = trim((string) $doc_no);
        if ($doc_no !== '' && isset($cols['doc_no'])) {
            $where[] = 'doc_no = %s';
            $args[] = $doc_no;
        }

        if (empty($where)) {
            return '';
        }

        $select = [];
        foreach (['attachment_id', 'image_url'] as $col) {
            if (isset($cols[$col])) {
                $select[] = "`{$col}`";
            }
        }

        if (empty($select)) {
            return '';
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $where_sql  = '(' . implode(' OR ', $where) . ')';

        if (isset($cols['proof_type'])) {
            $where_sql .= ' AND proof_type = %s';
            $args[] = 'DELIVERY_PROOF';
        }

        if (isset($cols['deleted_at'])) {
            $where_sql .= ' AND deleted_at IS NULL';
        }

        $order_col = isset($cols['captured_at']) ? 'captured_at' : 'id';
        $proof = $wpdb->get_row($wpdb->prepare(
            "SELECT " . implode(', ', $select) . " FROM `{$safe_table}` WHERE {$where_sql} ORDER BY `{$order_col}` ASC LIMIT 1",
            $args
        ), ARRAY_A);

        if (!$proof) {
            return '';
        }

        if (!empty($proof['attachment_id'])) {
            $attachment_url = wp_get_attachment_url((int) $proof['attachment_id']);
            if (!empty($attachment_url)) {
                return esc_url_raw($attachment_url);
            }
        }

        return !empty($proof['image_url']) ? esc_url_raw((string) $proof['image_url']) : '';
    }
}

if (!function_exists('bdo_drv_insert_basket_return_proof')) {
    function bdo_drv_insert_basket_return_proof($data) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_basket_return_proof_images';
        if (!bdo_drv_table_exists($table)) {
            return false;
        }

        $cols = bdo_drv_table_columns($table);
        $base = [
            'ledger_id'     => isset($data['ledger_id']) ? (int) $data['ledger_id'] : 0,
            'source_ref'    => isset($data['source_ref']) ? sanitize_text_field((string) $data['source_ref']) : '',
            'debtor_code'   => isset($data['debtor_code']) ? sanitize_text_field((string) $data['debtor_code']) : '',
            'debtor_name'   => isset($data['debtor_name']) ? sanitize_text_field((string) $data['debtor_name']) : '',
            'attachment_id' => isset($data['attachment_id']) ? (int) $data['attachment_id'] : 0,
            'image_url'     => isset($data['image_url']) ? esc_url_raw((string) $data['image_url']) : '',
            'image_path'    => isset($data['image_path']) ? sanitize_text_field((string) $data['image_path']) : '',
            'mime_type'     => isset($data['mime_type']) ? sanitize_mime_type((string) $data['mime_type']) : '',
            'file_size'     => isset($data['file_size']) ? (int) $data['file_size'] : 0,
            'captured_by'   => get_current_user_id(),
            'captured_at'   => current_time('mysql'),
            'created_at'    => current_time('mysql'),
        ];

        $insert = [];
        foreach ($base as $key => $value) {
            if (isset($cols[$key])) {
                $insert[$key] = $value;
            }
        }

        return !empty($insert) && $wpdb->insert($table, $insert) ? (int) $wpdb->insert_id : false;
    }
}

if (!function_exists('bdo_drv_get_basket_return_receipt')) {
    function bdo_drv_get_basket_return_receipt($ledger_id, $ledger_table, $ledger_table_safe, $proof_table, $proof_table_safe, $current_user_id) {
        if (!$ledger_id || !bdo_drv_table_exists($ledger_table)) {
            return null;
        }

        $ledger_cols = bdo_drv_table_columns($ledger_table);
        $where       = 'id = %d AND txn_type = %s';
        $args        = [(int) $ledger_id, 'RETURN'];

        if (isset($ledger_cols['created_by'])) {
            $where .= ' AND created_by = %d';
            $args[] = (int) $current_user_id;
        }

        $row = $GLOBALS['wpdb']->get_row($GLOBALS['wpdb']->prepare("SELECT * FROM `{$ledger_table_safe}` WHERE {$where} LIMIT 1", $args), ARRAY_A);
        if (!$row) {
            return null;
        }

        $proof_url = '';
        if (bdo_drv_table_exists($proof_table)) {
            $proof_cols = bdo_drv_table_columns($proof_table);
            $proof_where = '';
            $proof_args  = [];

            if (isset($proof_cols['ledger_id'])) {
                $proof_where = 'ledger_)���;��P���������E�'
?����le-badge{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border-radius:999rem;
    border:1px solid;
    padding:.1rem .28rem;
    font-size:.48rem;
    font-weight:800;
    text-transform:uppercase;
    letter-spacing:.15px;
    white-space:nowrap;
    line-height:1.02;
}

.do-table-badge-active{
    color:#166534;
    background:#dcfce7;
    border-color:#86efac;
}

.do-table-badge-cancelled{
    color:#9f1239;
    background:#ffe4e6;
    border-color:#fda4af;
}

.do-table-badge-default{
    color:#374151;
    background:#f3f4f6;
    border-color:#d1d5db;
}

/* Desktop */
@media (min-width:1025px){
    .do-table-wrap{
        padding:.75rem;
    }

    .do-table-head{
        margin-bottom:.45rem;
    }

    .do-table-head h2{
        font-size:1.32rem;
    }

    .do-table-filter{
        padding:.65rem;
        margin-bottom:.45rem;
    }

    .do-table-form{
        gap:.45rem;
    }

    .do-table-filter-row{
        grid-template-columns:11rem 10rem 10rem;
        justify-content:start;
        gap:.45rem;
    }

    .do-table-label{
        font-size:.7rem;
    }

    .do-table-input{
        min-height:2.15rem;
        padding:.45rem .6rem;
        font-size:.86rem;
    }

    .do-table-btn{
        min-height:2.15rem;
        font-size:.88rem;
    }

    .do-table-summary{
        font-size:.74rem;
        margin:.1rem 0 .42rem;
    }

    .do-table-grid{
        min-width:0;
        font-size:.7rem;
    }

    .do-table-grid th,
    .do-table-grid td{
        padding:.45rem .42rem;
    }

    .do-table-grid thead th{
        font-size:.6rem;
    }

    .do-table-customer{
        min-width:14rem;
    }

    .do-table-name{
        font-size:.76rem;
    }

    .do-table-code{
        font-size:.6rem;
    }

    .do-table-items{
        min-width:24rem;
    }

    .do-table-itemname{
        font-size:.66rem;
    }

    .do-table-itemmeta{
        font-size:.58rem;
    }

    .do-table-badge{
        font-size:.5rem;
        padding:.1rem .32rem;
    }
}

/* Tablet */
@media (min-width:681px) and (max-width:1024px){
    .do-table-wrap{
        padding:.55rem;
    }

    .do-table-head h2{
        font-size:1.16rem;
    }

    .do-table-filter{
        padding:.5rem;
        margin-bottom:.35rem;
    }

    .do-table-form{
        gap:.35rem;
    }

    .do-table-filter-row{
        grid-template-columns:.9fr 1fr 1fr;
        gap:.35rem;
    }

    .do-table-label{
        font-size:.64rem;
    }

    .do-table-input{
        min-height:2.04rem;
        padding:.42rem .54rem;
        font-size:.82rem;
    }

    .do-table-btn{
        min-height:2.04rem;
        font-size:.84rem;
        padding:.42rem .56rem;
    }

    .do-table-summary{
        font-size:.7rem;
        margin:.08rem 0 .35rem;
    }

    .do-table-grid{
        min-width:50rem;
        font-size:.66rem;
    }

    .do-table-grid th,
    .do-table-grid td{
        padding:.38rem .3rem;
    }

    .do-table-grid thead th{
        font-size:.56rem;
    }

    .do-table-name{
        font-size:.7rem;
    }

    .do-table-code{
        font-size:.56rem;
    }

    .do-table-items{
        min-width:20rem;
    }

    .do-table-itemname{
        font-size:.6rem;
    }

    .do-table-itemmeta{
        font-size:.53rem;
    }

    .do-table-badge{
        font-size:.48rem;
        padding:.1rem .28rem;
    }
}

/* Mobile */
@media (max-width:680px){
    .do-table-wrap{
        padding:.32rem;
        border-radius:0;
    }

    .do-table-head{
        margin-bottom:.28rem;
    }

    .do-table-head h2{
        font-size:1.04rem;
    }

    .do-table-filter{
        padding:.38rem;
        margin-bottom:.28rem;
        border-radius:.5rem;
    }

    .do-table-form{
        gap:.3rem;
    }

    .do-table-filter-row{
        grid-template-columns:.9fr 1fr 1fr;
        gap:.24rem;
    }

    .do-table-label{
        font-size:.58rem;
    }

    .do-table-input{
        min-height:1.92rem;
        padding:.36rem .46rem;
        font-size:.76rem;
        border-radius:.42rem;
    }

    .do-table-btn{
        min-height:1.92rem;
        font-size:.78rem;
        padding:.36rem .5rem;
        border-radius:.42rem;
    }

    .do-table-summary{
        margin:.06rem 0 .28rem;
        font-size:.64rem;
    }

    .do-table-grid{
        min-width:46rem;
        font-size:.62rem;
    }

    .do-table-grid th,
    .do-table-grid td{
        padding:.32rem .26rem;
    }

    .do-table-grid thead th{
        font-size:.52rem;
    }

    .do-table-name{
        font-size:.66rem;
    }

    .do-table-code{
        font-size:.52rem;
    }

    .do-table-items{
        min-width:16rem;
    }

    .do-table-date{
        min-width:5.8rem;
    }

    .do-table-itemname{
        font-size:.58rem;
    }

    .do-table-itemmeta{
        font-size:.5rem;
    }

    .do-table-badge{
        font-size:.44rem;
        padding:.08rem .24rem;
    }
}

/* Typography scale override */
.do-table-wrap{
    --do-fs-base:1rem;        /* desktop target */
    --do-fs-label:.82rem;
    --do-fs-summary:.82rem;
    --do-fs-table:.82rem;
    --do-fs-th:.72rem;
    --do-fs-name:1rem;
    --do-fs-code:.78rem;
    --do-fs-item-name:.84rem;
    --do-fs-item-meta:.74rem;
    --do-fs-badge:.62rem;
}

.do-table-head h2{
    font-size:1.5rem;
}

.do-table-label{
    font-size:var(--do-fs-label);
}

.do-table-input,
.do-table-btn{
    font-size:var(--do-fs-base);
}

.do-table-summary{
    font-size:var(--do-fs-summary);
}

.do-table-grid{
    font-size:var(--do-fs-table);
}

.do-table-grid thead th{
    font-size:var(--do-fs-th);
}

.do-table-name{
    font-size:var(--do-fs-name);
}

.do-table-code{
    font-size:var(--do-fs-code);
}

.do-table-itemname{
    font-size:var(--do-fs-item-name);
}

.do-table-itemmeta,
.do-table-muted{
    font-size:var(--do-fs-item-meta);
}

.do-table-badge{
    font-size:var(--do-fs-badge);
}

/* Desktop */
@media (min-width:1025px){
    .do-table-wrap{
        --do-fs-base:1rem;
        --do-fs-label:.82rem;
        --do-fs-summary:.82rem;
        --do-fs-table:.82rem;
        --do-fs-th:.72rem;
        --do-fs-name:1rem;
        --do-fs-code:.78rem;
        --do-fs-item-name:.84rem;
        --do-fs-item-meta:.74rem;
        --do-fs-badge:.62rem;
    }

    .do-table-head h2{
        font-size:1.5rem;
    }
}

/* Tablet */
@media (min-width:681px) and (max-width:1024px){
    .do-table-wrap{
        --do-fs-base:.92rem;
        --do-fs-label:.76rem;
        --do-fs-summary:.76rem;
        --do-fs-table:.76rem;
        --do-fs-th:.66rem;
        --do-fs-name:.9rem;
        --do-fs-code:.7rem;
        --do-fs-item-name:.78rem;
        --do-fs-item-meta:.68rem;
        --do-fs-badge:.56rem;
    }

    .do-table-head h2{
        font-size:1.28rem;
    }
}

/* Mobile */
@media (max-width:680px){
    .do-table-wrap{
        --do-fs-base:.84rem;
        --do-fs-label:.68rem;
        --do-fs-summary:.68rem;
        --do-fs-table:.68rem;
        --do-fs-th:.58rem;
        --do-fs-name:.8rem;
        --do-fs-code:.62rem;
        --do-fs-item-name:.7rem;
        --do-fs-item-meta:.6rem;
        --do-fs-badge:.5rem;
    }

    .do-table-head h2{
        font-size:1.08rem;
    }
}
</style>�E�'~gHQ��������)��
??�Rid = %d';
                $proof_args[] = (int) $ledger_id;
            } elseif (isset($proof_cols['source_ref']) && !empty($row['source_ref'])) {
                $proof_where = 'source_ref = %s';
                $proof_args[] = (string) $row['source_ref'];
            }

            if ($proof_where !== '') {
                $proof = $GLOBALS['wpdb']->get_row($GLOBALS['wpdb']->prepare("SELECT * FROM `{$proof_table_safe}` WHERE {$proof_where} ORDER BY id DESC LIMIT 1", $proof_args), ARRAY_A);
                if ($proof && !empty($proof['image_url'])) {
                    $proof_url = esc_url_raw((string) $proof['image_url']);
                }
            }
        }

        $date_value = (string) ($row['created_at'] ?? ($row['txn_date'] ?? current_time('mysql')));
        $date_ts    = strtotime($date_value);

        return [
            'id'           => (int) $ledger_id,
            'sourceRef'    => (string) ($row['source_ref'] ?? ('BR-' . (int) $ledger_id)),
            'customerCode' => (string) ($row['debtor_code'] ?? ''),
            'customerName' => (string) ($row['debtor_name'] ?? 'Customer'),
            'qty'          => (int) round((float) ($row['qty'] ?? 0)),
            'date'         => $date_value,
            'displayDate'  => $date_ts ? date('d/m/Y h:i A', $date_ts) : $date_value,
            'driverName'   => strtoupper(trim(wp_get_current_user()->display_name ?: wp_get_current_user()->user_login ?: 'DRIVER')),
            'proofUrl'     => $proof_url,
        ];
    }
}

if (!function_exists('bdo_drv_job_summary')) {
    function bdo_drv_job_summary($job) {
        $payload = bdo_drv_json($job->payload ?? '');
        $result  = bdo_drv_json($job->result ?? '');
        $lines   = bdo_drv_sum_lines($payload);

        $doc_no = bdo_drv_pick($result, ['docNo', 'DocNo', 'doc_no'], '');
        if ($doc_no === '') {
            $doc_no = bdo_drv_pick($payload, ['docNo', 'DocNo', 'sourceDocNo', 'SourceDocNo'], 'JOB-' . (int) ($job->id ?? 0));
        }

        $doc_key       = (int) bdo_drv_pick($result, ['docKey', 'DocKey', 'doc_key'], 0);
        $customer_name = bdo_drv_pick($payload, ['customerName', 'CustomerName', 'debtorName', 'DebtorName', 'customer_name'], 'Customer');
        $customer_code = bdo_drv_pick($payload, ['customerCode', 'CustomerCode', 'debtorCode', 'DebtorCode', 'customer_code'], '');
        $location      = bdo_drv_pick($payload, ['location', 'Location', 'headerLocation', 'HeaderLocation'], 'HQ');

        $address_parts = [];
        foreach (['deliveryAddress', 'DeliveryAddress', 'address', 'Address', 'shipToAddress', 'ShipToAddress', 'address1', 'Address1', 'address2', 'Address2', 'address3', 'Address3', 'address4', 'Address4'] as $key) {
            $value = bdo_drv_pick($payload, [$key], '');
            if ($value !== '') {
                $address_parts[] = $value;
            }
        }

        $created_at = (string) ($job->created_at ?? '');
        $doc_date   = (string) bdo_drv_pick($payload, ['docDate', 'DocDate'], $created_at);
        $date_ts    = strtotime($doc_date);
        $job_id     = (int) ($job->id ?? 0);

        return [
            'id'             => $job_id,
            'docNo'          => (string) $doc_no,
            'docKey'         => $doc_key,
            'customerName'   => (string) $customer_name,
            'customerCode'   => (string) $customer_code,
            'address'        => implode(', ', array_unique($address_parts)),
            'location'       => (string) $location,
            'status'         => (string) ($job->status ?? ''),
            'deliveryStatus' => (string) ($job->delivery_status ?? ''),
            'createdAt'      => $created_at,
            'displayDate'    => $date_ts ? date('d/m/Y', $date_ts) : date('d/m/Y'),
            'proofUrl'       => bdo_drv_get_do_proof_url($job_id, $doc_key, $doc_no),
            'summary'        => $lines,
        ];
    }
}

if (!function_exists('bdo_drv_driver_where')) {
    function bdo_drv_driver_where($jobs_table, $jobs_table_safe, $current_user_id) {
        $cols = bdo_drv_table_columns($jobs_table);

        if (!isset($cols['assigned_driver_id'])) {
            return [
                'sql'    => ' AND 1 = 0 ',
                'args'   => [],
                'usable' => false,
            ];
        }

        return [
            'sql'    => ' AND assigned_driver_id = %d ',
            'args'   => [(int) $current_user_id],
            'usable' => true,
        ];
    }
}

if (!function_exists('bdo_drv_get_assigned_job')) {
    function bdo_drv_get_assigned_job($job_id, $jobs_table, $jobs_table_safe, $current_user_id) {
        if (!$job_id || !bdo_drv_table_exists($jobs_table)) {
            return null;
        }

        $cols = bdo_drv_table_columns($jobs_table);
        if (!isset($cols['assigned_driver_id'])) {
            return null;
        }

        $where = 'id = %d AND assigned_driver_id = %d';
        $args  = [(int) $job_id, (int) $current_user_id];

        return $GLOBALS['wpdb']->get_row($GLOBALS['wpdb']->prepare("SELECT * FROM `{$jobs_table_safe}` WHERE {$where} LIMIT 1", $args));
    }
}

$jobs_table         = $wpdb->prefix . 'ac_jobs';
$ledger_table       = $wpdb->prefix . 'ac_basket_ledger';
$basket_proof_table = $wpdb->prefix . 'ac_basket_return_proof_images';
$jobs_table_safe    = preg_replace('/[^A-Za-z0-9_]/', '', $jobs_table);
$ledger_table_safe  = preg_replace('/[^A-Za-z0-9_]/', '', $ledger_table);
$basket_proof_table_safe = preg_replace('/[^A-Za-z0-9_]/', '', $basket_proof_table);
$jobs_cols          = bdo_drv_table_exists($jobs_table) ? bdo_drv_table_columns($jobs_table) : [];

$today       = current_time('Y-m-d');
$current_url = bdo_drv_page_url();
$form_nonce  = wp_create_nonce('bdo_driver_page_action');

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['bdo_driver_action'])) {
    $action = sanitize_key(wp_unslash($_POST['bdo_driver_action']));

    if (!isset($_POST['bdo_driver_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['bdo_driver_nonce'])), 'bdo_driver_page_action')) {
        wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Security check failed.')], $current_url));
        exit;
    }

    if ($action === 'confirm_received') {
        $job_id = isset($_POST['job_id']) ? absint($_POST['job_id']) : 0;
        $job    = bdo_drv_get_assigned_job($job_id, $jobs_table, $jobs_table_safe, $current_user_id);

        if (!$job) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Assigned delivery not found.')], $current_url));
            exit;
        }

        $delivery_status = strtoupper((string) ($job->delivery_status ?? ''));
        if (in_array($delivery_status, ['DELIVERED', 'COMPLETED', 'CANCELLED'], true)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('This delivery is already closed.')], $current_url));
            exit;
        }

        if (strtoupper((string) ($job->status ?? '')) !== 'SUCCESS') {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Official AutoCount DO is not ready yet. Please ask staff to reprint after sync completes.')], $current_url));
            exit;
        }

        $job_summary = bdo_drv_job_summary($job);
        if (empty($job_summary['docNo']) || strpos((string) $job_summary['docNo'], 'JOB-') === 0) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Official AutoCount DO number is missing. Please ask staff to check this order.')], $current_url));
            exit;
        }

        $update = [];
        if (isset($jobs_cols['delivery_status'])) {
            $update['delivery_status'] = 'OUT_FOR_DELIVERY';
        }
        if (isset($jobs_cols['driver_received_by'])) {
            $update['driver_received_by'] = $current_user_id;
        }
        if (isset($jobs_cols['driver_received_at'])) {
            $update['driver_received_at'] = current_time('mysql');
        }
        if (isset($jobs_cols['driver_receive_note'])) {
            $update['driver_receive_note'] = 'Driver confirmed received printed DO and goods.';
        }
        if (isset($jobs_cols['updated_at'])) {
            $update['updated_at'] = current_time('mysql');
        }

        if (!empty($update)) {
            $wpdb->update($jobs_table, $update, ['id' => $job_id], null, ['%d']);
        }

        wp_safe_redirect(add_query_arg(['bdo_msg' => 'received', 'bdo_job_id' => $job_id, 'bdo_tab' => 'deliveries', 'bdo_filter' => 'assigned'], $current_url));
        exit;
    }

    if ($action === 'complete_delivery') {
        $job_id = isset($_POST['job_id']) ? absint($_POST['job_id']) : 0;
        $job    = bdo_drv_get_assigned_job($job_id, $jobs_table, $jobs_table_safe, $current_user_id);

        if (!$job) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Assigned delivery not found.')], $current_url));
            exit;
        }

        $delivery_status = strtoupper((string) ($job->delivery_status ?? ''));
        if (!in_array($delivery_status, ['OUT_FOR_DELIVERY', 'DRIVER_ACKNOWLEDGED'], true)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Please confirm received before marking delivered.')], $current_url));
            exit;
        }

        if (strtoupper((string) ($job->status ?? '')) !== 'SUCCESS') {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Official AutoCount DO is not ready yet.')], $current_url));
            exit;
        }

        $upload = bdo_drv_upload_image('delivery_proof', false);
        if (is_wp_error($upload)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode($upload->get_error_message())], $current_url));
            exit;
        }

        if (!empty($upload['uploaded']) && !empty($upload['image_url'])) {
            $summary = bdo_drv_job_summary($job);
            bdo_drv_insert_proof(array_merge($upload, [
                'job_id'  => $job_id,
                'doc_no'  => $summary['docNo'],
                'doc_key' => $summary['docKey'],
            ]));
        }

        $update = [];
        if (isset($jobs_cols['delivery_status'])) {
            $update['delivery_status'] = 'DELIVERED';
        }
        if (isset($jobs_cols['delivery_completed_by'])) {
            $update['delivery_completed_by'] = $current_user_id;
        }
        if (isset($jobs_cols['delivery_completed_at'])) {
            $update['delivery_completed_at'] = current_time('mysql');
        }
        if (isset($jobs_cols['delivery_note'])) {
            $update['delivery_note'] = !empty($upload['uploaded']) ? 'Completed with proof of delivery' : 'Completed without proof of delivery';
        }
        if (isset($jobs_cols['updated_at'])) {
            $update['updated_at'] = current_time('mysql');
        }

        if (!empty($update)) {
            $wpdb->update($jobs_table, $update, ['id' => $job_id], null, ['%d']);
        }

        wp_safe_redirect(add_query_arg(['bdo_msg' => 'delivered', 'bdo_job_id' => $job_id, 'bdo_tab' => 'deliveries', 'bdo_filter' => 'delivered'], $current_url));
        exit;
    }

    if ($action === 'report_shortage') {
        $job_id = isset($_POST['job_id']) ? absint($_POST['job_id']) : 0;
        $job    = bdo_drv_get_assigned_job($job_id, $jobs_table, $jobs_table_safe, $current_user_id);

        if (!$job) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Assigned delivery not found.')], $current_url));
            exit;
        }

        $delivery_status = strtoupper((string) ($job->delivery_status ?? ''));
        if (in_array($delivery_status, ['DELIVERED', 'COMPLETED', 'CANCELLED', 'NEEDS_STAFF_EDIT'], true)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('This delivery cannot be sent back for edit now.')], $current_url));
            exit;
        }

        if (strtoupper((string) ($job->status ?? '')) !== 'SUCCESS') {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Official AutoCount DO is not ready yet.')], $current_url));
            exit;
        }

        $update = [];
        if (isset($jobs_cols['delivery_status'])) {
            $update['delivery_status'] = 'NEEDS_STAFF_EDIT';
        }
        if (isset($jobs_cols['delivery_note'])) {
            $update['delivery_note'] = 'Driver reported not enough item. Printed DO should be returned to staff for edit and reprint.';
        }
        if (isset($jobs_cols['driver_receive_note'])) {
            $update['driver_receive_note'] = 'Not enough item. Driver sent DO back to staff for correction.';
        }
        if (isset($jobs_cols['updated_at'])) {
            $update['updated_at'] = current_time('mysql');
        }

        if (!empty($update)) {
            $wpdb->update($jobs_table, $update, ['id' => $job_id], null, ['%d']);
        }

        wp_safe_redirect(add_query_arg(['bdo_msg' => 'shortage', 'bdo_job_id' => $job_id, 'bdo_tab' => 'deliveries', 'bdo_filter' => 'assigned'], $current_url));
        exit;
    }

    if ($action === 'basket_return') {
        $debtor_code = isset($_POST['br_debtor_code']) ? sanitize_text_field(wp_unslash($_POST['br_debtor_code'])) : '';
        $debtor_name = isset($_POST['br_debtor_name']) ? sanitize_text_field(wp_unslash($_POST['br_debtor_name'])) : '';
        $basket_qty  = isset($_POST['br_basket_qty']) ? absint($_POST['br_basket_qty']) : 0;

        if ($debtor_code === '' || $debtor_name === '') {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Please select customer for basket return.')], $current_url));
            exit;
        }

        if ($basket_qty <= 0 || $basket_qty > 9999) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Basket return quantity must be between 1 and 9999.')], $current_url));
            exit;
        }

        if (!bdo_drv_table_exists($ledger_table)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Basket return table is not ready.')], $current_url));
            exit;
        }

        if (!empty($_FILES['basket_return_proof']['name']) && !bdo_drv_table_exists($basket_proof_table)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Basket return proof table is not ready.')], $current_url));
            exit;
        }

        $upload = bdo_drv_upload_image('basket_return_proof', false);
        if (is_wp_error($upload)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode($upload->get_error_message())], $current_url));
            exit;
        }

        $source_ref = 'BR-' . current_time('YmdHis') . '-' . $current_user_id;
        $cols       = bdo_drv_table_columns($ledger_table);
        $base       = [
            'source_ref'  => $source_ref,
            'txn_type'    => 'RETURN',
            'debtor_code' => $debtor_code,
            'debtor_name' => $debtor_name,
            'txn_date'    => $today,
            'qty'         => $basket_qty,
            'created_by'  => $current_user_id,
            'created_at'  => current_time('mysql'),
            'updated_at'  => current_time('mysql'),
            'remark'      => 'Basket return by assigned driver',
            'note'        => 'Basket return by assigned driver',
        ];

        $insert = [];
        foreach ($base as $key => $value) {
            if (isset($cols[$key])) {
                $insert[$key] = $value;
            }
        }

        if (empty($insert) || !$wpdb->insert($ledger_table, $insert)) {
            wp_safe_redirect(add_query_arg(['bdo_err' => rawurlencode('Failed to save basket return.')], $current_url));
            exit;
        }

        $ledger_id = (int) $wpdb->insert_id;
        if (!empty($upload['uploaded'])) {
            bdo_drv_insert_basket_return_proof(array_merge($upload, [
                'ledger_id'   => $ledger_id,
                'source_ref'  => $source_ref,
             )���-�R��������)�&
??�T   'debtor_code' => $debtor_code,
                'debtor_name' => $debtor_name,
            ]));
        }

        wp_safe_redirect(add_query_arg(['bdo_msg' => 'br_saved', 'bdo_br_id' => $ledger_id, 'bdo_tab' => 'return'], $current_url));
        exit;
    }
}

$ajax_url     = admin_url('admin-ajax.php');
$debtor_nonce = wp_create_nonce('ac_cs_debtor_search');
$driver_where = bdo_drv_table_exists($jobs_table) ? bdo_drv_driver_where($jobs_table, $jobs_table_safe, $current_user_id) : ['sql' => ' AND 1 = 0 ', 'args' => [], 'usable' => false];

$stats = [
    'assigned'  => 0,
    'received'  => 0,
    'delivered' => 0,
    'returns'   => 0,
];

$active_jobs    = [];
$delivered_jobs = [];
$return_rows    = [];
$basket_receipts = [];

if (bdo_drv_table_exists($jobs_table) && $driver_where['usable']) {
    $base_args = $driver_where['args'];

    $stats['assigned'] = (int) $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(*) FROM `{$jobs_table_safe}` WHERE job_type = 'DELIVERY_ORDER' AND status = 'SUCCESS' AND delivery_status = 'ASSIGNED' {$driver_where['sql']}",
        $base_args
    ));

    $stats['received'] = (int) $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(*) FROM `{$jobs_table_safe}` WHERE job_type = 'DELIVERY_ORDER' AND status = 'SUCCESS' AND delivery_status IN ('OUT_FOR_DELIVERY','DRIVER_ACKNOWLEDGED') {$driver_where['sql']}",
        $base_args
    ));

    $stats['delivered'] = (int) $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(*) FROM `{$jobs_table_safe}` WHERE job_type = 'DELIVERY_ORDER' AND status = 'SUCCESS' AND delivery_status = 'DELIVERED' AND DATE(COALESCE(delivery_completed_at, updated_at, created_at)) = %s {$driver_where['sql']}",
        array_merge([$today], $base_args)
    ));

    $active_sql = "SELECT * FROM `{$jobs_table_safe}`
        WHERE job_type = 'DELIVERY_ORDER'
        AND status = 'SUCCESS'
        AND delivery_status IN ('ASSIGNED','OUT_FOR_DELIVERY','DRIVER_ACKNOWLEDGED')
        {$driver_where['sql']}
        ORDER BY
            CASE delivery_status
                WHEN 'OUT_FOR_DELIVERY' THEN 1
                WHEN 'DRIVER_ACKNOWLEDGED' THEN 2
                WHEN 'ASSIGNED' THEN 3
                ELSE 4
            END,
            id DESC
        LIMIT 100";

    foreach ((array) $wpdb->get_results($wpdb->prepare($active_sql, $base_args)) as $row) {
        $active_jobs[] = bdo_drv_job_summary($row);
    }

    $delivered_sql = "SELECT * FROM `{$jobs_table_safe}`
        WHERE job_type = 'DELIVERY_ORDER'
        AND status = 'SUCCESS'
        AND delivery_status = 'DELIVERED'
        AND DATE(COALESCE(delivery_completed_at, updated_at, created_at)) = %s
        {$driver_where['sql']}
        ORDER BY COALESCE(delivery_completed_at, updated_at, created_at) DESC
        LIMIT 100";

    foreach ((array) $wpdb->get_results($wpdb->prepare($delivered_sql, array_merge([$today], $base_args))) as $row) {
        $delivered_jobs[] = bdo_drv_job_summary($row);
    }
}

if (bdo_drv_table_exists($ledger_table)) {
    $ledger_cols = bdo_drv_table_columns($ledger_table);
    $date_col    = isset($ledger_cols['created_at']) ? 'created_at' : (isset($ledger_cols['txn_date']) ? 'txn_date' : '');

    if ($date_col !== '' && isset($ledger_cols['txn_type'])) {
        $ledger_driver_sql  = isset($ledger_cols['created_by']) ? ' AND created_by = %d ' : '';
        $ledger_driver_args = isset($ledger_cols['created_by']) ? [$current_user_id] : [];
        $qty_expr           = isset($ledger_cols['qty']) ? 'COALESCE(SUM(ABS(qty)),0)' : 'COUNT(*)';

        $stats['returns'] = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT {$qty_expr} FROM `{$ledger_table_safe}` WHERE txn_type = 'RETURN' AND DATE(`{$date_col}`) = %s {$ledger_driver_sql}",
            array_merge([$today], $ledger_driver_args)
        ));

        $return_sql = "SELECT * FROM `{$ledger_table_safe}`
            WHERE txn_type = 'RETURN'
            AND DATE(`{$date_col}`) = %s
            {$ledger_driver_sql}
            ORDER BY id DESC
            LIMIT 100";

        foreach ((array) $wpdb->get_results($wpdb->prepare($return_sql, array_merge([$today], $ledger_driver_args)), ARRAY_A) as $row) {
            $receipt = bdo_drv_get_basket_return_receipt((int) ($row['id'] ?? 0), $ledger_table, $ledger_table_safe, $basket_proof_table, $basket_proof_table_safe, $current_user_id);
            if (!$receipt) {
                continue;
            }

            $return_rows[] = $receipt;
            $basket_receipts[(string) $receipt['id']] = $receipt;
        }
    }
}

$selected_basket_return_id = isset($_GET['bdo_br_id']) ? absint($_GET['bdo_br_id']) : 0;
$selected_basket_receipt   = $selected_basket_return_id > 0
    ? bdo_drv_get_basket_return_receipt($selected_basket_return_id, $ledger_table, $ledger_table_safe, $basket_proof_table, $basket_proof_table_safe, $current_user_id)
    : null;

if ($selected_basket_receipt) {
    $basket_receipts[(string) $selected_basket_receipt['id']] = $selected_basket_receipt;
}

$active_job = !empty($active_jobs) ? $active_jobs[0] : null;

$message     = isset($_GET['bdo_msg']) ? sanitize_key(wp_unslash($_GET['bdo_msg'])) : '';
$error       = isset($_GET['bdo_err']) ? sanitize_text_field(wp_unslash($_GET['bdo_err'])) : '';
$initial_tab = isset($_GET['bdo_tab']) ? sanitize_key(wp_unslash($_GET['bdo_tab'])) : 'home';
$initial_filter = isset($_GET['bdo_filter']) ? sanitize_key(wp_unslash($_GET['bdo_filter'])) : '';
$driver_name = strtoupper(trim($current_user->display_name ?: $current_user->user_login ?: 'DRIVER'));
$hour        = (int) current_time('H');
$greeting    = 'Good evening,';
if ($hour < 12) {
    $greeting = 'Good morning,';
} elseif ($hour < 18) {
    $greeting = 'Good afternoon,';
}

$alert_html = '';
if ($message === 'received') {
    $alert_html = '<div class="bdo-alert ok">Delivery received. You can now mark it delivered after drop-off.</div>';
} elseif ($message === 'delivered') {
    $alert_html = '<div class="bdo-alert ok">Delivery marked as delivered.</div>';
} elseif ($message === 'br_saved') {
    $alert_html = '<div class="bdo-alert ok">Basket return saved.</div>';
} elseif ($message === 'shortage') {
    $alert_html = '<div class="bdo-alert ok">Sent back to staff for DO edit and reprint.</div>';
}

if ($error !== '') {
    $alert_html = '<div class="bdo-alert err">' . esc_html($error) . '</div>';
}

if (!$driver_where['usable']) {
    $alert_html = '<div class="bdo-alert err">Driver assignment column is missing. Please update the bridge schema before using this page.</div>';
}

$html = <<<'HTML'
<style>
html,body{background:#eef4ef!important}
#wpadminbar,header,footer,.site-header,.site-footer,.elementor-location-header,.elementor-location-footer{display:none!important}
html{margin-top:0!important}
#bdo-driver-app{--main:#0B4A2D;--main-2:#0f6b42;--soft:#eaf5ef;--soft-2:#d7efe2;--ink:#102019;--muted:#617067;--line:#d9e5dd;--danger:#dc2626;min-height:100vh;background:linear-gradient(180deg,#0B4A2D 0,#0B4A2D 210px,#eef4ef 210px,#eef4ef 100%);font-family:Segoe UI,Roboto,Arial,sans-serif;color:var(--ink)}
#bdo-driver-app *{box-sizing:border-box}
.bdo-wrap{max-width:560px;margin:0 auto;min-height:100vh;padding:16px 14px 96px}
.bdo-top{color:#fff;display:flex;justify-content:space-between;align-items:center;gap:12px;padding:4px 2px 16px}
.bdo-brand{font-size:22px;font-weight:900;letter-spacing:-.02em}
.bdo-top-actions{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.bdo-logout{min-height:42px;display:inline-flex;align-items:center;justify-content:center;border-radius:999px;font-size:13px;font-weight:900;line-height:1;white-space:nowrap}
.bdo-logout{background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.28);color:#fff!important;text-decoration:none!important;padding:0 16px}
.bdo-logout:hover,.bdo-logout:focus{background:rgba(255,255,255,.26)!important;border-color:rgba(255,255,255,.42)!important;color:#fff!important;outline:none}
.bdo-hero,.bdo-card{background:#fff;border:1px solid var(--line);box-shadow:0 10px 26px rgba(10,45,29,.07);overflow:hidden}
.bdo-hero{border-radius:24px;padding:16px;box-shadow:0 18px 40px rgba(7,32,20,.18);border-color:rgba(255,255,255,.8);margin-bottom:12px}
.bdo-card{border-radius:22px;margin-bottom:12px}
.bdo-driver-row{display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:14px}
.bdo-avatar-row{display:flex;gap:11px;align-items:center;min-width:0}
.bdo-avatar{width:48px;height:48px;border-radius:18px;background:var(--soft);display:grid;place-items:center;font-size:25px;flex:0 0 auto}
.bdo-greet{color:var(--muted);font-size:12px;font-weight:700}
.bdo-name{font-size:20px;font-weight:900;letter-spacing:-.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bdo-stats{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);border-radius:18px;overflow:hidden;background:#fbfdfb}
.bdo-stat{padding:12px 6px;text-align:center;border:0;border-right:1px solid var(--line);background:transparent;cursor:pointer;font-family:inherit}
.bdo-stat:last-child{border-right:0}
.bdo-stat:hover,.bdo-stat.active{background:var(--soft)}
.bdo-stat strong{display:block;font-size:23px;line-height:1;font-weight:950;color:var(--main);letter-spacing:-.03em}
.bdo-stat span{display:block;margin-top:5px;font-size:10.5px;line-height:1.15;color:#46564c;font-weight:800}
.bdo-panel{display:none;animation:bdoFade .16s ease}
.bdo-panel.active{display:block}
@keyframes bdoFade{from{opacity:.5;transform:translateY(4px)}to{opacity:1;transform:none}}
.bdo-card-head{padding:14px 15px;display:flex;justify-content:space-between;align-items:center;gap:10px;border-bottom:1px solid #eef4f0}
.bdo-card-title{font-weight:950;font-size:16px;letter-spacing:-.01em}
.bdo-card-sub{color:var(--muted);font-size:12px;font-weight:700;margin-top:2px}
.bdo-card-body{padding:15px}
.bdo-quick-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:12px}
.bdo-quick{border:1px solid var(--line);background:#fff;border-radius:20px;padding:14px;text-align:left;cursor:pointer;min-height:95px;box-shadow:0 10px 24px rgba(10,45,29,.05);transition:background .12s ease,border-color .12s ease,box-shadow .12s ease,transform .12s ease}
.bdo-quick:hover,.bdo-quick:focus{background:var(--soft)!important;border-color:var(--soft-2)!important;box-shadow:0 12px 26px rgba(11,74,45,.10)!important;outline:none;transform:translateY(-1px)}
.bdo-quick:active{background:var(--soft-2)!important;transform:none}
.bdo-quick-grid .bdo-quick:nth-child(3){grid-column:1/-1;min-height:82px}
.bdo-quick-icon{width:38px;height:38px;border-radius:15px;background:var(--soft);display:grid;place-items:center;color:var(--main);font-size:20px;margin-bottom:10px}
.bdo-quick strong{display:block;font-size:14px;font-weight:950;color:var(--ink)}
.bdo-quick span{display:block;color:var(--muted);font-size:11.5px;font-weight:700;margin-top:3px;line-height:1.25}
.bdo-alert{margin-bottom:12px;padding:12px 14px;border-radius:16px;font-weight:850;font-size:13px}
.bdo-alert.ok{background:#dcfce7;color:#14532d;border:1px solid #bbf7d0}
.bdo-alert.err{background:#fff1f2;color:#9f1239;border:1px solid #fecdd3}
.bdo-empty{padding:18px;text-align:center;color:var(--muted);font-weight:700;background:#f8fbf9;border-radius:16px}
.bdo-delivery-list{display:grid;gap:10px}
.bdo-delivery-row,.bdo-active-mini{border:1px solid var(--line);border-radius:18px;padding:13px;background:#fff;box-shadow:0 8px 22px rgba(10,45,29,.045)}
.bdo-delivery-top{display:flex;justify-content:space-between;align-items:flex-start;gap:10px;margin-bottom:8px}
.bdo-delivery-title{font-size:15px;font-weight:950;letter-spacing:-.01em}
.bdo-delivery-sub{font-size:12px;color:var(--muted);font-weight:800;margin-top:3px}
.bdo-delivery-metrics,.bdo-job-metrics{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;margin:10px 0}
.bdo-delivery-metrics > span,.bdo-metric{flex:1 1 calc(25% - 6px);max-width:calc(25% - 5px);min-width:92px;background:#f8fbf9;border:1px solid #e6f0ea;border-radius:12px;padding:8px 4px;text-align:center;color:var(--main);font-weight:950;font-size:12px}
.bdo-status-chip{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border-radius:999px;background:var(--soft);color:var(--main);font-size:11px;font-weight:950}
.bdo-status-chip.wait{background:#fff7ed;color:#b45309}
.bdo-job-main{margin:12px 0}
.bdo-job-main h3{margin:0;font-size:20px;font-weight:950;letter-spacing:-.02em}
.bdo-job-main p{margin:5px 0 0;color:var(--muted);font-size:13px;font-weight:700;line-height:1.35}
.bdo-job-note{margin:8px 0 0;color:#43534a;font-size:12px;font-weight:800;line-height:1.35;background:#f8fbf9;border:1px solid #e6f0ea;border-radius:12px;padding:8px 10px}
.bdo-metric strong{display:block;font-size:18px;font-weight:950;color:var(--main);line-height:1}
.bdo-metric span{display:block;font-size:10.5px;color:var(--muted);font-weight:800;margin-top:5px;background:transparent;border:0;padding:0}
.bdo-btn,.bdo-btn-soft{width:100%;border:0;border-radius:15px;min-height:48px;padding:12px 14px;font-size:15px;font-weight:950;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;text-decoration:none!important;gap:8px;font-family:inherit}
.bdo-btn{background:var(--main);color:#fff!important;box-shadow:0 12px 22px rgba(11,74,45,.18)}
.bdo-btn:hover{background:var(--main-2)}
.bdo-btn:disabled{background:#94a3b8;box-shadow:none;cursor:not-allowed}
.bdo-btn-warn{margin-top:9px;background:#fff7ed!important;color:#9a3412!important;border:1px solid #fed7aa!important;box-shadow:none!important}
.bdo-btn-warn:hover,.bdo-btn-warn:focus{background:#ffedd5!important;color:#7c2d12!important;border-color:#fdba74!important;outline:none}
.bdo-btn-soft{background:var(--soft);color:var(--main)!important;border:1px solid var(--soft-2)}
.bdo-btn-soft:hover,.bdo-btn-soft:focus{background:var(--soft-2)!important;border-color:#b9ddc8!important;color:var(--main)!important;outline:none}
.bdo-two{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.bdo-field{margin-bottom:12px}
.bdo-field label{display:block;font-size:12px;color:var(--muted);font-weight:900;margin-bottom:5px}
.bdo-input,.bdo-file{width:100%;min-height:46px;border:1px solid #cddbd2;border-radius:14px;padding:10px 12px;background:#fff;font-size:15px;color:var(--ink);font-family:inherit}
.bdo-input:focus{outline:none;border-color:var(--main);box-shadow:0 0 0 3px rgba(11,74,45,.1)}
.bdo-proof-box{background:#f8fbf9;border:1px dashed #aec8b9;border-radius:18px;padding:13px;margin-top:12px}
.bdo-proof-box strong{display:block;color:var(--main);margin-bottom:5px}
.bdo-proof-box p{margin:0 0 10px;color:var(--muted);font-size:12px;font-weight:700}
.bdo-file-hint{margin:8px 0 0;color:var(--muted);font-size:11px;font-weight:800;line-height:1.3}
.bdo-receipt-card{border:1px solid var(--line);border-radius:18px;padding:14px;background:#fff;margin-top:14px;box-shadow:0 8px 22px rgba(10,45,29,.045)}
.bdo-receipt-paper{border:1px solid #d1d5db;background:#fff;padding:16px;color:#111;font-family:Arial,sans-serif}
.bdo-receipt-head{display:flex;justify-content:space-between;align-items:center;gap:12px;border-bottom:2px solid #111;padding-bottom:10px;margin-bottom:12px}
.bdo-receipt-logo{width:176px;height:64px;object-fit:contain;object-position:left center;display:block}
.bdo-receipt-title{text-align:right;font-size:12px;font-weight:900;letter-spacing:.08em}
.bdo-receipt-no{text-align:right;font-size:15px;font-weight:900;margin-top:4px}
.bdo-receipt-info{display:grid;grid-template-columns:1fr 1fr;gap:12px;border-bottom:1px solid #e5e7eb;padding:8px 0 10px}
.bdo-receipt-field{min-width:0}
.bdo-receipt-field span{display:block;font-size:12px;font-weight:800;color:#555;margin-bottom:4px}
.bdo-receipt-field strong{display:block;font-size:15px;font-weight:900;line-height:1.2;word-break:break-word}
.bdo-receipt-qty{font-size:34px;font-weight:950;text-align:center;color:var(--main);padding:18px 0}
.bdo-receipt-proof{margin-top:6px;border:1px dashed #cbd5e1;padding:10px;text-align:center;font-size:12px;font-weigh)�&ri)S��������� )
?�����ddEventListener('click', e => {
    e.preventDefault();
    e.stopPropagation();
    clearCustomerSelection();
  });
  $('ac_bs_picker_close').addEventListener('click', closePicker);
  $('ac_bs_picker_backdrop').addEventListener('click', closePicker);
  $('ac_bs_picker_search').addEventListener('input', function(){ runPickerSearch(this.value); });
  $('ac_bs_picker_results').addEventListener('click', e => {
    const btn = e.target.closest('[data-picker-idx]');
    if (!btn) return;
    const idx = parseInt(btn.dataset.pickerIdx, 10);
    if (!isNaN(idx) && pickerState.items[idx] && pickerState.onPick) {
      pickerState.onPick(pickerState.items[idx].raw);
    }
  });
  $('ac_bs_ledger_close').addEventListener('click', closeLedgerModal);
  $('ac_bs_ledger_backdrop').addEventListener('click', closeLedgerModal);
  document.addEventListener('keydown', function(e){
    if (e.key === 'Escape') {
      closeLedgerModal();
      const receiptMount = $('ac_bs_receipt_mount');
      if (receiptMount && receiptMount.innerHTML) {
        receiptMount.innerHTML = '';
        document.body.classList.remove('bs-br-open');
      }
    }
  });

  setDefaultDateRange();
  updateCustomerClearButton();
  loadSummary();
})();
</script>� )��k�T��������)�R:
??�[t:800;color:#64748b;min-height:58px}
.bdo-receipt-proof img{display:block;width:100%;max-height:330px;object-fit:contain;margin-top:8px}
.bdo-receipt-actions{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:12px}
.bdo-items{display:grid;gap:9px;margin-top:12px}
.bdo-item{border:1px solid #e6f0ea;background:#fff;border-radius:15px;padding:10px}
.bdo-item-name{font-size:14px;font-weight:950;line-height:1.25;margin-bottom:8px;word-break:break-word}
.bdo-item-grid{display:flex;justify-content:center;gap:6px}
.bdo-item-grid div{flex:1 1 0;background:#f8fbf9;border:1px solid #edf4ef;border-radius:11px;padding:7px 4px;text-align:center;min-width:0}
.bdo-item-grid small{display:block;color:var(--muted);font-size:9px;font-weight:900;text-transform:uppercase;line-height:1}
.bdo-item-grid strong{display:block;color:var(--main);font-size:12px;font-weight:950;margin-top:4px;line-height:1.1}
.bdo-search-wrap{position:relative}
.bdo-search-wrap .bdo-input{padding-right:52px}
.bdo-clear{display:none;position:absolute;right:9px;top:50%;transform:translateY(-50%);width:34px;height:34px;border-radius:11px;border:1px solid var(--line);background:#fff;color:var(--muted);font-size:22px;line-height:1;cursor:pointer}
.bdo-clear.show{display:inline-flex;align-items:center;justify-content:center}
.bdo-bottom-nav{position:fixed;left:50%;transform:translateX(-50%);bottom:0;width:100%;max-width:560px;background:rgba(255,255,255,.96);backdrop-filter:blur(12px);border-top:1px solid var(--line);display:grid;grid-template-columns:repeat(3,1fr);padding:8px 8px 12px;z-index:9999;box-shadow:0 -12px 28px rgba(10,45,29,.09)}
.bdo-nav{border:0;background:transparent;color:#53645a;font-size:11px;font-weight:900;min-height:50px;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px}
.bdo-nav i{font-style:normal;font-size:20px}
.bdo-nav:hover,.bdo-nav:focus{color:var(--main)!important;background:var(--soft)!important;border-radius:14px;outline:none}
.bdo-nav.active{color:var(--main)!important;background:var(--soft)!important;border-radius:14px}
.bdo-picker-modal{display:none;position:fixed;inset:0;z-index:99999;align-items:center;justify-content:center;padding:14px}
.bdo-picker-modal.active{display:flex}
.bdo-picker-backdrop{position:absolute;inset:0;background:rgba(5,20,13,.58)}
.bdo-picker-sheet{position:relative;width:100%;max-width:520px;max-height:86vh;background:#fff;border-radius:22px;overflow:hidden;box-shadow:0 24px 60px rgba(0,0,0,.28)}
.bdo-picker-head{padding:14px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center}
.bdo-picker-title{font-weight:950;font-size:17px}
.bdo-picker-close{width:40px;height:40px;border:0;border-radius:14px;background:var(--main);color:#fff;font-size:26px;font-weight:800;line-height:1;cursor:pointer}
.bdo-picker-body{padding:14px}
.bdo-picker-results{max-height:55vh;overflow-y:auto;margin-top:10px}
.bdo-picker-item{display:block;width:100%;text-align:left;background:#fff;border:1px solid var(--line);border-radius:15px;padding:12px;margin-bottom:8px;cursor:pointer}
.bdo-picker-item:hover,.bdo-picker-item:focus{background:var(--soft)!important;border-color:var(--soft-2)!important;outline:none}
.bdo-picker-main{display:block;font-weight:950;color:var(--ink)}
.bdo-picker-note{padding:18px;text-align:center;color:var(--muted);font-weight:800}
#bdoPrintArea{display:none!important}
@media print{html,body{background:#fff!important;width:148mm;min-height:0!important}body *{visibility:hidden!important}#bdoPrintArea,#bdoPrintArea *{visibility:visible!important}#bdoPrintArea{display:block!important;position:absolute!important;left:0;top:0;width:100%!important;max-height:190mm!important;overflow:hidden!important}.bdo-receipt-card{border:0!important;box-shadow:none!important;margin:0!important;padding:0!important}.bdo-receipt-paper{height:188mm!important;overflow:hidden!important}.bdo-receipt-actions,.bdo-bottom-nav,.bdo-top{display:none!important}@page{size:A5 portrait;margin:6mm}}
@media(max-width:390px){.bdo-stat strong{font-size:20px}.bdo-stat span{font-size:9.5px}.bdo-two,.bdo-quick-grid,.bdo-receipt-actions{grid-template-columns:1fr}.bdo-delivery-metrics > span,.bdo-metric{flex-basis:calc(50% - 6px);max-width:calc(50% - 5px);min-width:0}}
</style>

<div id="bdo-driver-app"
     data-ajax-url="__AJAX_URL__"
     data-debtor-nonce="__DEBTOR_NONCE__"
     data-active-jobs="__ACTIVE_JOBS_JSON__"
     data-delivered-jobs="__DELIVERED_JOBS_JSON__"
     data-return-rows="__RETURN_ROWS_JSON__"
     data-basket-receipts="__BASKET_RECEIPTS_JSON__"
     data-selected-basket-receipt-id="__SELECTED_BASKET_RECEIPT_ID__"
     data-stats="__STATS_JSON__"
     data-initial-tab="__INITIAL_TAB__"
     data-initial-filter="__INITIAL_FILTER__">
    <div class="bdo-wrap">
        <div class="bdo-top">
            <div class="bdo-brand">BasketDO Driver</div>
            <div class="bdo-top-actions">
                <a class="bdo-logout" href="__LOGOUT_URL__">Logout</a>
            </div>
        </div>

        __ALERT_HTML__

        <div class="bdo-hero">
            <div class="bdo-driver-row">
                <div class="bdo-avatar-row">
                    <div class="bdo-avatar">DO</div>
                    <div>
                        <div class="bdo-greet">__GREETING__</div>
                        <div class="bdo-name">__DRIVER_NAME__</div>
                    </div>
                </div>
            </div>
            <div class="bdo-stats">
                <button type="button" class="bdo-stat" data-stat-filter="assigned"><strong id="bdoStatAssigned">0</strong><span>Need<br>Receive</span></button>
                <button type="button" class="bdo-stat" data-stat-filter="received"><strong id="bdoStatReceived">0</strong><span>Out For<br>Delivery</span></button>
                <button type="button" class="bdo-stat" data-stat-filter="delivered"><strong id="bdoStatDelivered">0</strong><span>Delivered<br>Today</span></button>
                <button type="button" class="bdo-stat" data-stat-filter="returns"><strong id="bdoStatReturn">0</strong><span>Return<br>Basket</span></button>
            </div>
        </div>

        <div class="bdo-panel active" data-panel="home">
            <div class="bdo-quick-grid">
                <button type="button" class="bdo-quick" data-open-panel="deliveries" data-set-filter="assigned"><div class="bdo-quick-icon">DO</div><strong>Confirm Receive</strong><span>Confirm printed DO and goods received from staff.</span></button>
                <button type="button" class="bdo-quick" data-open-panel="deliveries" data-set-filter="received"><div class="bdo-quick-icon">POD</div><strong>Mark Delivered</strong><span>Use after drop-off, with optional POD photo.</span></button>
                <button type="button" class="bdo-quick" data-open-panel="return"><div class="bdo-quick-icon">BR</div><strong>Basket Return</strong><span>Record returned baskets with photo proof.</span></button>
            </div>

            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Next Delivery</div><div class="bdo-card-sub">Staff-created DO assigned to this driver</div></div>
                </div>
                <div class="bdo-card-body">
                    <div id="bdoHomeActiveMount"></div>
                </div>
            </div>
        </div>

        <div class="bdo-panel" data-panel="deliveries">
            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Assigned Deliveries</div><div class="bdo-card-sub">Only jobs assigned to this driver account</div></div>
                </div>
                <div class="bdo-card-body">
                    <div id="bdoDeliveryListMount" class="bdo-delivery-list"></div>
                </div>
            </div>
        </div>

        <div class="bdo-panel" data-panel="active">
            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Delivery Action</div><div class="bdo-card-sub">Confirm received first, then mark delivered</div></div>
                </div>
                <div class="bdo-card-body">
                    <div id="bdoActiveMount"></div>
                </div>
            </div>
        </div>

        <div class="bdo-panel" data-panel="return">
            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Basket Return</div><div class="bdo-card-sub">Only tracks basket, not carton</div></div>
                </div>
                <form method="post" enctype="multipart/form-data" class="bdo-card-body" id="bdoBasketReturnForm">
                    <input type="hidden" name="bdo_driver_action" value="basket_return">
                    <input type="hidden" name="bdo_driver_nonce" value="__FORM_NONCE__">
                    <input type="hidden" id="bdo_br_debtor_code" name="br_debtor_code" value="">
                    <input type="hidden" id="bdo_br_debtor_name" name="br_debtor_name" value="">

                    <div class="bdo-field">
                        <label>Customer</label>
                        <div class="bdo-search-wrap">
                            <input type="text" id="bdoBrCustomerInput" class="bdo-input" placeholder="Search customer..." readonly autocomplete="off">
                            <button type="button" class="bdo-clear" id="bdoBrCustomerClear">×</button>
                        </div>
                    </div>

                    <div class="bdo-field">
                        <label>Basket Return Qty</label>
                        <input type="number" class="bdo-input" name="br_basket_qty" id="bdoBrQty" min="1" max="9999" step="1" placeholder="Basket qty" required>
                    </div>

                    <div class="bdo-proof-box">
                        <strong>Photo proof optional</strong>
                        <p>Take a photo of the returned baskets when available.</p>
                        <input type="file" class="bdo-file" name="basket_return_proof" accept="image/jpeg,image/png,image/webp" capture="environment">
                        <div class="bdo-file-hint">JPG, PNG, or WebP only. Max 4 MB.</div>
                    </div>

                    <div style="height:12px"></div>
                    <button type="submit" class="bdo-btn">Save Basket Return</button>
                </form>
                <div class="bdo-card-body" id="bdoBasketReceiptMount" style="display:none"></div>
            </div>
        </div>
    </div>

    <div class="bdo-bottom-nav">
        <button type="button" class="bdo-nav active" data-open-panel="home"><i>H</i><span>Home</span></button>
        <button type="button" class="bdo-nav" data-open-panel="deliveries"><i>DO</i><span>Deliveries</span></button>
        <button type="button" class="bdo-nav" data-open-panel="return"><i>BR</i><span>Return</span></button>
    </div>

    <div class="bdo-picker-modal" id="bdoPickerModal" aria-hidden="true">
        <div class="bdo-picker-backdrop" id="bdoPickerBackdrop"></div>
        <div class="bdo-picker-sheet">
            <div class="bdo-picker-head">
                <div class="bdo-picker-title">Select Customer</div>
                <button type="button" class="bdo-picker-close" id="bdoPickerClose">×</button>
            </div>
            <div class="bdo-picker-body">
                <input type="text" class="bdo-input" id="bdoPickerSearch" placeholder="Type to search..." autocomplete="off">
                <div class="bdo-picker-results" id="bdoPickerResults"></div>
            </div>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
(function(){
    const root = document.getElementById('bdo-driver-app');
    if (!root || root.dataset.ready === '1') return;
    root.dataset.ready = '1';

    const cfg = {
        ajaxUrl: root.dataset.ajaxUrl,
        debtorNonce: root.dataset.debtorNonce
    };

    let activeJobs = JSON.parse(root.dataset.activeJobs || '[]');
    let deliveredJobs = JSON.parse(root.dataset.deliveredJobs || '[]');
    let returnRows = JSON.parse(root.dataset.returnRows || '[]');
    let basketReceipts = JSON.parse(root.dataset.basketReceipts || '{}');
    let selectedBasketReceiptId = root.dataset.selectedBasketReceiptId || '';
    let stats = JSON.parse(root.dataset.stats || '{}');
    let activeJob = activeJobs.length ? activeJobs[0] : null;
    let currentPanel = 'home';
    let deliveryFilter = ['assigned','received','delivered','returns'].includes(root.dataset.initialFilter || '') ? root.dataset.initialFilter : 'assigned';
    let submittingForm = false;
    let brJsPdfPromise = null;
    const receiptLogoUrl = 'https://website.ipohserver.com/VegeBasketDO/wp-content/uploads/2026/05/Untitled-design-15.png';
    const doCompanyName = 'EXCELLENT VEGE SDN. BHD.';
    const doCompanyAddr = 'No. 45, 47, Complex Pasar Borong, 3rd Miles, Jalan Ipoh, 51200 Kuala Lumpur';
    const doCompanyTel = '017-4373 752 / 016-963 752 / 012-3013 752';

    const $ = id => document.getElementById(id);
    const esc = s => String(s ?? '').replace(/[&<>'"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#039;','"':'&quot;'}[c]));
    const whole = n => { const x = Number(n); return (!Number.isFinite(x) || x < 0) ? 0 : Math.round(x); };
    const fmt = n => String(whole(n));
    const toast = (icon, title, text='') => window.Swal
        ? Swal.fire({toast:true, position:'top-end', icon, title, text, showConfirmButton:false, timer:2300})
        : alert(title + (text ? '\n' + text : ''));
    const confirmModal = async (opts) => {
        if (!window.Swal) {
            return confirm(opts.text || opts.title || 'Continue?');
        }

        const result = await Swal.fire({
            icon: opts.icon || 'question',
            title: opts.title || 'Confirm',
            text: opts.text || '',
            showCancelButton: true,
            confirmButtonText: opts.confirmButtonText || 'Confirm',
            cancelButtonText: opts.cancelButtonText || 'Cancel',
            confirmButtonColor: opts.confirmButtonColor || '#0B4A2D',
            cancelButtonColor: '#64748b',
            reverseButtons: true
        });

        return result.isConfirmed;
    };

    function setStats(s) {
        $('bdoStatAssigned').textContent = s.assigned || 0;
        $('bdoStatReceived').textContent = s.received || 0;
        $('bdoStatDelivered').textContent = s.delivered || 0;
        $('bdoStatReturn').textContent = s.returns || 0;
    }

    function updateStatHighlight() {
        document.querySelectorAll('[data-stat-filter]').forEach(btn => {
            btn.classList.toggle('active', currentPanel === 'deliveries' && btn.dataset.statFilter === deliveryFilter);
        });
    }

    function openPanel(name) {
        currentPanel = name;
        document.querySelectorAll('#bdo-driver-app .bdo-panel').forEach(p => p.classList.toggle('active', p.dataset.panel === name));
        document.querySelectorAll('#bdo-driver-app .bdo-nav').forEach(b => b.classList.toggle('active', b.dataset.openPanel === name));
        updateStatHighlight();
        window.scrollTo({top:0, behavior:'smooth'});
    }

    function statusLabel(job) {
        const st = String(job?.deliveryStatus || '').toUpperCase();
        if (st === 'ASSIGNED') return 'NEED RECEIVE';
        if (st === 'OUT_FOR_DELIVERY' || st === 'DRIVER_ACKNOWLEDGED') return 'OUT FOR DELIVERY';
        if (st === 'DELIVERED') return 'DELIVERED';
        return st.replaceAll('_', ' ') || 'READY';
    }

    function packInfo(line) {
        const packType = String(line?.packType || '').toUpperCase();
        const basket = whole(line?.basket || 0);
        const carton = whole(line?.carton || 0);

        if (packType === 'CARTON' || carton > 0) {
            return {label:'Carton', qty:carton || whole(l)�R:��U��������+�IA
??�V<?php
/**
 * BASKET STAFF RETURN LIST
 *
 * Staff-facing basket summary and movement history page.
 * Basket return receipts use the same A5 visual style as the driver basket receipt.
 */

if (!defined('ABSPATH')) exit;

if (!is_user_logged_in()) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">Please log in to view Basket Summary.</div>';
    return;
}

if (!current_user_can('edit_posts') && !current_user_can('manage_options')) {
    echo '<div style="padding:14px;border:1px solid #fecaca;background:#fff1f2;border-radius:12px;color:#991b1b;">You do not have permission to view Basket Summary.</div>';
    return;
}

$rest_nonce         = wp_create_nonce('wp_rest');
$rest_summary_url   = rest_url('ac/v1/basket/summary');
$rest_ledger_url    = rest_url('ac/v1/basket/ledger');
$ajax_url           = admin_url('admin-ajax.php');
$debtor_nonce       = wp_create_nonce('ac_cs_debtor_search');
$basket_proof_nonce = wp_create_nonce('ac_bs_basket_proof');
$receipt_logo_url   = 'https://website.ipohserver.com/VegeBasketDO/wp-content/uploads/2026/05/Untitled-design-15.png';
$show_debtor_code   = false;
?>

<div id="ac-basket-summary-root"
     class="ac-bs-wrap bs-container"
     data-rest-nonce="<?php echo esc_attr($rest_nonce); ?>"
     data-rest-summary-url="<?php echo esc_attr($rest_summary_url); ?>"
     data-rest-ledger-url="<?php echo esc_attr($rest_ledger_url); ?>"
     data-ajax-url="<?php echo esc_attr($ajax_url); ?>"
     data-debtor-nonce="<?php echo esc_attr($debtor_nonce); ?>"
     data-basket-proof-nonce="<?php echo esc_attr($basket_proof_nonce); ?>"
     data-receipt-logo-url="<?php echo esc_url($receipt_logo_url); ?>"
     data-show-debtor-code="<?php echo $show_debtor_code ? '1' : '0'; ?>">

  <div class="bs-head">
    <h1>Basket Summary</h1>
  </div>

  <div class="bs-card">
    <div class="bs-grid">
      <div class="bs-field">
        <label class="bs-label">Customer</label>
        <div class="bs-search-wrap">
          <input type="text" id="ac_bs_customer_input" class="bs-input" placeholder="Search customer to add..." autocomplete="off" readonly>
          <button type="button" id="ac_bs_customer_clear" class="bs-field-clear" aria-label="Clear customer">x</button>
          <input type="hidden" id="ac_bs_debtor_code" value="">
          <input type="hidden" id="ac_bs_debtor_name" value="">
        </div>
        <div id="ac_bs_selected_customers" class="bs-selected-customers"></div>
        <div id="ac_bs_manage_selected" class="bs-manage-selected" aria-hidden="true">
          <div class="bs-manage-head">
            <div>
              <strong>Selected Customers</strong>
              <span id="ac_bs_manage_count">0 selected</span>
            </div>
            <button type="button" class="bs-mini-btn" id="ac_bs_manage_done">Done</button>
          </div>
          <div class="bs-manage-actions">
            <button type="button" class="bs-mini-btn primary" id="ac_bs_manage_add">Add Customer</button>
            <button type="button" class="bs-mini-btn danger" id="ac_bs_manage_clear">Clear All</button>
          </div>
          <div id="ac_bs_manage_list" class="bs-manage-list"></div>
        </div>
      </div>

      <div class="bs-field">
        <label class="bs-label" for="ac_bs_date_from">Date From</label>
        <input id="ac_bs_date_from" type="date" class="bs-input">
      </div>

      <div class="bs-field">
        <label class="bs-label" for="ac_bs_date_to">Date To</label>
        <input id="ac_bs_date_to" type="date" class="bs-input">
      </div>

      <div class="bs-actions">
        <button id="ac_bs_refresh" class="bs-btn" type="button">Refresh Summary</button>
      </div>
    </div>

    <div id="ac_bs_status" class="bs-status"></div>
  </div>

  <div class="bs-card">
    <div class="bs-totals" id="ac_bs_totals"></div>

    <div class="bs-table-wrap">
      <table class="bs-table">
        <thead>
          <tr>
            <th style="width:60px;">No</th>
            <th style="width:140px;">Customer Code</th>
            <th>Customer Name</th>
            <th style="width:120px;">Basket Sent</th>
            <th style="width:130px;">Basket Returned</th>
            <th style="width:150px;">Outstanding Basket</th>
            <th style="width:130px;">Last Activity</th>
            <th style="width:90px;">Action</th>
          </tr>
        </thead>
        <tbody id="ac_bs_rows_table">
          <tr><td colspan="8" class="bs-empty-cell">No data</td></tr>
        </tbody>
      </table>
    </div>
  </div>

  <div class="bs-ledger-modal" id="ac_bs_ledger_modal" aria-hidden="true">
    <div class="bs-ledger-backdrop" id="ac_bs_ledger_backdrop"></div>
    <div class="bs-ledger-dialog">
      <div class="bs-ledger-head">
        <h2 class="bs-subtitle" id="ac_bs_ledger_title">Basket Movement History</h2>
        <button type="button" class="bs-ledger-close" id="ac_bs_ledger_close" aria-label="Close">x</button>
      </div>

      <div class="bs-ledger-body">
        <div class="bs-table-wrap">
          <table class="bs-table bs-ledger-table">
            <thead>
              <tr>
                <th style="width:60px;">No</th>
                <th style="width:120px;">Date</th>
                <th style="width:120px;">Movement</th>
                <th style="width:90px;">Qty</th>
                <th style="width:150px;">From</th>
                <th style="width:140px;">Document No.</th>
                <th>Note</th>
                <th style="width:150px;">Receipt</th>
              </tr>
            </thead>
            <tbody id="ac_bs_ledger_table">
              <tr><td colspan="8" class="bs-empty-cell">Select a customer to view basket movement history.</td></tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </div>

  <div id="ac_bs_receipt_mount"></div>

  <div class="bs-picker-modal" id="ac_bs_picker_modal" aria-hidden="true">
    <div class="bs-picker-backdrop" id="ac_bs_picker_backdrop"></div>
    <div class="bs-picker-sheet">
      <div class="bs-picker-head">
        <div class="bs-picker-title" id="ac_bs_picker_title">Select Customer</div>
        <button type="button" class="bs-picker-close" id="ac_bs_picker_close" aria-label="Close">x</button>
      </div>

      <div class="bs-picker-body">
        <input type="text" id="ac_bs_picker_search" class="bs-input bs-picker-search" placeholder="Search customer..." autocomplete="off">
        <div class="bs-picker-results" id="ac_bs_picker_results"></div>
      </div>
    </div>
  </div>
</div>

<style>
.bs-container{
  --bs-border:#dbe4ee;
  --bs-border-strong:#c4d0dd;
  --bs-text:#0f172a;
  --bs-muted:#475569;
  --bs-green:#0B4A2D;
  --bs-green-soft:#f0fdf4;
  max-width:1360px;
  margin:0 auto;
  padding:16px;
  font-family:"Segoe UI",Roboto,Arial,sans-serif;
  background:linear-gradient(180deg,#f4faf5 0%,#eef7f1 100%);
  border-radius:16px;
  color:var(--bs-text);
  box-sizing:border-box;
}
.bs-head{display:none;}
.bs-card{background:#fff;border:1px solid var(--bs-border);border-radius:14px;box-shadow:0 4px 16px rgba(15,23,42,.05);padding:16px;margin-bottom:14px;box-sizing:border-box;}
.bs-grid{display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:10px;align-items:end;}
.bs-label{display:block;font-size:13px;color:var(--bs-muted);margin-bottom:6px;font-weight:700;}
.bs-input{width:100%;min-height:46px;font-size:15px;padding:10px 12px;border-radius:10px;border:1px solid var(--bs-border-strong);box-sizing:border-box;background:#fff;color:#111;}
.bs-input:focus,.bs-btn:focus,.bs-view-btn:focus{outline:none;border-color:var(--bs-green);box-shadow:0 0 0 3px rgba(11,74,45,.12);}
#ac-basket-summary-root #ac_bs_refresh.bs-btn{min-width:170px;border:0!important;border-radius:12px!important;background:linear-gradient(90deg,#16a34a,#166534)!important;color:#fff!important;font-size:15px!important;font-weight:800!important;padding:10px 18px!important;cursor:pointer;box-shadow:0 8px 18px rgba(22,101,52,.18)!important;}
#ac-basket-summary-root #ac_bs_refresh.bs-btn:hover{background:linear-gradient(90deg,#15803d,#14532d)!important;transform:translateY(-1px);}
.bs-status{display:none;margin-top:10px;font-size:14px;color:#334155;}
.bs-totals{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:14px;}
.bs-total-box{border:1px solid #e5e7eb;background:#f8fafc;border-radius:10px;padding:10px;}
.bs-total-box.issue{border-color:#fecaca;background:#fff7f7;}
.bs-total-label{font-size:12px;color:#64748b;font-weight:700;}
.bs-total-value{font-size:22px;font-weight:800;color:#0f172a;margin-top:4px;}
.bs-total-sub{font-size:12px;color:#64748b;font-weight:700;margin-top:3px;line-height:1.25;}
.bs-table-wrap{width:100%;overflow:auto;border:1px solid #e5e7eb;border-radius:12px;}
.bs-table{width:100%;min-width:980px;border-collapse:collapse;background:#fff;}
.bs-table thead th{background:#f8fafc;color:#334155;font-size:13px;font-weight:900;text-align:left;padding:10px 12px;border-bottom:1px solid #e5e7eb;white-space:nowrap;}
.bs-table tbody td{padding:10px 12px;font-size:14px;line-height:1.25;color:#0f172a;border-bottom:0;vertical-align:top;}
.bs-table tbody tr{box-shadow:inset 0 -1px 0 #edf2f7;}
.bs-table tbody tr:nth-child(odd){background:#ffffff;}
.bs-table tbody tr:nth-child(even){background:#f1f8f3;}
.bs-table tbody tr:hover{background:#e8f5ec;}
.bs-empty-cell{color:#64748b;text-align:center;padding:18px!important;}
.bs-view-btn{-webkit-appearance:none!important;appearance:none!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;min-height:34px!important;border:1px solid #16a34a!important;border-radius:8px!important;background:#f0fdf4!important;color:#166534!important;font-size:12px!important;font-weight:800!important;line-height:1.15!important;padding:6px 10px!important;text-shadow:none!important;box-shadow:none!important;cursor:pointer!important;}
.bs-view-btn:hover,.bs-view-btn:focus{border-color:#166534!important;background:#166534!important;color:#fff!important;text-decoration:none!important;box-shadow:0 0 0 3px rgba(22,101,52,.16)!important;}
.bs-receipt-btn{min-width:120px!important;border:0!important;background:#166534!important;color:#fff!important;white-space:normal!important;text-align:center!important;box-shadow:0 6px 14px rgba(22,101,52,.18)!important;}
.bs-row-selected,.bs-row-selected td{background:#ecfdf3!important;}
.bs-chip{display:inline-flex;align-items:center;justify-content:center;border-radius:999px;padding:4px 10px;font-size:12px;font-weight:800;border:1px solid;white-space:nowrap;}
.bs-chip.ok{color:#166534;background:#dcfce7;border-color:#86efac;}
.bs-chip.warn{color:#9a3412;background:#ffedd5;border-color:#fdba74;}
.bs-chip.neg{color:#991b1b;background:#fee2e2;border-color:#fca5a5;}
.bs-type-send{color:#166534;font-weight:800;}
.bs-type-return{color:#9a3412;font-weight:800;}
.bs-search-wrap{position:relative;}
.bs-search-wrap .bs-input{padding-right:2.35rem;}
.bs-field-clear{position:absolute;top:50%;right:.35rem;transform:translateY(-50%);width:1.65rem;height:1.65rem;border:1px solid var(--bs-border);background:#fff;color:#64748b;border-radius:.42rem;display:none;align-items:center;justify-content:center;font-size:.9rem;cursor:pointer;padding:0;}
.bs-field-clear.show{display:inline-flex;}
.bs-selected-customers{display:flex;flex-wrap:nowrap;align-items:center;gap:6px;margin-top:8px;min-height:30px;overflow:hidden;}
.bs-selected-customers:empty{display:none;}
.bs-selected-chip{display:inline-flex;align-items:center;gap:6px;max-width:150px;min-width:0;border:1px solid #bbf7d0;background:#f0fdf4;color:#166534;border-radius:999px;padding:5px 8px;font-size:12px;font-weight:800;line-height:1.15;}
.bs-selected-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.bs-selected-more{display:inline-flex;align-items:center;flex:0 0 auto;border:1px solid #cbd5e1;background:#f8fafc;color:#334155;border-radius:999px;padding:5px 9px;font-size:12px;font-weight:900;line-height:1.15;}
.bs-manage-toggle{flex:0 0 auto;border:1px solid #166534!important;background:#166534!important;color:#fff!important;border-radius:999px!important;padding:5px 10px!important;font-size:12px!important;font-weight:900!important;line-height:1.15!important;cursor:pointer!important;}
.bs-manage-toggle:hover,.bs-manage-toggle:focus{background:#0f4f2e!important;border-color:#0f4f2e!important;outline:none!important;box-shadow:0 0 0 3px rgba(22,101,52,.16)!important;}
.bs-selected-remove{position:relative;width:18px;height:18px;flex:0 0 18px;border:1px solid #86efac!important;background:#fff!important;color:#166534!important;border-radius:999px!important;display:inline-block!important;padding:0!important;font-size:0!important;line-height:0!important;cursor:pointer!important;vertical-align:middle!important;}
.bs-selected-remove::before,.bs-selected-remove::after{content:"";position:absolute;left:50%;top:50%;width:8px;height:2px;background:currentColor;border-radius:999px;transform-origin:center;}
.bs-selected-remove::before{transform:translate(-50%,-50%) rotate(45deg);}
.bs-selected-remove::after{transform:translate(-50%,-50%) rotate(-45deg);}
.bs-selected-remove:hover,.bs-selected-remove:focus{background:#166534!important;color:#fff!important;border-color:#166534!important;outline:none!important;}
.bs-field{position:relative;}
.bs-manage-selected{position:absolute;z-index:30;left:0;top:calc(100% + 8px);width:min(440px, calc(100vw - 48px));display:none;background:#fff;border:1px solid #cbd5e1;border-radius:12px;box-shadow:0 18px 40px rgba(15,23,42,.18);padding:10px;box-sizing:border-box;}
.bs-manage-selected.active{display:block;}
.bs-manage-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-bottom:8px;border-bottom:1px solid #e5e7eb;}
.bs-manage-head strong{display:block;font-size:13px;color:#0f172a;line-height:1.2;}
.bs-manage-head span{display:block;margin-top:2px;font-size:12px;color:#64748b;font-weight:800;}
.bs-manage-actions{display:flex;gap:8px;margin:10px 0;}
.bs-mini-btn{border:1px solid #cbd5e1!important;background:#fff!important;color:#334155!important;border-radius:8px!important;padding:7px 10px!important;font-size:12px!important;font-weight:900!important;cursor:pointer!important;}
.bs-mini-btn.primary{background:#166534!important;border-color:#166534!important;color:#fff!important;}
.bs-mini-btn.danger{background:#fff1f2!important;border-color:#fecaca!important;color:#991b1b!important;}
.bs-mini-btn:hover,.bs-mini-btn:focus{filter:brightness(.97);outline:none!important;box-shadow:0 0 0 3px rgba(15,23,42,.08)!important;}
.bs-manage-list{max-height:220px;overflow:auto;display:flex;flex-direction:column;gap:6px;}
.bs-manage-row{display:flex;align-items:center;justify-content:space-between;gap:10px;border:1px solid #e5e7eb;background:#f8fafc;border-radius:8px;padding:8px 9px;}
.bs-manage-row-name{min-width:0;font-size:13px;font-weight:900;color:#0f172a;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.bs-manage-empty{padding:14px;text-align:center;color:#64748b;font-size:13px;font-weight:800;background:#f8fafc;border-radius:8px;}
.bs-ledger-modal,.bs-picker-modal{position:fixed;inset:0;z-index:99990;display:none;align-items:center;justify-content:center;padding:1rem;box-sizing:border-box;}
.bs-ledger-modal.active,.bs-picker-modal.active{display:flex;}
.bs-ledger-backdrop,.bs-picker-backdrop{position:absolute;inset:0;background:rgba(15,23,42,.55);}
.bs-ledger-dialog{position:relative;width:min(1040px, calc(100vw - 2rem));max-height:calc(100vh - 2rem);background:#fff;border-radius:14px;box-shadow:0 24px 60px rgba(15,23,42,.28);display:flex;flex-direction:column;overflow:hidden;}
.bs-ledger-head,.bs-picker-head{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:14px 16px;border-bottom:1px solid #e5e7eb;background:#f8fafc;}
.bs-ledger-head .bs-subtitle{margin:0;font-size:18px;line-height:1.2;}
.bs-ledger-close,.bs-picker-close{width:34px;heig+�IA;�9V��������+��T
??�]ht:34px;border:1px solid #dbe4ee!important;border-radius:8px!important;background:#fff!important;color:#334155!important;display:inline-flex!important;align-items:center!important;justify-content:center!important;padding:0!important;font-size:16px!important;font-weight:900!important;cursor:pointer;line-height:1!important;}
.bs-ledger-body{padding:10px;overflow:auto;}
.bs-ledger-table{min-width:760px!important;table-layout:fixed;}
.bs-ledger-table th,.bs-ledger-table td{padding:8px 10px!important;font-size:13px!important;line-height:1.25!important;vertical-align:middle!important;}
.bs-ledger-loading-cell{padding:36px 18px!important;text-align:center!important;background:#fff!important;}
.bs-ledger-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:#334155;font-weight:800;}
.bs-ledger-spinner{width:34px;height:34px;border:4px solid #dbe4ee;border-top-color:#166534;border-radius:50%;animation:bsSpin .8s linear infinite;}
@keyframes bsSpin{to{transform:rotate(360deg);}}
body.bs-ledger-open,body.bs-br-open{overflow:hidden;}
.bs-picker-sheet{position:relative;width:100%;max-width:36rem;background:#fff;border-radius:.75rem;box-shadow:0 1rem 2rem rgba(0,0,0,.18);overflow:hidden;}
.bs-picker-title{font-size:.95rem;font-weight:800;}
.bs-picker-body{padding:.7rem .8rem .8rem;display:flex;flex-direction:column;gap:.45rem;}
.bs-picker-results{max-height:16rem;overflow-y:auto;display:flex;flex-direction:column;gap:.35rem;}
.bs-picker-note{text-align:center;padding:.55rem;color:var(--bs-muted);font-size:.8rem;}
.bs-picker-item{display:block;width:100%;text-align:left;padding:.62rem .7rem;border:1px solid var(--bs-border);border-radius:.55rem;background:#fff;color:var(--bs-text);cursor:pointer;}
.bs-picker-item-main{display:block;font-weight:800;font-size:.88rem;color:var(--bs-text);}
.bs-picker-item-sub{display:block;font-size:.72rem;color:var(--bs-muted);margin-top:.08rem;}

/* Driver-style Basket Return Receipt */
.bs-br-overlay{position:fixed;inset:0;z-index:100001;background:rgba(15,23,42,.55);overflow:auto;padding:24px;display:flex;align-items:flex-start;justify-content:center;font-family:Arial,Helvetica,sans-serif;color:#111;}
.bs-br-modal{width:min(560px, calc(100vw - 40px));background:#f3f4f6;border-radius:16px;padding:14px;box-shadow:0 24px 70px rgba(0,0,0,.35);}
.bs-br-actions{position:sticky;top:0;z-index:5;display:grid;grid-template-columns:1fr 1fr 1fr;gap:9px;margin:0 0 12px;background:#f3f4f6;padding-bottom:8px;}
.bs-br-actions button{border:0!important;border-radius:15px!important;min-height:46px!important;padding:10px 14px!important;font-size:14px!important;font-weight:950!important;cursor:pointer!important;font-family:inherit!important;}
.bs-br-print,.bs-br-share{background:#e9f7ee!important;color:#0B4A2D!important;border:1px solid #cce8d6!important;}
.bs-br-close{background:#0B4A2D!important;color:#fff!important;}
.bs-br-card{border:1px solid #dbe4ee;border-radius:18px;padding:14px;background:#fff;box-shadow:0 8px 22px rgba(10,45,29,.045);}
.bs-br-paper{border:1px solid #d1d5db;background:#fff;padding:16px;color:#111;font-family:Arial,sans-serif;box-sizing:border-box;}
.bs-br-head{display:flex;justify-content:space-between;align-items:center;gap:12px;border-bottom:2px solid #111;padding-bottom:10px;margin-bottom:12px;}
.bs-br-logo{width:176px;height:64px;object-fit:contain;object-position:left center;display:block;}
.bs-br-title{text-align:right;font-size:12px;font-weight:900;letter-spacing:.08em;}
.bs-br-no{text-align:right;font-size:15px;font-weight:900;margin-top:4px;}
.bs-br-info{display:grid;grid-template-columns:1fr 1fr;gap:12px;border-bottom:1px solid #e5e7eb;padding:8px 0 10px;}
.bs-br-field{min-width:0;}
.bs-br-field span{display:block;font-size:12px;font-weight:800;color:#111;margin-bottom:4px;}
.bs-br-field strong{display:block;font-size:15px;font-weight:900;line-height:1.2;word-break:break-word;color:#111;}
.bs-br-qty{font-size:34px;font-weight:950;text-align:center;color:#111;padding:18px 0;}
.bs-br-proof{margin-top:6px;border:1px dashed #cbd5e1;padding:10px;text-align:center;font-size:12px;font-weight:800;color:#111;min-height:58px;}
.bs-br-proof img{display:block;width:100%;max-height:330px;object-fit:contain;margin-top:8px;}
.bs-br-loading{padding:24px;text-align:center;font-weight:900;color:#334155;}
#bsBrPrintArea{display:none!important;}

@media (max-width:1024px){
  .bs-container{max-width:none;margin:0;border-radius:0;padding:.6rem;}
  .bs-card{padding:.6rem;margin-bottom:.5rem;border-radius:.75rem;box-shadow:0 1px 3px rgba(15,23,42,.04);}
  .bs-head{display:flex;align-items:center;margin-bottom:12px;}
  .bs-head h1{font-size:1.35rem;}
  .bs-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:.45rem;}
  .bs-actions{grid-column:1 / -1;}
  .bs-btn{width:100%;min-height:2.35rem;border-radius:.55rem;}
  .bs-table{min-width:880px;}
  .bs-table thead th,.bs-table tbody td{font-size:.78rem;padding:.5rem .6rem;}
  .bs-totals{gap:.45rem;margin-bottom:.6rem;}
  .bs-total-value{font-size:1rem;}
}
@media (max-width:680px){
  .bs-container{padding:.45rem;}
  .bs-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:.4rem;}
  .bs-input{min-height:2.2rem;padding:.45rem .58rem;font-size:.9rem;border-radius:.5rem;}
  .bs-totals{gap:.3rem;margin-bottom:.45rem;}
  .bs-total-label{font-size:.56rem;}
  .bs-total-value{font-size:.84rem;}
  .bs-br-overlay{padding:12px;}
  .bs-br-modal{width:100%;border-radius:12px;padding:12px;}
  .bs-br-info,.bs-br-actions{grid-template-columns:1fr;}
}
@media print{
  html,body{background:#fff!important;width:148mm;min-height:0!important;height:auto!important;overflow:hidden!important;}
  body > *:not(#bsBrPrintArea){display:none!important;}
  body *{visibility:hidden!important;}
  #bsBrPrintArea,#bsBrPrintArea *{visibility:visible!important;}
  #bsBrPrintArea{display:block!important;position:absolute!important;left:0!important;top:0!important;width:100%!important;max-height:190mm!important;overflow:hidden!important;page-break-after:avoid!important;break-after:avoid!important;}
  #bsBrPrintArea .bs-br-card{border:0!important;box-shadow:none!important;margin:0!important;padding:0!important;page-break-inside:avoid!important;break-inside:avoid!important;}
  #bsBrPrintArea .bs-br-paper{height:188mm!important;overflow:hidden!important;page-break-inside:avoid!important;break-inside:avoid!important;}
  .bs-br-actions{display:none!important;}
  @page{size:A5 portrait;margin:6mm;}
}
</style>

<script>
(function(){
  const wrap = document.getElementById('ac-basket-summary-root');
  if (!wrap || wrap.dataset.init === '1') return;
  wrap.dataset.init = '1';

  const REST_NONCE       = wrap.dataset.restNonce || '';
  const REST_SUMMARY_URL = wrap.dataset.restSummaryUrl || '';
  const REST_LEDGER_URL  = wrap.dataset.restLedgerUrl || '';
  const AJAX_URL         = wrap.dataset.ajaxUrl || '';
  const DEBTOR_NONCE     = wrap.dataset.debtorNonce || '';
  const SHOW_DEBTOR_CODE = wrap.dataset.showDebtorCode === '1';
  const RECEIPT_LOGO_URL = wrap.dataset.receiptLogoUrl || '';

  const $ = id => wrap.querySelector('#' + id);
  const pickerState = { items: [], fetchFn: null, onPick: null };
  const selectedDebtors = [];
  const ledgerCache = {};
  let pickerTimer = null;
  let currentLedgerRows = [];
  let currentLedgerCustomer = { code: '', name: '' };
  let currentReceiptForShare = null;
  let brJsPdfPromise = null;
  let loadSummarySeq = 0;

  function fmtQty(n){
    const x = Number(n);
    return Number.isFinite(x) ? Math.round(x) : '0';
  }

  function esc(s){
    if (s === null || s === undefined) return '';
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
  }

  function showError(title, message){
    if (window.Swal && typeof Swal.fire === 'function') {
      Swal.fire({ icon: 'error', title: title || 'Error', text: message || 'Something went wrong' });
    } else {
      alert((title || 'Error') + '\n' + (message || 'Something went wrong'));
    }
  }

  function chipClass(outstanding){
    const v = Number(outstanding) || 0;
    if (v < 0) return 'neg';
    if (v > 0) return 'warn';
    return 'ok';
  }

  function outstandingMeaning(outstanding){
    const v = Number(outstanding) || 0;
    if (v < 0) return 'Over-return: returned ' + fmtQty(Math.abs(v)) + ' more than sent';
    if (v > 0) return 'Outstanding: customer still has ' + fmtQty(v) + ' basket';
    return 'Balanced: sent and returned baskets match';
  }

  function dateSortValue(value){
    if (!value) return 0;
    const parsed = Date.parse(String(value).replace(' ', 'T'));
    return Number.isNaN(parsed) ? 0 : parsed;
  }

  function compareText(a, b){
    return String(a || '').localeCompare(String(b || ''), undefined, { sensitivity:'base', numeric:true });
  }

  function compareSummaryByLastActivity(a, b){
    const dateDiff = dateSortValue(b.lastTxnDate || b.last_txn_date) - dateSortValue(a.lastTxnDate || a.last_txn_date);
    if (dateDiff !== 0) return dateDiff;

    const nameDiff = compareText(a.debtorName || a.debtor_name, b.debtorName || b.debtor_name);
    if (nameDiff !== 0) return nameDiff;

    return compareText(a.debtorCode || a.debtor_code, b.debtorCode || b.debtor_code);
  }

  function compareLedgerByLastActivity(a, b){
    const dateDiff = dateSortValue(b.txnDate || b.txn_date || b.date) - dateSortValue(a.txnDate || a.txn_date || a.date);
    if (dateDiff !== 0) return dateDiff;

    return compareText(b.id || b.ledgerId || b.ledger_id, a.id || a.ledgerId || a.ledger_id);
  }

  function pick(row, keys, fallback=''){
    if (!row) return fallback;
    for (const key of keys) {
      if (row[key] !== undefined && row[key] !== null && row[key] !== '') return row[key];
    }
    return fallback;
  }

  function normalizeSummaryRow(row){
    const debtorCode = String(pick(row, ['debtorCode', 'debtor_code', 'customerCode', 'customer_code'], '')).trim();
    const debtorName = String(pick(row, ['debtorName', 'debtor_name', 'customerName', 'customer_name'], '')).trim();
    return {
      ...row,
      debtorCode,
      debtorName,
      sendQty: Number(pick(row, ['sendQty', 'send_qty', 'basketSent', 'basket_sent'], 0)) || 0,
      returnQty: Number(pick(row, ['returnQty', 'return_qty', 'basketReturned', 'basket_returned'], 0)) || 0,
      outstandingQty: Number(pick(row, ['outstandingQty', 'outstanding_qty', 'outstandingBasket', 'outstanding_basket'], 0)) || 0,
      lastTxnDate: String(pick(row, ['lastTxnDate', 'last_txn_date', 'lastActivity', 'last_activity'], '')).trim()
    };
  }

  function normalizeLedgerRow(row){
    return {
      ...row,
      id: pick(row, ['id', 'ledgerId', 'ledger_id'], ''),
      txnDate: String(pick(row, ['txnDate', 'txn_date', 'date'], '')).trim(),
      txnType: String(pick(row, ['txnType', 'txn_type', 'type'], '')).trim(),
      qty: Number(pick(row, ['qty', 'quantity'], 0)) || 0,
      sourceType: String(pick(row, ['sourceType', 'source_type'], '')).trim(),
      sourceRef: String(pick(row, ['sourceRef', 'source_ref', 'refNo', 'ref_no', 'docNo', 'doc_no'], '')).trim(),
      remark: String(pick(row, ['remark', 'note'], '')).trim()
    };
  }

  function dedupeSummaryRowsByDebtor(rows){
    const byCode = new Map();
    rows.forEach(row => {
      const key = debtorKey(row.debtorCode || row.debtorName);
      if (!key) return;
      const existing = byCode.get(key);
      if (!existing || dateSortValue(row.lastTxnDate) > dateSortValue(existing.lastTxnDate)) {
        byCode.set(key, row);
      }
    });
    return Array.from(byCode.values());
  }

  function debtorKey(value){
    return String(value || '').trim().toUpperCase();
  }

  function selectedDebtorCodes(){
    return selectedDebtors.map(d => debtorKey(d.code)).filter(Boolean);
  }

  function syncCustomerFilterInputs(){
    const first = selectedDebtors[0] || { code: '', name: '' };
    $('ac_bs_debtor_code').value = first.code || '';
    $('ac_bs_debtor_name').value = first.name || '';

    const input = $('ac_bs_customer_input');
    if (!selectedDebtors.length) {
      input.value = '';
    } else if (selectedDebtors.length === 1) {
      input.value = selectedDebtors[0].name || selectedDebtors[0].code || '';
    } else {
      input.value = selectedDebtors.length + ' customers selected';
    }
  }

  function renderSelectedCustomers(){
    const mount = $('ac_bs_selected_customers');
    if (!mount) return;

    if (!selectedDebtors.length) {
      mount.innerHTML = '';
      renderManageSelectedCustomers();
      return;
    }

    const visibleDebtors = selectedDebtors.slice(0, 2);
    const hiddenCount = Math.max(0, selectedDebtors.length - visibleDebtors.length);
    const chips = visibleDebtors.map(d => {
      const label = d.name || d.code || 'Customer';
      const meta = SHOW_DEBTOR_CODE && d.code ? ' (' + d.code + ')' : '';
      return `<span class="bs-selected-chip" title="${esc(label + meta)}">
        <span>${esc(label + meta)}</span>
        <button type="button" class="bs-selected-remove" data-remove-selected-customer="${esc(d.code)}" aria-label="Remove ${esc(label)}"></button>
      </span>`;
    });

    if (hiddenCount > 0) {
      chips.push('<span class="bs-selected-more">+' + fmtQty(hiddenCount) + ' more</span>');
    }

    chips.push('<button type="button" class="bs-manage-toggle" data-toggle-selected-customers>Manage</button>');
    mount.innerHTML = chips.join('');
    renderManageSelectedCustomers();
  }

  function renderManageSelectedCustomers(){
    const list = $('ac_bs_manage_list');
    const count = $('ac_bs_manage_count');
    if (count) count.textContent = selectedDebtors.length + (selectedDebtors.length === 1 ? ' selected' : ' selected');
    if (!list) return;

    if (!selectedDebtors.length) {
      list.innerHTML = '<div class="bs-manage-empty">No customer selected.</div>';
      return;
    }

    list.innerHTML = selectedDebtors.map(d => {
      const label = d.name || d.code || 'Customer';
      const meta = SHOW_DEBTOR_CODE && d.code ? ' (' + d.code + ')' : '';
      return `<div class="bs-manage-row">
        <div class="bs-manage-row-name" title="${esc(label + meta)}">${esc(label + meta)}</div>
        <button type="button" class="bs-selected-remove" data-remove-selected-customer="${esc(d.code)}" aria-label="Remove ${esc(label)}"></button>
      </div>`;
    }).join('');
  }

  function openSelectedCustomerManager(){
    const panel = $('ac_bs_manage_selected');
    if (!panel) return;
    renderManageSelectedCustomers();
    panel.classList.add('active');
    panel.setAttribute('aria-hidden', 'false');
  }

  function closeSelectedCustomerManager(){
    const panel = $('ac_bs_manage_selected');
    if (!panel) return;
    panel.classList.remove('active');
    panel.setAttribute('aria-hidden', 'true');
  }

  function toggleSelectedCustomerManager(){
    const panel = $('ac_bs_manage_selected');
    if (!panel) return;
    if (panel.classList.contains('active')) {
      closeSelectedCustomerManager();
    } else {
      openSelectedCustomerManager();
    }
  }

  function updateCustomerSelectionUi(){
    syncCustomerFilterInputs();
    renderSelectedCustomers();
    updateCustomerClearButton();
  }

  function addSelectedCustomer(customer){
    if (!customer) return false;
    const code = String(customer.code || '').trim();
    const name = String(customer.name || '').trim();
    if (!code && !name) return false;

    const key = debtorKey(code || name);
    if (selectedDebtors.some(d => debtorKey(d.code || d.name) === key)) return false;

    selectedDebtors.push({ code, name });
    updateCustomerSelectionUi();
    return true;
  }

  function removeSelectedCustomer(code){
    const key = debtorKey(code);
    const idx = selectedDebtors.findIndex(d => debtorKey(d.code) === key);
    if (idx < 0) return;
    selectedDebtors.splice(idx, 1);
    updateCustomerSelectionUi();
    loadSummary();
  }

  function filterRowsB+��T`u�>W��������Sm�
??�Y<?php
if (!defined('ABSPATH')) exit;

/*
 * Template Name: View Delivery Order
 *
 * VegeBasketDO printable Delivery Order page with proof image.
 *
 * Staff list URL:
 * /view-delivery-order/?docNo=DO-000074&docKey=409
 *
 * Legacy fallback URL:
 * /view-delivery-order/?job_id=83
 */

global $wpdb;

if (!is_user_logged_in()) {
    wp_die('Please login to view this delivery order.');
}

if (!current_user_can('edit_posts') && !current_user_can('manage_options')) {
    wp_die('You do not have permission to view this delivery order.');
}

$jobs_table  = $wpdb->prefix . 'ac_jobs';
$proof_table = $wpdb->prefix . 'ac_do_proof_images';

if (!function_exists('ac_do_h')) {
    function ac_do_h($value) {
        return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
    }
}

if (!function_exists('ac_do_num')) {
    function ac_do_num($value) {
        if ($value === '' || $value === null) return '';

        $num = (float)$value;

        if (floor($num) == $num) {
            return (string)intval($num);
        }

        return rtrim(rtrim(number_format($num, 2, '.', ''), '0'), '.');
    }
}

if (!function_exists('ac_do_pick')) {
    function ac_do_pick($array, $keys, $default = '') {
        if (!is_array($array)) return $default;

        foreach ($keys as $key) {
            if (isset($array[$key]) && $array[$key] !== '' && $array[$key] !== null) {
                return $array[$key];
            }
        }

        return $default;
    }
}

if (!function_exists('ac_do_date')) {
    function ac_do_date($value) {
        if ($value instanceof DateTime) {
            return $value->format('Y-m-d');
        }

        $value = trim((string)$value);
        if ($value === '') return '';

        $timestamp = strtotime($value);
        return $timestamp ? date('Y-m-d', $timestamp) : $value;
    }
}

if (!function_exists('ac_do_wp_table_exists')) {
    function ac_do_wp_table_exists($table_name) {
        global $wpdb;

        if (!$wpdb) return false;

        return $wpdb->get_var(
            $wpdb->prepare('SHOW TABLES LIKE %s', $table_name)
        ) === $table_name;
    }
}

if (!function_exists('ac_do_wp_table_columns')) {
    function ac_do_wp_table_columns($table_name) {
        global $wpdb;

        static $cache = array();
        if (!$wpdb) return array();

        if (isset($cache[$table_name])) {
            return $cache[$table_name];
        }

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table_name);
        $cols = $wpdb->get_col("SHOW COLUMNS FROM `{$safe_table}`", 0);

        $cache[$table_name] = is_array($cols) ? array_flip($cols) : array();

        return $cache[$table_name];
    }
}

if (!function_exists('ac_do_doc_no_from_data')) {
    function ac_do_doc_no_from_data($data) {
        $doc_no = ac_do_pick($data, array('docNo', 'DocNo', 'docno', 'sourceDocNo', 'oldDocNo', 'originalDocNo'), '');
        return strtoupper(trim((string)$doc_no));
    }
}

if (!function_exists('ac_do_doc_key_from_data')) {
    function ac_do_doc_key_from_data($data) {
        $doc_key = ac_do_pick($data, array('docKey', 'DocKey', 'dockey'), 0);
        return is_numeric($doc_key) ? (int)$doc_key : 0;
    }
}

if (!function_exists('ac_do_load_job_by_ref')) {
    function ac_do_load_job_by_ref($job_id, $doc_no, $doc_key) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_jobs';
        if (!ac_do_wp_table_exists($table)) return null;

        if ($job_id > 0) {
            return $wpdb->get_row(
                $wpdb->prepare("
                    SELECT *
                    FROM {$table}
                    WHERE id = %d
                      AND job_type = 'DELIVERY_ORDER'
                    LIMIT 1
                ", $job_id),
                ARRAY_A
            );
        }

        $rows = $wpdb->get_results("
            SELECT *
            FROM {$table}
            WHERE job_type = 'DELIVERY_ORDER'
            ORDER BY id DESC
            LIMIT 800
        ", ARRAY_A);

        $doc_no = strtoupper(trim((string)$doc_no));
        $doc_key = (int)$doc_key;

        foreach ((array)$rows as $row) {
            $payload = json_decode((string)($row['payload'] ?? ''), true);
            $result = json_decode((string)($row['result'] ?? ''), true);
            $payload = is_array($payload) ? $payload : array();
            $result = is_array($result) ? $result : array();

            $row_doc_no = ac_do_doc_no_from_data($result);
            if ($row_doc_no === '') {
                $row_doc_no = ac_do_doc_no_from_data($payload);
            }

            $row_doc_key = ac_do_doc_key_from_data($result);
            if ($row_doc_key <= 0) {
                $row_doc_key = ac_do_doc_key_from_data($payload);
            }

            if ($doc_no !== '' && $row_doc_no === $doc_no) {
                return $row;
            }

            if ($doc_key > 0 && $row_doc_key === $doc_key) {
                return $row;
            }
        }

        return null;
    }
}

if (!function_exists('ac_do_load_autocount_order')) {
    function ac_do_load_autocount_order($doc_no, $doc_key) {
        if (!function_exists('get_mssql')) return null;

        $conn = get_mssql();
        if (!$conn) return null;

        $where = array();
        $params = array();

        $doc_no = trim((string)$doc_no);
        $doc_key = (int)$doc_key;

        if ($doc_key > 0) {
            $where[] = 'DOH.DocKey = ?';
            $params[] = $doc_key;
        }

        if ($doc_no !== '') {
            $where[] = 'DOH.DocNo = ?';
            $params[] = $doc_no;
        }

        if (empty($where)) return null;

        $header_sql = "
            SELECT TOP 1
                DOH.DocKey,
                DOH.DocNo,
                DOH.DocDate,
                DOH.DebtorCode,
                DOH.DebtorName,
                ISNULL(DOH.UDF_SUMBASKET, 0) AS TotalBasket,
                ISNULL(DOH.UDF_SUMCARTON, 0) AS TotalCarton
            FROM dbo.[DO] AS DOH
            WHERE " . implode(' OR ', $where) . "
            ORDER BY DOH.DocKey DESC
        ";

        $stmt = sqlsrv_query($conn, $header_sql, $params);
        if ($stmt === false) return null;

        $header = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
        sqlsrv_free_stmt($stmt);

        if (!$header) return null;

        $detail_sql = "
            SELECT
                ISNULL(DTL.ItemCode, '') AS ItemCode,
                ISNULL(DTL.Description, '') AS Description,
                ISNULL(DTL.Qty, 0) AS Qty,
                ISNULL(DTL.UDF_BASKET, 0) AS Basket,
                ISNULL(DTL.UDF_CARTON, 0) AS Carton,
                ISNULL(DTL.UDF_WEIGHTKG, 0) AS WeightKG
            FROM dbo.DODTL AS DTL
            WHERE DTL.DocKey = ?
              AND ISNULL(DTL.MainItem, 'T') = 'T'
            ORDER BY ISNULL(DTL.Seq, 0) ASC, ISNULL(DTL.ItemCode, '') ASC
        ";

        $detail_stmt = sqlsrv_query($conn, $detail_sql, array((int)$header['DocKey']));
        $lines = array();

        if ($detail_stmt !== false) {
            while ($line = sqlsrv_fetch_array($detail_stmt, SQLSRV_FETCH_ASSOC)) {
                $basket = (float)($line['Basket'] ?? 0);
                $carton = (float)($line['Carton'] ?? 0);
                $qty = (float)($line['Qty'] ?? 0);
                $pack_type = $carton > 0 ? 'CTN' : ($basket > 0 ? 'BSK' : '');
                $display_qty = $carton > 0 ? $carton : ($basket > 0 ? $basket : $qty);

                $lines[] = array(
                    'qty' => $display_qty,
                    'kg' => $line['WeightKG'] ?? 0,
                    'totalKg' => $line['WeightKG'] ?? 0,
                    'description' => $line['Description'] !== '' ? $line['Description'] : $line['ItemCode'],
                    'packType' => $pack_type,
                    'cartonQty' => $carton,
                    'basketQty' => $basket,
                );
            }

            sqlsrv_free_stmt($detail_stmt);
        }

        return array(
            'docNo' => (string)($header['DocNo'] ?? ''),
            'docKey' => (int)($header['DocKey'] ?? 0),
            'customerCode' => (string)($header['DebtorCode'] ?? ''),
            'customerName' => (string)($header['DebtorName'] ?? ''),
            'docDate' => ac_do_date($header['DocDate'] ?? ''),
            'remark' => '',
            'lines' => $lines,
        );
    }
}

if (!function_exists('ac_do_get_proof_url')) {
    function ac_do_get_proof_url($job_id, $doc_key, $doc_no) {
        global $wpdb;

        $table = $wpdb->prefix . 'ac_do_proof_images';
        if (!ac_do_wp_table_exists($table)) return '';

        $cols = ac_do_wp_table_columns($table);
        $where = array();
        $args = array();

        if ((int)$job_id > 0 && isset($cols['job_id'])) {
            $where[] = 'job_id = %d';
            $args[] = (int)$job_id;
        }

        if ((int)$doc_key > 0 && isset($cols['doc_key'])) {
            $where[] = 'doc_key = %d';
            $args[] = (int)$doc_key;
        }

        $doc_no = trim((string)$doc_no);
        if ($doc_no !== '' && isset($cols['doc_no'])) {
            $where[] = 'doc_no = %s';
            $args[] = $doc_no;
        }

        if (empty($where)) return '';

        $safe_table = preg_replace('/[^A-Za-z0-9_]/', '', $table);
        $select = array();

        foreach (array('attachment_id', 'image_url') as $col) {
            if (isset($cols[$col])) {
                $select[] = "`{$col}`";
            }
        }

        if (empty($select)) return '';

        $where_sql = '(' . implode(' OR ', $where) . ')';

        if (isset($cols['proof_type'])) {
            $where_sql .= ' AND proof_type = %s';
            $args[] = 'DELIVERY_PROOF';
        }

        if (isset($cols['deleted_at'])) {
            $where_sql .= ' AND deleted_at IS NULL';
        }

        $order_col = isset($cols['captured_at']) ? 'captured_at' : 'id';

        $sql = "
            SELECT " . implode(', ', $select) . "
            FROM `{$safe_table}`
            WHERE {$where_sql}
            ORDER BY `{$order_col}` ASC
            LIMIT 1
        ";

        $proof = $wpdb->get_row($wpdb->prepare($sql, $args), ARRAY_A);
        if (!$proof) return '';

        if (!empty($proof['attachment_id'])) {
            $attachment_url = wp_get_attachment_url((int)$proof['attachment_id']);
            if (!empty($attachment_url)) {
                return $attachment_url;
            }
        }

        return !empty($proof['image_url']) ? esc_url_raw((string)$proof['image_url']) : '';
    }
}

$job_id = isset($_GET['job_id']) ? absint($_GET['job_id']) : 0;
$doc_no_param = isset($_GET['docNo']) ? sanitize_text_field(wp_unslash($_GET['docNo'])) : '';
$doc_key_param = isset($_GET['docKey']) ? absint($_GET['docKey']) : 0;

if ($doc_no_param === '' && isset($_GET['docno'])) {
    $doc_no_param = sanitize_text_field(wp_unslash($_GET['docno']));
}

if ($doc_key_param <= 0 && isset($_GET['dockey'])) {
    $doc_key_param = absint($_GET['dockey']);
}

$job = ac_do_load_job_by_ref($job_id, $doc_no_param, $doc_key_param);
$payload = array();
$result = array();

if ($job) {
    $payload = json_decode((string)($job['payload'] ?? ''), true);
    $result = json_decode((string)($job['result'] ?? ''), true);
    $payload = is_array($payload) ? $payload : array();
    $result = is_array($result) ? $result : array();
}

$doc_no = $doc_no_param !== '' ? strtoupper(trim($doc_no_param)) : ac_do_doc_no_from_data($result);
if ($doc_no === '') {
    $doc_no = ac_do_doc_no_from_data($payload);
}

$doc_key = $doc_key_param > 0 ? $doc_key_param : ac_do_doc_key_from_data($result);
if ($doc_key <= 0) {
    $doc_key = ac_do_doc_key_from_data($payload);
}

$autocount_order = ac_do_load_autocount_order($doc_no, $doc_key);

if ($autocount_order) {
    $doc_no = $autocount_order['docNo'];
    $doc_key = $autocount_order['docKey'];
    $customer_code = $autocount_order['customerCode'];
    $customer_name = $autocount_order['customerName'];
    $doc_date = $autocount_order['docDate'];
    $remark = $autocount_order['remark'];
    $lines = $autocount_order['lines'];
} elseif (!empty($payload)) {
    $customer_code = ac_do_pick($payload, array('customerCode', 'debtorCode', 'DebtorCode'), '');
    $customer_name = ac_do_pick($payload, array('customerName', 'debtorName', 'DebtorName'), '');
    $doc_date = ac_do_pick($payload, array('docDate', 'DocDate'), date('Y-m-d'));
    $remark = ac_do_pick($payload, array('remark', 'Remark'), '');
    $lines = isset($payload['lines']) && is_array($payload['lines']) ? $payload['lines'] : array();
} else {
    wp_die('Delivery order not found.');
}

if ($doc_no === '' && $job) {
    $doc_no = 'DO-' . str_pad((string)$job['id'], 6, '0', STR_PAD_LEFT);
}

$company_name = 'EXCELLENT VEGE SDN. BHD.';
$company_addr = 'No. 45, 47, Complex Pasar Borong, 3rd Miles, Jalan Ipoh, 51200 Kuala Lumpur';
$company_tel  = '017-4373 752 / 016-963 752 / 012-3013 752';
$company_logo_url = 'https://website.ipohserver.com/VegeBasketDO/wp-content/uploads/2026/05/Untitled-design-15.png';

$total_ctn = 0;
$total_bsk = 0;
$total_kg = 0;

foreach ($lines as $line) {
    $pack_type = strtoupper((string)ac_do_pick($line, array('packType', 'PackType'), ''));

    if ($pack_type === 'CARTON' || $pack_type === 'CTN') {
        $total_ctn += (float)ac_do_pick($line, array('cartonQty', 'ctnQty', 'qty', 'Qty'), 0);
    } elseif ($pack_type === 'BASKET' || $pack_type === 'BSK') {
        $total_bsk += (float)ac_do_pick($line, array('basketQty', 'bskQty', 'qty', 'Qty'), 0);
    }

    $total_kg += (float)ac_do_pick($line, array('totalKg', 'TotalKg', 'UDF_WEIGHTKG', 'kg', 'Kg'), 0);
}

$display_date = $doc_date;
$timestamp = strtotime((string)$doc_date);
if ($timestamp) {
    $display_date = date('d/m/Y', $timestamp);
}

$proof_url = ac_do_get_proof_url((int)($job['id'] ?? 0), $doc_key, $doc_no);
$pdf_file_name = 'Delivery-Order-' . preg_replace('/[^A-Za-z0-9_-]/', '-', $doc_no !== '' ? $doc_no : 'DO') . '.pdf';
$pdf_lines = array();

foreach ($lines as $line) {
    $pack_type = strtoupper((string)ac_do_pick($line, array('packType', 'PackType'), ''));
    $is_ctn = ($pack_type === 'CARTON' || $pack_type === 'CTN');
    $is_bsk = ($pack_type === 'BASKET' || $pack_type === 'BSK');

    if ($is_ctn) {
        $qty = ac_do_pick($line, array('cartonQty', 'ctnQty', 'qty', 'Qty'), '');
    } elseif ($is_bsk) {
        $qty = ac_do_pick($line, array('basketQty', 'bskQty', 'qty', 'Qty'), '');
    } else {
        $qty = ac_do_pick($line, array('cartonQty', 'basketQty', 'qty', 'Qty'), '');
    }

    $pdf_lines[] = array(
        'qty' => ac_do_num($qty),
        'kg' => ac_do_num(ac_do_pick($line, array('kg', 'Kg', 'UDF_WEIGHTKG'), '')),
        'description' => (string)ac_do_pick($line, array('description', 'Description', 'itemName', 'ItemName', 'itemDescription', 'ItemDescription', 'itemCode', 'ItemCode'), ''),
        'isCtn' => $is_ctn,
        'isBsk' => $is_bsk,
        'totalKg' => ac_do_num(ac_do_pick($line, array('totalKg', 'TotalKg', 'UDF_WEIGHTKG', 'kg', 'Kg'), '')),
    );
}

$pdf_payload = array(
    'companyName' => $company_name,
    'companyAddr' => $company_addr,
    'companyTel' => $company_tel,
    'companyLogoUrl' => $company_logo_url,
    'docNo' => $doc_no,
    'customerCode' => $customer_code,
    'customerName' => $customer_name,
    'displayDate' => $display_date,
    'remark' => $remark,
    'lines' => $pdf_lines,
    'totalCtn' => ac_do_num($total_ctn),
    'totalBsk' => ac_do_num($total_bsk),
    'totalKg' => ac_do_num($total_kg),
    'proofUrl' => $proof_url,
);
?>

<style>
    * { box-sizing: border-box; }

    .ac-do-page-wrap {
        --ac-do-preview-scale: 1;
        --ac-do-preview-width: 794px;
        --ac-do-preview-height: 1123px;
        --ac-do-preview-gap: 24px;
        width: 100%;Sm楯��X���������XO�
??�Z<?php
global $wpdb;

if (!is_user_logged_in()) {
    echo '<div class="driver-do-wrap">
        <div class="driver-do-empty">
            <div class="driver-do-empty-title">Login Required</div>
            <div class="driver-do-empty-text">Please login to view your delivery orders.</div>
        </div>
    </div>';
    return;
}

$current_user_id  = get_current_user_id();
$jobs_table       = $wpdb->prefix . 'ac_jobs';
$proof_table      = $wpdb->prefix . 'ac_do_proof_images';
$create_order_url = 'https://website.ipohserver.com/VegeBasketDO/create-delivery-order/';

$today_start    = current_time('Y-m-d 00:00:00');
$tomorrow_start = date('Y-m-d 00:00:00', strtotime(current_time('Y-m-d') . ' +1 day'));

$has_delivery_status = (int) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT COUNT(*) 
         FROM INFORMATION_SCHEMA.COLUMNS 
         WHERE TABLE_SCHEMA = %s
         AND TABLE_NAME = %s
         AND COLUMN_NAME = 'delivery_status'",
        DB_NAME,
        $jobs_table
    )
);

$proof_join = "
    LEFT JOIN (
        SELECT p1.*
        FROM {$proof_table} p1
        INNER JOIN (
            SELECT job_id, MAX(id) AS latest_id
            FROM {$proof_table}
            WHERE deleted_at IS NULL
            GROUP BY job_id
        ) latest_proof
            ON latest_proof.latest_id = p1.id
    ) p
        ON p.job_id = j.id
";

if ($has_delivery_status) {
    $orders = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT 
                j.*,
                p.id AS proof_id,
                p.image_url AS proof_image_url,
                p.captured_at AS proof_captured_at
             FROM {$jobs_table} j
             {$proof_join}
             WHERE j.job_type = 'DELIVERY_ORDER'
             AND j.created_by = %d
             AND j.created_at >= %s
             AND j.created_at < %s
             AND (
                j.delivery_status IS NULL
                OR j.delivery_status <> 'CANCELLED'
             )
             ORDER BY 
                CASE 
                    WHEN j.delivery_status = 'COMPLETED' OR p.id IS NOT NULL THEN 1
                    ELSE 0
                END ASC,
                j.created_at DESC
             LIMIT 50",
            $current_user_id,
            $today_start,
            $tomorrow_start
        )
    );
} else {
    $orders = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT 
                j.*,
                p.id AS proof_id,
                p.image_url AS proof_image_url,
                p.captured_at AS proof_captured_at
             FROM {$jobs_table} j
             {$proof_join}
             WHERE j.job_type = 'DELIVERY_ORDER'
             AND j.created_by = %d
             AND j.created_at >= %s
             AND j.created_at < %s
             ORDER BY 
                CASE 
                    WHEN p.id IS NOT NULL THEN 1
                    ELSE 0
                END ASC,
                j.created_at DESC
             LIMIT 50",
            $current_user_id,
            $today_start,
            $tomorrow_start
        )
    );
}

if (!function_exists('driver_do_safe_json')) {
    function driver_do_safe_json($value) {
        if (empty($value)) return array();
        $decoded = json_decode($value, true);
        return is_array($decoded) ? $decoded : array();
    }
}

if (!function_exists('driver_do_pick')) {
    function driver_do_pick($array, $keys, $default = '-') {
        if (!is_array($array)) return $default;

        foreach ($keys as $key) {
            if (isset($array[$key]) && $array[$key] !== '' && $array[$key] !== null) {
                return $array[$key];
            }
        }

        return $default;
    }
}

if (!function_exists('driver_do_pick_item_name')) {
    function driver_do_pick_item_name($line) {
        $name = driver_do_pick($line, array(
            'itemName',
            'ItemName',
            'item_name',
            'name',
            'Name',
            'productName',
            'ProductName',
            'description',
            'Description',
            'desc',
            'Desc',
            'itemDesc',
            'ItemDesc',
            'itemDescription',
            'ItemDescription'
        ), '');

        if ($name !== '') {
            return $name;
        }

        if (isset($line['item']) && is_array($line['item'])) {
            $nested_name = driver_do_pick($line['item'], array(
                'itemName',
                'ItemName',
                'name',
                'Name',
                'description',
                'Description',
                'itemDesc',
                'ItemDesc'
            ), '');

            if ($nested_name !== '') {
                return $nested_name;
            }
        }

        return '-';
    }
}

if (!function_exists('driver_do_num')) {
    function driver_do_num($number) {
        return rtrim(rtrim(number_format((float) $number, 2), '0'), '.');
    }
}
?>

<style>
.driver-do-wrap {
    width: 100%;
    max-width: 1280px;
    margin: 0 auto;
    font-family: Roboto, Arial, sans-serif;
    color: #17231c;
}

.driver-do-head {
    margin-bottom: 12px;
}

.driver-do-title {
    font-size: 1.18rem;
    font-weight: 800;
    color: #0B4A2D;
    line-height: 1.2;
}

.driver-do-subtitle {
    margin-top: 3px;
    font-size: 0.84rem;
    color: #647067;
    line-height: 1.35;
}

.driver-do-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.driver-do-box {
    background: #ffffff;
    border: 1px solid #dfe7e2;
    border-radius: 10px;
    overflow: hidden;
}

.driver-do-box.delivered {
    opacity: 0.78;
    background: #fafafa;
}

.driver-do-top {
    padding: 10px 14px;
    background: #f5faf7;
    border-bottom: 1px solid #dfe7e2;
}

.driver-do-box.delivered .driver-do-top {
    background: #eef7f1;
}

.driver-do-main-row {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    align-items: center;
}

.driver-do-customer {
    min-width: 0;
    flex: 1 1 auto;
    font-size: 0.98rem;
    font-weight: 800;
    color: #0B4A2D;
    line-height: 1.25;
    word-break: break-word;
}

.driver-do-doc {
    flex: 0 0 auto;
    font-size: 0.82rem;
    font-weight: 800;
    color: #0B4A2D;
    text-align: right;
    white-space: nowrap;
    background: #ffffff;
    border: 1px solid #dce9e2;
    border-radius: 999px;
    padding: 5px 9px;
}

.driver-do-section {
    padding: 8px 14px 4px;
}

.driver-do-items {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.88rem;
}

.driver-do-items th {
    text-align: left;
    background: #f4f6f5;
    color: #4f5b53;
    font-weight: 800;
    padding: 8px 7px;
    border-bottom: 1px solid #dfe7e2;
}

.driver-do-items th:not(:first-child),
.driver-do-items td:not(:first-child) {
    text-align: center;
}

.driver-do-items td {
    padding: 9px 7px;
    border-bottom: 1px solid #edf1ee;
    vertical-align: middle;
}

.driver-do-items tr:last-child td {
    border-bottom: none;
}

.driver-do-item-name {
    font-weight: 800;
    color: #1c271f;
}

.driver-do-pack-label {
    font-size: 0.76rem;
    font-weight: 800;
    color: #647067;
    margin-right: 4px;
}

.driver-do-pack-qty {
    font-weight: 900;
    color: #17231c;
}

.driver-do-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 8px 14px 12px;
    background: #ffffff;
}

.driver-do-btn {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    min-height: 38px;
    padding: 9px 14px;
    border-radius: 9px;
    text-decoration: none;
    font-size: 0.86rem;
    font-weight: 800;
    border: 1px solid transparent;
    cursor: pointer;
    line-height: 1.15;
}

.driver-do-btn.primary {
    background: #0B4A2D;
    color: #ffffff;
}

.driver-do-btn.secondary {
    background: #ffffff;
    color: #0B4A2D;
    border-color: #0B4A2D;
}

.driver-do-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.driver-do-delivered-label,
.driver-do-waiting-label {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 36px;
    padding: 8px 12px;
    border-radius: 999px;
    font-size: 0.84rem;
    font-weight: 800;
}

.driver-do-delivered-label {
    background: #e8f7ee;
    color: #0B6B3A;
}

.driver-do-waiting-label {
    background: #fff4d8;
    color: #7a5200;
}

.driver-do-empty {
    background: #ffffff;
    border: 1px solid #dfe7e2;
    border-radius: 14px;
    padding: 22px 16px;
    text-align: center;
}

.driver-do-empty-title {
    font-size: 1.1rem;
    font-weight: 800;
    color: #0B4A2D;
    margin-bottom: 6px;
}

.driver-do-empty-text {
    font-size: 0.9rem;
    color: #66736b;
    line-height: 1.45;
    margin-bottom: 16px;
}

.driver-do-empty .driver-do-btn {
    width: 100%;
}

.driver-do-proof-modal {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    z-index: 99999;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 16px;
}

.driver-do-proof-modal.active {
    display: flex;
}

.driver-do-proof-panel {
    width: 100%;
    max-width: 420px;
    background: #ffffff;
    border-radius: 14px;
    padding: 16px;
    box-shadow: 0 18px 45px rgba(0, 0, 0, 0.22);
}

.driver-do-proof-title {
    font-size: 1.05rem;
    font-weight: 800;
    color: #0B4A2D;
    margin-bottom: 5px;
}

.driver-do-proof-text {
    font-size: 0.86rem;
    color: #5f6b63;
    margin-bottom: 12px;
}

.driver-do-proof-preview {
    width: 100%;
    max-height: 280px;
    object-fit: contain;
    border: 1px solid #dfe7e2;
    border-radius: 12px;
    background: #f7faf8;
    display: none;
    margin-bottom: 12px;
}

.driver-do-proof-input {
    display: none;
}

.driver-do-proof-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    flex-wrap: wrap;
}

.driver-do-toast {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%);
    background: #0B4A2D;
    color: #ffffff;
    font-size: 0.88rem;
    font-weight: 800;
    padding: 11px 15px;
    border-radius: 999px;
    z-index: 100000;
    display: none;
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.22);
    max-width: calc(100vw - 24px);
    text-align: center;
}

.driver-do-toast.active {
    display: block;
}

@media (min-width: 681px) {
    .driver-do-empty {
        padding: 30px;
    }

    .driver-do-empty .driver-do-btn {
        width: auto;
        min-width: 220px;
    }
}

@media (min-width: 1025px) {
    .driver-do-title {
        font-size: 1.3rem;
    }

    .driver-do-items {
        font-size: 0.92rem;
    }

    .driver-do-items th,
    .driver-do-items td {
        padding: 9px 8px;
    }
}

@media (max-width: 680px) {
    .driver-do-wrap {
        width: 100%;
    }

    .driver-do-head {
        margin-bottom: 10px;
    }

    .driver-do-title {
        font-size: 1.05rem;
    }

    .driver-do-subtitle {
        font-size: 0.78rem;
    }

    .driver-do-list {
        gap: 9px;
    }

    .driver-do-top {
        padding: 9px 10px;
    }

    .driver-do-main-row {
        gap: 8px;
        align-items: flex-start;
    }

    .driver-do-customer {
        font-size: 0.88rem;
        line-height: 1.2;
    }

    .driver-do-doc {
        font-size: 0.7rem;
        padding: 4px 7px;
    }

    .driver-do-section {
        padding: 6px 10px 2px;
    }

    .driver-do-items thead {
        display: none;
    }

    .driver-do-items tbody,
    .driver-do-items tr,
    .driver-do-items td {
        display: block;
    }

    .driver-do-items tr {
        display: grid;
        grid-template-columns: 1fr auto;
        gap: 8px;
        align-items: center;
        padding: 8px 0;
        border-bottom: 1px solid #edf1ee;
    }

    .driver-do-items tr:last-child {
        border-bottom: none;
    }

    .driver-do-items td {
        border-bottom: none;
        padding: 0;
        background: transparent;
        min-width: 0;
    }

    .driver-do-items td:first-child {
        text-align: left;
    }

    .driver-do-items td:not(:first-child) {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 62px;
        padding: 5px 7px;
        border-radius: 999px;
        background: #f4f8f6;
        border: 1px solid #e1ebe5;
        font-weight: 800;
        font-size: 0.8rem;
    }

    .driver-do-items td:not(:first-child)::before {
        content: none;
    }

    .driver-do-actions {
        padding: 6px 10px 10px;
    }

    .driver-do-actions .driver-do-btn {
        width: 100%;
        min-height: 40px;
    }

    .driver-do-delivered-label,
    .driver-do-waiting-label {
        width: 100%;
    }

    .driver-do-proof-actions {
        flex-direction: column;
    }

    .driver-do-proof-actions .driver-do-btn {
        width: 100%;
    }
}
</style>

<div class="driver-do-wrap">
    <div class="driver-do-head">
        <div>
            <div class="driver-do-title">Today Delivery Orders</div>
            <div class="driver-do-subtitle">Undelivered orders appear first. Delivered orders stay below as today&apos;s record.</div>
        </div>
    </div>

    <?php if (empty($orders)) : ?>
        <div class="driver-do-empty">
            <div class="driver-do-empty-title">No Active Order Today</div>
            <div class="driver-do-empty-text">
                You currently have no Delivery Order for today.
            </div>
            <a class="driver-do-btn primary" href="<?php echo esc_url($create_order_url); ?>">
                Create Delivery Order
            </a>
        </div>
    <?php else : ?>
        <div class="driver-do-list">
            <?php foreach ($orders as $order) : ?>
                <?php
                $payload = driver_do_safe_json($order->payload);
                $result  = driver_do_safe_json($order->result);

                $customer_name = driver_do_pick($payload, array(
                    'customerName',
                    'debtorName',
                    'DebtorName',
                    'customer_name',
                    'CustomerName'
                ), '-');

                $doc_no = driver_do_pick($result, array(
                    'docNo',
                    'DocNo',
                    'doc_no'
                ), '-');

                $doc_key = driver_do_pick($result, array(
                    'docKey',
                    'DocKey',
                    'doc_key'
                ), 0);

                $lines = array();
                if (isset($payload['lines']) && is_array($payload['lines'])) {
                    $lines = $payload['lines'];
                }

                $delivery_status = isset($order->delivery_status) ? strtoupper((string) $order->delivery_status) : '';
                $job_status      = strtoupper((string) $order->status);
                $is_delivered    = !empty($order->proof_id) || $delivery_status === 'COMPLETED';
                $can_complete    = !$is_delivered && $job_status === 'SUCCESS' && $doc_no !== '-';
                $box_class       = $is_delivered ? ' delivered' : '';
                ?>

                <div class="driver-do-box<?php echo esc_attr($box_class); ?>">
                    <div class="driver-do-top">
                        <div class="driver-do-main-row">
                            <div class="driver-do-customer">
                                <?php echo esc_html($customer_name); ?>
                            </div>

                            <div class="driver-do-doc">
                                DO: <?php echo esc_html($doc_no); ?>
                            </div>
 �XO�T	Y��������S��
??�j
        background: #e5e5e5;
        padding: 20px 0 40px;
        font-family: Arial, Helvetica, sans-serif;
        color: #222;
        overflow-x: hidden;
    }

    .ac-do-actions {
        width: min(794px, calc(100vw - 32px));
        margin: 0 auto 14px;
        display: flex;
        justify-content: flex-end;
        gap: 8px;
        text-align: right;
    }

    .ac-do-actions button {
        border: 0;
        background: #0B4A2D;
        color: #fff;
        padding: 10px 18px;
        border-radius: 6px;
        font-size: 14px;
        cursor: pointer;
        font-weight: 700;
    }

    .ac-do-actions button.ac-do-share-btn {
        background: #128C7E;
    }

    .ac-do-actions button:disabled {
        cursor: not-allowed;
        opacity: 0.65;
    }

    .ac-do-share-source {
        width: 794px;
        margin: 0 auto;
        background: #fff;
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        z-index: 2147483647;
        pointer-events: none;
    }

    .ac-do-share-source .ac-do-paper,
    .ac-do-share-source .ac-do-proof-paper {
        margin: 0 auto;
        box-shadow: none;
    }

    .ac-do-share-source .ac-do-proof-paper {
        margin-top: 0;
        page-break-before: always;
        break-before: page;
    }

    .ac-do-preview-page {
        width: var(--ac-do-preview-width);
        height: var(--ac-do-preview-height);
        margin: 0 auto;
        position: relative;
    }

    .ac-do-preview-page + .ac-do-preview-page {
        margin-top: var(--ac-do-preview-gap);
    }

    .ac-do-preview-page > .ac-do-paper,
    .ac-do-preview-page > .ac-do-proof-paper {
        position: absolute;
        top: 0;
        left: 0;
        transform: scale(var(--ac-do-preview-scale));
        transform-origin: top left;
    }

    .ac-do-preview-page > .ac-do-proof-paper {
        margin-top: 0;
    }

    .ac-do-paper,
    .ac-do-proof-paper {
        width: 794px;
        min-height: 1123px;
        margin: 0 auto;
        background: #fff;
        padding: 34px 42px;
        border: 1px solid #d1d5db;
        position: relative;
    }

    .ac-do-proof-paper { margin-top: 24px; }
    .ac-do-top-label {
        position: absolute;
        top: 36px;
        right: 42px;
        text-align: right;
        margin-bottom: 0;
    }

    .ac-do-top-label span {
        background: #444;
        color: #fff;
        font-weight: 700;
        font-size: 13px;
        padding: 4px 12px;
        border-radius: 10px;
        letter-spacing: 0.5px;
    }

    .ac-do-header {
        display: grid;
        grid-template-columns: 128px 1fr 145px;
        gap: 14px;
        align-items: start;
        margin: 24px 0 12px;
    }

    .ac-do-logo {
        height: 94px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .ac-do-logo img {
        display: block;
        max-width: 124px;
        max-height: 86px;
        width: auto;
        height: auto;
        object-fit: contain;
    }

    .ac-do-company h1 {
        margin: 0;
        font-size: 28px;
        letter-spacing: 0.2px;
        line-height: 1.02;
        font-weight: 900;
    }

    .ac-do-company p {
        margin: 3px 0;
        font-size: 10.5px;
        line-height: 1.25;
    }

    .ac-do-doc-no {
        text-align: right;
        padding-top: 74px;
        font-size: 14px;
        white-space: nowrap;
    }

    .ac-do-doc-no strong {
        color: #d54b4b;
        font-size: 19px;
        letter-spacing: 1px;
    }

    .ac-do-info-row {
        display: grid;
        grid-template-columns: 1fr 190px;
        gap: 20px;
        margin: 8px 0 12px;
        font-size: 13px;
    }

    .ac-do-line-field {
        display: grid;
        grid-template-columns: 70px 1fr;
        align-items: end;
    }

    .ac-do-line-field.ac-do-date { grid-template-columns: 45px 1fr; }
    .ac-do-line-field span { font-weight: 700; }

    .ac-do-line-field div {
        border-bottom: 1px dotted #666;
        min-height: 20px;
        padding-left: 6px;
    }

    .ac-do-table {
        width: 100%;
        border-collapse: collapse;
        background: rgba(255, 255, 255, 0.15);
        font-size: 13px;
    }

    .ac-do-table th,
    .ac-do-table td {
        border: 1px solid #444;
        padding: 5px 6px;
        vertical-align: middle;
    }

    .ac-do-table th {
        text-align: center;
        font-size: 11px;
        line-height: 1.1;
        font-weight: 700;
    }

    .ac-do-qty,
    .ac-do-kg,
    .ac-do-total {
        text-align: center;
        width: 70px;
    }

    .ac-do-desc { width: 360px; }

    .ac-do-pack {
        width: 100px;
        text-align: center;
        font-size: 11px;
        white-space: nowrap;
    }

    .ac-do-checkbox {
        display: inline-block;
        width: 11px;
        height: 11px;
        border: 1px solid #222;
        margin-right: 2px;
        vertical-align: -1px;
        position: relative;
    }

    .ac-do-checkbox.checked::after {
        content: "✓";
        position: absolute;
        left: 1px;
        top: -6px;
        font-size: 16px;
        font-weight: 700;
    }

    .ac-do-bottom-area {
        display: grid;
        grid-template-columns: 1fr 270px;
        gap: 20px;
        margin-top: 12px;
        align-items: start;
    }

    .ac-do-slogan {
        font-weight: 700;
        font-style: italic;
        font-size: 15px;
        margin-top: 8px;
    }

    .ac-do-totals {
        display: grid;
        grid-template-columns: 1fr 80px;
        gap: 8px 10px;
        align-items: center;
        font-size: 14px;
    }

    .ac-do-total-label {
        text-align: right;
        line-height: 1.1;
    }

    .ac-do-total-box {
        border: 1px solid #444;
        height: 28px;
        background: rgba(255, 255, 255, 0.25);
        display: flex;
        align-items: center;
        justify-content: center;
        font-weight: 700;
    }

    .ac-do-remark {
        margin-top: 12px;
        font-size: 12px;
    }

    .ac-do-signature-row {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 120px;
        margin-top: 90px;
        font-size: 13px;
    }

    .ac-do-signature {
        border-top: 1px dotted #555;
        padding-top: 6px;
    }

    .ac-proof-title-row {
        display: grid;
        grid-template-columns: 1fr auto;
        gap: 20px;
        align-items: start;
        margin-bottom: 20px;
        border-bottom: 2px solid #333;
        padding-bottom: 12px;
    }

    .ac-proof-title h2 {
        margin: 0;
        font-size: 24px;
        line-height: 1.2;
    }

    .ac-proof-title p {
        margin: 4px 0 0;
        font-size: 13px;
    }

    .ac-proof-meta {
        text-align: right;
        font-size: 13px;
        line-height: 1.5;
    }

    .ac-proof-image-box {
        width: 100%;
        height: 820px;
        border: 1px solid #444;
        background: rgba(255,255,255,0.28);
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 14px;
        overflow: hidden;
    }

    .ac-proof-image-box img {
        max-width: 100%;
        max-height: 790px;
        width: auto;
        height: auto;
        object-fit: contain;
        display: block;
    }

    .ac-proof-empty {
        font-size: 16px;
        font-weight: 700;
        color: #555;
        text-align: center;
    }

    .ac-proof-footer {
        margin-top: 18px;
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 70px;
        font-size: 13px;
    }

    .ac-proof-footer-line {
        border-top: 1px dotted #555;
        padding-top: 6px;
    }

    @media screen and (max-width: 860px) {
        .ac-do-page-wrap {
            --ac-do-preview-scale: 0.9;
            --ac-do-preview-width: 715px;
            --ac-do-preview-height: 1011px;
            --ac-do-preview-gap: 22px;
        }
    }

    @media screen and (max-width: 760px) {
        .ac-do-page-wrap {
            --ac-do-preview-scale: 0.82;
            --ac-do-preview-width: 651px;
            --ac-do-preview-height: 921px;
            --ac-do-preview-gap: 20px;
            padding: 14px 0 28px;
        }

        .ac-do-actions {
            justify-content: center;
            flex-wrap: wrap;
            gap: 8px;
        }
    }

    @media screen and (max-width: 680px) {
        .ac-do-page-wrap {
            --ac-do-preview-scale: 0.72;
            --ac-do-preview-width: 572px;
            --ac-do-preview-height: 809px;
            --ac-do-preview-gap: 17px;
        }
    }

    @media screen and (max-width: 600px) {
        .ac-do-page-wrap {
            --ac-do-preview-scale: 0.62;
            --ac-do-preview-width: 492px;
            --ac-do-preview-height: 696px;
            --ac-do-preview-gap: 15px;
        }
    }

    @media screen and (max-width: 520px) {
        .ac-do-page-wrap {
            --ac-do-preview-scale: 0.52;
            --ac-do-preview-width: 413px;
            --ac-do-preview-height: 584px;
            --ac-do-preview-gap: 12px;
        }

        .ac-do-actions button {
            padding: 9px 12px;
            font-size: 13px;
        }
    }

    @media screen and (max-width: 430px) {
        .ac-do-page-wrap {
            --ac-do-preview-scale: 0.45;
            --ac-do-preview-width: 357px;
            --ac-do-preview-height: 505px;
            --ac-do-preview-gap: 11px;
        }
    }

    @media print {
        html,
        body {
            background: #fff !important;
            margin: 0 !important;
            padding: 0 !important;
            -webkit-print-color-adjust: exact !important;
            print-color-adjust: exact !important;
        }

        #wpadminbar,
        header,
        footer,
        .site-header,
        .site-footer,
        .elementor-location-header,
        .elementor-location-footer,
        .ac-do-actions {
            display: none !important;
        }

        .ac-do-page-wrap {
            width: 100% !important;
            margin: 0 !important;
            padding: 0 !important;
            background: #fff !important;
            overflow: visible !important;
        }

        .ac-do-preview-page {
            width: auto !important;
            height: auto !important;
            margin: 0 auto !important;
            position: static !important;
        }

        .ac-do-paper,
        .ac-do-proof-paper {
            width: 132mm !important;
            min-height: 194mm !important;
            margin: 0 auto !important;
            padding: 6mm !important;
            border: 0 !important;
            box-shadow: none !important;
            overflow: visible !important;
            background: #fff !important;
            position: relative !important;
            left: auto !important;
            top: auto !important;
            transform: none !important;
        }

        .ac-do-paper {
            page-break-after: always !important;
            break-after: page !important;
        }

        .ac-do-proof-paper {
            margin-top: 0 !important;
            page-break-after: auto !important;
            break-after: auto !important;
        }

        .ac-proof-image-box {
            height: 130mm !important;
            min-height: 130mm !important;
            max-height: 130mm !important;
        }

        .ac-proof-image-box img {
            max-width: 100% !important;
            max-height: 126mm !important;
            object-fit: contain !important;
        }

        @page {
            size: A5 portrait;
            margin: 6mm;
        }
    }
</style>

<div class="ac-do-page-wrap">
    <div class="ac-do-actions">
        <button type="button" onclick="acDoPrintWithImages()">Print / Save PDF</button>
        <button type="button" class="ac-do-share-btn" onclick="acDoSharePdf(this)">Share PDF</button>
    </div>

    <div class="ac-do-preview-page">
    <div class="ac-do-paper">
        <div class="ac-do-top-label">
            <span>DELIVERY ORDER</span>
        </div>

        <div class="ac-do-header">
            <div class="ac-do-logo">
                <img src="<?php echo esc_url($company_logo_url); ?>" alt="<?php echo esc_attr($company_name); ?>">
            </div>

            <div class="ac-do-company">
                <h1><?php echo ac_do_h($company_name); ?></h1>
                <p><?php echo ac_do_h($company_addr); ?></p>
                <p>H/P: <?php echo ac_do_h($company_tel); ?></p>
            </div>

            <div class="ac-do-doc-no">
                No <strong><?php echo ac_do_h($doc_no); ?></strong>
            </div>
        </div>

        <div class="ac-do-info-row">
            <div class="ac-do-line-field">
                <span>Customer</span>
                <div>
                    <?php echo ac_do_h($customer_name); ?>
                    <?php if ($customer_code !== ''): ?>
                        (<?php echo ac_do_h($customer_code); ?>)
                    <?php endif; ?>
                </div>
            </div>

            <div class="ac-do-line-field ac-do-date">
                <span>Date</span>
                <div><?php echo ac_do_h($display_date); ?></div>
            </div>
        </div>

        <table class="ac-do-table">
            <thead>
                <tr>
                    <th>数量<br>Quantity</th>
                    <th>公斤<br>Kg</th>
                    <th>货物名称<br>Description</th>
                    <th>箱 / 篮<br>Box / Basket</th>
                    <th>总公斤<br>Total Kg</th>
                </tr>
            </thead>

            <tbody>
                <?php foreach ($lines as $line): ?>
                    <?php
                    $pack_type = strtoupper((string)ac_do_pick($line, array('packType', 'PackType'), ''));
                    $is_ctn = ($pack_type === 'CARTON' || $pack_type === 'CTN');
                    $is_bsk = ($pack_type === 'BASKET' || $pack_type === 'BSK');

                    if ($is_ctn) {
                        $qty = ac_do_pick($line, array('cartonQty', 'ctnQty', 'qty', 'Qty'), '');
                    } elseif ($is_bsk) {
                        $qty = ac_do_pick($line, array('basketQty', 'bskQty', 'qty', 'Qty'), '');
                    } else {
                        $qty = ac_do_pick($line, array('cartonQty', 'basketQty', 'qty', 'Qty'), '');
                    }

                    $kg = ac_do_pick($line, array('kg', 'Kg', 'UDF_WEIGHTKG'), '');
                    $total_line_kg = ac_do_pick($line, array('totalKg', 'TotalKg', 'UDF_WEIGHTKG', 'kg', 'Kg'), '');
                    $description = ac_do_pick($line, array('description', 'Description', 'itemName', 'ItemName', 'itemDescription', 'ItemDescription', 'itemCode', 'ItemCode'), '');
                    ?>

                    <tr>
                        <td class="ac-do-qty"><?php echo ac_do_h(ac_do_num($qty)); ?></td>
                        <td class="ac-do-kg"><?php echo ac_do_h(ac_do_num($kg)); ?></td>
                        <td class="ac-do-desc"><?php echo ac_do_h($description); ?></td>
                        <td class="ac-do-pack">
                            <span class="ac-do-checkbox <?php echo $is_ctn ? 'checked' : ''; ?>"></span>Ctn
                            &nbsp;
                            <span class="ac-do-checkbox <?php echo $is_bsk ? 'checked' : ''; ?>"></span>Bsk
                        </td>
                        <td class="ac-do-total"><?php echo ac_do_h(ac_do_num($total_line_kg)); ?></td>
                    </tr>
                <?php endforeach; ?>

                <?php
                $minimum_rows = 14;
    S���`�Z���������XO�
?(�����                       </div>
                    </div>

                    <div class="driver-do-section">
                        <?php if (empty($lines)) : ?>
                            <div style="font-size:0.84rem;color:#69756d;padding:8px 0;">No item data found.</div>
                        <?php else : ?>
                            <table class="driver-do-items">
                                <thead>
                                    <tr>
                                        <th>Item</th>
                                        <th>Pack</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <?php foreach ($lines as $line) : ?>
                                        <?php
                                        $item_name = driver_do_pick_item_name($line);

                                        $basket = (float) driver_do_pick($line, array('basketQty', 'basket', 'Basket', 'UDF_BASKET'), 0);
                                        $carton = (float) driver_do_pick($line, array('cartonQty', 'carton', 'Carton', 'UDF_CARTON'), 0);

                                        $pack_type = strtoupper((string) driver_do_pick($line, array(
                                            'packType',
                                            'PackType',
                                            'pack_type',
                                            'type',
                                            'Type'
                                        ), ''));

                                        $pack_label = 'Basket';
                                        $pack_qty   = $basket;

                                        if ($carton > 0 || ($basket <= 0 && $pack_type === 'CARTON')) {
                                            $pack_label = 'Carton';
                                            $pack_qty   = $carton;
                                        } elseif ($basket > 0 || $pack_type === 'BASKET') {
                                            $pack_label = 'Basket';
                                            $pack_qty   = $basket;
                                        }
                                        ?>
                                        <tr>
                                            <td data-label="Item">
                                                <div class="driver-do-item-name"><?php echo esc_html($item_name); ?></div>
                                            </td>
                                            <td data-label="<?php echo esc_attr($pack_label); ?>">
                                                <span class="driver-do-pack-label"><?php echo esc_html($pack_label); ?>:</span>
                                                <span class="driver-do-pack-qty"><?php echo esc_html(driver_do_num($pack_qty)); ?></span>
                                            </td>
                                        </tr>
                                    <?php endforeach; ?>
                                </tbody>
                            </table>
                        <?php endif; ?>
                    </div>

                    <div class="driver-do-actions">
                        <?php if ($is_delivered) : ?>
                            <div class="driver-do-delivered-label">
                                Delivered
                            </div>
                        <?php elseif (!$can_complete) : ?>
                            <div class="driver-do-waiting-label">
                                Waiting DO
                            </div>
                        <?php else : ?>
                            <button 
                                type="button"
                                class="driver-do-btn primary driver-do-proof-btn"
                                data-job-id="<?php echo esc_attr($order->id); ?>"
                                data-doc-no="<?php echo esc_attr($doc_no); ?>"
                                data-doc-key="<?php echo esc_attr($doc_key); ?>"
                            >
                                Complete Delivery
                            </button>
                        <?php endif; ?>
                    </div>
                </div>
            <?php endforeach; ?>
        </div>
    <?php endif; ?>
</div>

<div class="driver-do-proof-modal" id="driverDoProofModal">
    <div class="driver-do-proof-panel">
        <div class="driver-do-proof-title">Proof of Delivery</div>
        <div class="driver-do-proof-text" id="driverDoProofText">
            Select or capture proof image before completing this delivery.
        </div>

        <img class="driver-do-proof-preview" id="driverDoProofPreview" alt="Proof preview">

        <input 
            type="file"
            accept="image/*"
            class="driver-do-proof-input"
            id="driverDoProofInput"
        >

        <div class="driver-do-proof-actions">
            <button type="button" class="driver-do-btn secondary" id="driverDoProofCancel">
                Cancel
            </button>

            <button type="button" class="driver-do-btn secondary" id="driverDoProofChoose">
                Choose Image
            </button>

            <button type="button" class="driver-do-btn primary" id="driverDoProofSubmit" disabled>
                Submit Proof
            </button>
        </div>
    </div>
</div>

<div class="driver-do-toast" id="driverDoToast"></div>

<script>
(function() {
    const restUrl = "<?php echo esc_js(rest_url('ac/v1/do-proof-upload')); ?>";
    const restNonce = "<?php echo esc_js(wp_create_nonce('wp_rest')); ?>";

    const modal = document.getElementById('driverDoProofModal');
    const proofText = document.getElementById('driverDoProofText');
    const proofInput = document.getElementById('driverDoProofInput');
    const proofPreview = document.getElementById('driverDoProofPreview');
    const chooseBtn = document.getElementById('driverDoProofChoose');
    const submitBtn = document.getElementById('driverDoProofSubmit');
    const cancelBtn = document.getElementById('driverDoProofCancel');
    const toast = document.getElementById('driverDoToast');

    let currentJobId = null;
    let currentDocNo = null;
    let currentDocKey = null;
    let selectedFile = null;

    function openModal(jobId, docNo, docKey) {
        currentJobId = jobId;
        currentDocNo = docNo;
        currentDocKey = docKey;
        selectedFile = null;

        proofText.textContent = 'Select or capture proof image for ' + docNo + '.';
        proofInput.value = '';
        proofPreview.removeAttribute('src');
        proofPreview.style.display = 'none';
        submitBtn.disabled = true;
        submitBtn.textContent = 'Submit Proof';

        modal.classList.add('active');
    }

    function closeModal() {
        modal.classList.remove('active');
        currentJobId = null;
        currentDocNo = null;
        currentDocKey = null;
        selectedFile = null;
    }

    function showToast(message) {
        toast.textContent = message;
        toast.classList.add('active');

        setTimeout(function() {
            toast.classList.remove('active');
        }, 2500);
    }

    document.querySelectorAll('.driver-do-proof-btn').forEach(function(btn) {
        btn.addEventListener('click', function() {
            openModal(this.dataset.jobId, this.dataset.docNo, this.dataset.docKey);
        });
    });

    chooseBtn.addEventListener('click', function() {
        proofInput.click();
    });

    proofInput.addEventListener('change', function() {
        if (!this.files || !this.files[0]) {
            return;
        }

        selectedFile = this.files[0];

        if (!selectedFile.type.startsWith('image/')) {
            alert('Please select an image file.');
            selectedFile = null;
            proofInput.value = '';
            submitBtn.disabled = true;
            return;
        }

        proofPreview.src = URL.createObjectURL(selectedFile);
        proofPreview.style.display = 'block';
        submitBtn.disabled = false;
    });

    cancelBtn.addEventListener('click', function() {
        closeModal();
    });

    modal.addEventListener('click', function(event) {
        if (event.target === modal) {
            closeModal();
        }
    });

    submitBtn.addEventListener('click', function() {
        if (!currentJobId || !selectedFile) {
            alert('Please select proof image first.');
            return;
        }

        const formData = new FormData();
        formData.append('job_id', currentJobId);
        formData.append('doc_no', currentDocNo);
        formData.append('doc_key', currentDocKey || '');
        formData.append('proof_type', 'DELIVERY_PROOF');
        formData.append('proof_image', selectedFile);

        submitBtn.disabled = true;
        submitBtn.textContent = 'Uploading...';

        fetch(restUrl, {
            method: 'POST',
            body: formData,
            credentials: 'same-origin',
            headers: {
                'X-WP-Nonce': restNonce
            }
        })
        .then(async function(response) {
            const data = await response.json().catch(function() {
                return {};
            });

            if (!response.ok || data.success === false) {
                const message = data.message ||
                    (data.data && data.data.message) ||
                    'Failed to upload proof.';

                throw new Error(message);
            }

            return data;
        })
        .then(function(data) {
            const message = data.message || 'Order ' + currentDocNo + ' Delivered.';

            closeModal();
            showToast(message);

            setTimeout(function() {
                window.location.reload();
            }, 900);
        })
        .catch(function(error) {
            alert(error.message);
            submitBtn.disabled = false;
            submitBtn.textContent = 'Submit Proof';
        });
    });
})();
</script>�XO�C�r-[��������)��M
??�_ine?.qty || 0)};
        }

        return {label:'Basket', qty:basket || whole(line?.qty || 0)};
    }

    function summaryMetrics(summary) {
        const chips = [
            `<div class="bdo-metric"><strong>${fmt(summary?.items)}</strong><span>Items</span></div>`
        ];

        if (whole(summary?.baskets) > 0) {
            chips.push(`<div class="bdo-metric"><strong>${fmt(summary.baskets)}</strong><span>Baskets</span></div>`);
        }

        if (whole(summary?.cartons) > 0) {
            chips.push(`<div class="bdo-metric"><strong>${fmt(summary.cartons)}</strong><span>Cartons</span></div>`);
        }

        if (whole(summary?.kg) > 0) {
            chips.push(`<div class="bdo-metric"><strong>${fmt(summary.kg)}</strong><span>KG</span></div>`);
        }

        return chips.join('');
    }

    function renderItems(job) {
        const lines = job?.summary?.lines || [];
        if (!lines.length) return '<div class="bdo-empty">No item detail found.</div>';
        return `<div class="bdo-items">${lines.map(l => {
            const pack = packInfo(l);
            return `
                <div class="bdo-item">
                    <div class="bdo-item-name">${esc(l.itemName || 'Item')}</div>
                    <div class="bdo-item-grid">
                        <div><small>${esc(pack.label)}</small><strong>${fmt(pack.qty)}</strong></div>
                        <div><small>KG</small><strong>${fmt(l.kg)}</strong></div>
                    </div>
                </div>`;
        }).join('')}</div>`;
    }

    function receiptFileName(receipt) {
        const ref = String(receipt?.sourceRef || receipt?.id || 'basket-return').replace(/[^A-Za-z0-9_-]/g, '-');
        return `Basket-Return-${ref}.pdf`;
    }

    function doFileName(job) {
        const ref = String(job?.docNo || job?.id || 'DO').replace(/[^A-Za-z0-9_-]/g, '-');
        return `Delivery-Order-${ref}.pdf`;
    }

    function receiptHtml(receipt, includeActions=true) {
        if (!receipt || !receipt.id) return '';

        return `
            <div class="bdo-receipt-card" data-bdo-receipt-id="${esc(receipt.id)}">
                <div class="bdo-receipt-paper">
                    <div class="bdo-receipt-head">
                        <div>
                            <img class="bdo-receipt-logo" src="${receiptLogoUrl}" alt="Company logo">
                        </div>
                        <div>
                            <div class="bdo-receipt-title">BASKET RETURN</div>
                            <div class="bdo-receipt-no">${esc(receipt.sourceRef || ('BR-' + receipt.id))}</div>
                        </div>
                    </div>
                    <div class="bdo-receipt-info">
                        <div class="bdo-receipt-field"><span>Customer</span><strong>${esc(receipt.customerName || 'Customer')}</strong></div>
                        <div class="bdo-receipt-field"><span>Driver</span><strong>${esc(receipt.driverName || '')}</strong></div>
                    </div>
                    <div class="bdo-receipt-qty">${fmt(receipt.qty)} BASKETS</div>
                    <div class="bdo-receipt-proof">
                        ${receipt.proofUrl ? `Image Proof<img src="${esc(receipt.proofUrl)}" alt="Basket return proof">` : 'No image proof uploaded'}
                    </div>
                </div>
                ${includeActions ? `
                    <div class="bdo-receipt-actions">
                        <button type="button" class="bdo-btn-soft" data-print-basket-receipt="${esc(receipt.id)}">Print</button>
                        <button type="button" class="bdo-btn" data-share-basket-receipt="${esc(receipt.id)}">Share PDF</button>
                    </div>` : ''}
            </div>`;
    }

    function renderSelectedBasketReceipt() {
        const mount = $('bdoBasketReceiptMount');
        if (!mount) return;

        const receipt = selectedBasketReceiptId ? basketReceipts[selectedBasketReceiptId] : null;
        if (!receipt) {
            mount.style.display = 'none';
            mount.innerHTML = '';
            return;
        }

        mount.style.display = '';
        mount.innerHTML = receiptHtml(receipt, true);
    }

    function shortageForm(jobId) {
        return `
            <form method="post" class="bdo-action-form bdo-shortage-form">
                <input type="hidden" name="bdo_driver_action" value="report_shortage">
                <input type="hidden" name="bdo_driver_nonce" value="__FORM_NONCE__">
                <input type="hidden" name="job_id" value="${jobId}">
                <button type="submit" class="bdo-btn bdo-btn-warn" data-bdo-loading-text="Sending...">Not Enough Item - Send Back To Staff</button>
            </form>`;
    }

    function activeCardHtml(job, compact=false) {
        if (!job || !job.id) {
            return '<div class="bdo-empty">No ready delivery yet. It will appear after staff assigns the DO and AutoCount sync completes.</div>';
        }

        const st = String(job.deliveryStatus || '').toUpperCase();
        const needsReceive = st === 'ASSIGNED';
        const canDeliver = st === 'OUT_FOR_DELIVERY' || st === 'DRIVER_ACKNOWLEDGED';
        const docText = job.docNo || ('Job #' + job.id);
        const location = String(job.location || '').trim();
        const locationText = location && location.toUpperCase() !== 'HQ' ? `Location: ${esc(location)}` : '';
        const addressText = job.address ? `Address: ${esc(job.address)}` : '';

        let html = `
            <div class="bdo-active-mini">
                <span class="bdo-status-chip${needsReceive ? ' wait' : ''}">${esc(statusLabel(job))}</span>
                <div class="bdo-job-main">
                    <h3>${esc(job.customerName || 'Customer')}</h3>
                    <p>${esc(docText)}</p>
                    ${locationText || addressText ? `<div class="bdo-job-note">${locationText}${locationText && addressText ? '<br>' : ''}${addressText}</div>` : ''}
                </div>
                <div class="bdo-job-metrics">
                    ${summaryMetrics(job.summary)}
                </div>
                ${compact ? '' : renderItems(job)}`;

        if (compact) {
            html += `<div style="height:10px"></div><button type="button" class="bdo-btn" data-select-delivery="${job.id}">${needsReceive ? 'Confirm Receive' : 'Open Delivery'}</button>`;
        } else if (needsReceive) {
            html += `
                <div style="height:12px"></div>
                <form method="post" class="bdo-action-form">
                    <input type="hidden" name="bdo_driver_action" value="confirm_received">
                    <input type="hidden" name="bdo_driver_nonce" value="__FORM_NONCE__">
                    <input type="hidden" name="job_id" value="${job.id}">
                    <button type="submit" class="bdo-btn" data-bdo-loading-text="Confirming...">Confirm Received From Staff</button>
                </form>
                ${shortageForm(job.id)}`;
        } else if (canDeliver) {
            html += `
                <div style="height:12px"></div>
                <form method="post" enctype="multipart/form-data" class="bdo-proof-box bdo-action-form" id="bdoCompleteDeliveryForm">
                    <input type="hidden" name="bdo_driver_action" value="complete_delivery">
                    <input type="hidden" name="bdo_driver_nonce" value="__FORM_NONCE__">
                    <input type="hidden" name="job_id" value="${job.id}">
                    <strong>Proof of delivery optional</strong>
                    <p>Take photo after vegetables are dropped, or continue without photo if needed.</p>
                    <input type="file" class="bdo-file" name="delivery_proof" accept="image/jpeg,image/png,image/webp" capture="environment">
                    <div class="bdo-file-hint">JPG, PNG, or WebP only. Max 4 MB.</div>
                    <div style="height:12px"></div>
                    <button type="submit" class="bdo-btn" data-bdo-loading-text="Saving...">Mark as Delivered</button>
                </form>`;
        } else {
            html += '<div class="bdo-alert ok" style="margin:0;">No driver action needed.</div>';
        }

        html += '</div>';
        return html;
    }

    function deliveryListCard(job) {
        const st = String(job.deliveryStatus || '').toUpperCase();
        const isDelivered = st === 'DELIVERED';
        const needsReceive = st === 'ASSIGNED';
        const docText = job.docNo || ('Job #' + job.id);
        const subText = job.address ? `${docText} | ${job.address}` : docText;
        return `
            <div class="bdo-delivery-row">
                <div class="bdo-delivery-top">
                    <div>
                        <div class="bdo-delivery-title">${esc(job.customerName || 'Customer')}</div>
                        <div class="bdo-delivery-sub">${esc(subText)}</div>
                    </div>
                    <span class="bdo-status-chip${needsReceive ? ' wait' : ''}">${esc(statusLabel(job))}</span>
                </div>
                <div class="bdo-delivery-metrics">
                    ${summaryMetrics(job.summary)}
                </div>
                ${isDelivered ? `
                    <div class="bdo-two">
                        <button type="button" class="bdo-btn-soft" data-download-do="${esc(job.id)}">Download DO</button>
                        <button type="button" class="bdo-btn" data-share-do="${esc(job.id)}">Share WhatsApp</button>
                    </div>` : `<button type="button" class="bdo-btn" data-select-delivery="${job.id}">${needsReceive ? 'Confirm Receive' : 'Take POD'}</button>`}
            </div>`;
    }

    function returnListCard(row) {
        return `
            <div class="bdo-delivery-row">
                <div class="bdo-delivery-top">
                    <div>
                        <div class="bdo-delivery-title">${esc(row.customerName || 'Customer')}</div>
                        <div class="bdo-delivery-sub">Basket return record</div>
                    </div>
                    <span class="bdo-status-chip">RETURN</span>
                </div>
                <div class="bdo-delivery-metrics" style="grid-template-columns:1fr">
                    <span>${fmt(row.qty)} Baskets</span>
                </div>
                <div class="bdo-two">
                    <button type="button" class="bdo-btn-soft" data-print-basket-receipt="${esc(row.id)}">Print</button>
                    <button type="button" class="bdo-btn" data-share-basket-receipt="${esc(row.id)}">Share PDF</button>
                </div>
            </div>`;
    }

    function renderDeliveryList() {
        const mount = $('bdoDeliveryListMount');
        if (!mount) return;

        updateStatHighlight();

        let title = 'Deliveries waiting for receive confirmation';
        let rows = activeJobs.filter(j => String(j.deliveryStatus || '').toUpperCase() === 'ASSIGNED');
        let renderer = deliveryListCard;

        if (deliveryFilter === 'received') {
            title = 'Deliveries ready for POD';
            rows = activeJobs.filter(j => ['OUT_FOR_DELIVERY','DRIVER_ACKNOWLEDGED'].includes(String(j.deliveryStatus || '').toUpperCase()));
        } else if (deliveryFilter === 'delivered') {
            title = 'Delivered today';
            rows = deliveredJobs;
        } else if (deliveryFilter === 'returns') {
            title = 'Basket returns today';
            rows = returnRows;
            renderer = returnListCard;
        }

        if (!rows.length) {
            mount.innerHTML = `<div class="bdo-empty">No record found. ${esc(title)}.</div>`;
            return;
        }

        mount.innerHTML = `<div style="font-size:13px;color:#617067;font-weight:900;margin-bottom:10px">${esc(title)}</div>` + rows.map(renderer).join('');
    }

    function renderActive() {
        $('bdoHomeActiveMount').innerHTML = activeCardHtml(activeJob, true);
        $('bdoActiveMount').innerHTML = activeCardHtml(activeJob, false);
        renderDeliveryList();
        renderSelectedBasketReceipt();
    }

    function loadJsPdf() {
        if (window.jspdf && window.jspdf.jsPDF) {
            return Promise.resolve(window.jspdf.jsPDF);
        }

        if (brJsPdfPromise) {
            return brJsPdfPromise;
        }

        brJsPdfPromise = new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
            script.onload = () => window.jspdf && window.jspdf.jsPDF ? resolve(window.jspdf.jsPDF) : reject(new Error('PDF library did not load.'));
            script.onerror = () => reject(new Error('PDF library could not be loaded.'));
            document.head.appendChild(script);
        });

        return brJsPdfPromise;
    }

    function loadProofImage(url) {
        if (!url) return Promise.resolve(null);

        return new Promise(resolve => {
            const img = new Image();
            img.crossOrigin = 'anonymous';
            img.onload = () => resolve(img);
            img.onerror = () => resolve(null);
            img.src = url;
        });
    }

    function drawText(ctx, text, x, y, size=18, color='#111', weight='400', align='left') {
        ctx.fillStyle = color;
        ctx.font = `${weight} ${size}px Arial, sans-serif`;
        ctx.textAlign = align;
        ctx.textBaseline = 'top';
        ctx.fillText(String(text || ''), x, y);
    }

    function drawWrappedText(ctx, text, x, y, maxWidth, lineHeight, size=18, color='#111', weight='400') {
        const words = String(text || '').split(/\s+/).filter(Boolean);
        let line = '';

        words.forEach(word => {
            const testLine = line ? `${line} ${word}` : word;
            if (ctx.measureText(testLine).width > maxWidth && line) {
                drawText(ctx, line, x, y, size, color, weight);
                line = word;
                y += lineHeight;
            } else {
                line = testLine;
            }
        });

        if (line) drawText(ctx, line, x, y, size, color, weight);
        return y + lineHeight;
    }

    function drawCanvasImageContained(ctx, image, x, y, maxW, maxH) {
        if (!image) return;

        const ratio = Math.min(maxW / image.width, maxH / image.height);
        const imgW = image.width * ratio;
        const imgH = image.height * ratio;
        ctx.drawImage(image, x + (maxW - imgW) / 2, y + (maxH - imgH) / 2, imgW, imgH);
    }

    function doCanvasText(ctx, text, x, y, size, color='#111111', weight='400', align='left') {
        ctx.fillStyle = color;
        ctx.font = `${weight} ${size}px Arial, Helvetica, sans-serif`;
        ctx.textAlign = align;
        ctx.textBaseline = 'alphabetic';
        ctx.fillText(String(text || ''), x, y);
    }

    function doCanvasLine(ctx, x1, y1, x2, y2, color='#333333', width=1) {
        ctx.strokeStyle = color;
        ctx.lineWidth = width;
        ctx.beginPath();
        ctx.moveTo(x1, y1);
        ctx.lineTo(x2, y2);
        ctx.stroke();
    }

    function doCanvasRect(ctx, x, y, width, height, color='#333333', lineWidth=1) {
        ctx.strokeStyle = color;
        ctx.lineWidth = lineWidth;
        ctx.strokeRect(x, y, width, height);
    }

    function doCanvasFillRect(ctx, x, y, width, height, color) {
        ctx.fillStyle = color;
        ctx.fillRect(x, y, width, height);
    }

    function doCanvasWrap(ctx, text, x, y, maxWidth, lineHeight, size=18, color='#111111', weight='400', maxLines=2) {
        let words = String(text || '').split(/\s+/).filter(Boolean);
        let line = '';
        let lines = [];

        ctx.fillStyle = color;
        ctx.font = `${weight} ${size}px Arial, Helvetica, sans-serif`;
        ctx.textAlign = 'left';
        ctx.textBaseline = 'alphabetic';

        words.forEach(word =)��M��h\��������`�
??�`ver-app .bdo-picker-close {
        width: 40px !important;
        height: 40px !important;
        min-width: 40px !important;
        min-height: 40px !important;
        padding: 0 !important;
        margin: 0 !important;
        display: inline-flex !important;
        align-items: center !important;
        justify-content: center !important;
        border: 0 !important;
        border-radius: 14px !important;
        background: var(--main) !important;
        color: #fff !important;
        font-size: 26px !important;
        font-weight: 800 !important;
        line-height: 1 !important;
        font-family: Arial, sans-serif !important;
        cursor: pointer !important;
        appearance: none !important;
        -webkit-appearance: none !important;
        box-shadow: 0 8px 18px rgba(11, 74, 45, .18) !important;
    }
    #bdo-driver-app .bdo-picker-close:hover,
    #bdo-driver-app .bdo-picker-close:focus,
    #bdo-driver-app .bdo-picker-close:active {
        background: var(--main-2) !important;
        color: #fff !important;
        outline: none !important;
    }
    .bdo-picker-body { padding:14px; }
    .bdo-picker-results { max-height:55vh; overflow-y:auto; margin-top:10px; }
    .bdo-picker-item { display:block; width:100%; text-align:left; background:#fff; border:1px solid var(--line); border-radius:15px; padding:12px; margin-bottom:8px; cursor:pointer; }
    .bdo-picker-item:hover { background:var(--main-soft); border-color:var(--main-soft-2); }
    .bdo-picker-main { display:block; font-weight:950; color:var(--ink); }
    .bdo-picker-sub { display:block; font-size:12px; color:var(--muted); font-weight:700; margin-top:3px; }
    .bdo-picker-note { padding:18px; text-align:center; color:var(--muted); font-weight:800; }
    .bdo-bottom-nav { position:fixed; left:50%; transform:translateX(-50%); bottom:0; width:100%; max-width:560px; background:rgba(255,255,255,.96); backdrop-filter:blur(12px); border-top:1px solid var(--line); display:grid; grid-template-columns:repeat(4,1fr); padding:8px 8px 12px; z-index:9999; box-shadow:0 -12px 28px rgba(10,45,29,.09); }
    .bdo-nav { border:0; background:transparent; color:#53645a; font-size:11px; font-weight:900; min-height:50px; cursor:pointer; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; }
    .bdo-nav i { font-style:normal; font-size:20px; }
    #bdo-driver-app .bdo-nav.active { color:var(--main) !important; background:var(--main-soft) !important; border-radius:14px; }
    #bdo-driver-app .bdo-nav.active i { color:var(--main) !important; }
    .bdo-alert { margin-bottom:12px; padding:12px 14px; border-radius:16px; font-weight:850; font-size:13px; }
    .bdo-alert.ok { background:#dcfce7; color:#14532d; border:1px solid #bbf7d0; }
    .bdo-alert.err { background:#fff1f2; color:#9f1239; border:1px solid #fecdd3; }
    .bdo-inline-receipt { position:fixed; inset:0; z-index:99998; background:#e5e5e5; overflow:auto; padding:20px 0 40px; }
    .bdo-receipt-actions { width:380px; max-width:calc(100vw - 24px); margin:0 auto 12px; display:flex; justify-content:flex-end; gap:8px; }
    .bdo-receipt-actions button { border:0; background:#0B4A2D; color:#fff; padding:9px 14px; border-radius:8px; font-weight:800; cursor:pointer; }
    /* Updated receipt paper styles (centered, proof image) */
    .bdo-receipt-paper {
        width: 380px;
        max-width: calc(100vw - 24px);
        margin: 0 auto;
        background: #fff;
        padding: 18px 20px;
        border: 1px solid #222;
        font-size: 13px;
        color: #222;
        text-align: center;
    }
    .bdo-receipt-company { text-align:center; font-weight:900; font-size:15px; margin-bottom:6px; }
    .bdo-receipt-title { text-align:center; font-weight:900; font-size:14px; border-top:1px dashed #555; border-bottom:1px dashed #555; padding:7px 0; margin-bottom:12px; }
    .bdo-receipt-row {
        display: block;
        margin-bottom: 9px;
    }
    .bdo-receipt-row strong {
        display: block;
        font-size: 11px;
        text-transform: uppercase;
        color: #555;
        margin-bottom: 3px;
    }
    .bdo-receipt-row span {
        display: block;
        font-weight: 800;
    }
    .bdo-receipt-total { margin:14px 0; padding:12px; border:1px solid #222; text-align:center; }
    .bdo-receipt-total span { display:block; font-size:12px; font-weight:800; margin-bottom:4px; }
    .bdo-receipt-total strong { display:block; font-size:34px; font-weight:950; line-height:1; }
    .bdo-receipt-proof {
        margin-top: 14px;
        padding-top: 12px;
        border-top: 1px dashed #555;
        text-align: center;
    }
    .bdo-receipt-proof-title {
        font-weight: 900;
        font-size: 12px;
        margin-bottom: 8px;
    }
    .bdo-receipt-proof-img {
        display: block;
        width: 100%;
        max-height: 520px;
        object-fit: contain;
        border: 1px solid #ddd;
        background: #f8f8f8;
        margin: 0 auto;
    }
    .bdo-receipt-proof-empty {
        padding: 12px;
        border: 1px dashed #bbb;
        color: #666;
        font-size: 12px;
    }
    .bdo-receipt-footer { margin-top:18px; text-align:center; font-size:11px; color:#555; }
    .bdo-return-metrics {
        grid-template-columns: 1fr !important;
    }

    /* Inline Delivery Order receipt - matches client template style */
    .bdo-do-receipt-overlay {
        position: fixed;
        inset: 0;
        z-index: 99998;
        background: #d9d9d9;
        overflow: auto;
        padding: 20px 20px 44px;
        font-family: Arial, Helvetica, sans-serif;
        color: #0b1720;
    }
    .bdo-do-receipt-actions {
        width: 794px;
        max-width: calc(100vw - 24px);
        margin: 0 auto 12px;
        display: flex;
        justify-content: flex-end;
        gap: 8px;
    }
    .bdo-do-receipt-actions button {
        border: 0;
        background: #0B4A2D;
        color: #fff;
        padding: 9px 14px;
        border-radius: 8px;
        font-weight: 800;
        cursor: pointer;
    }
    .bdo-do-paper {
        width: 794px;
        min-width: 794px;
        min-height: 1123px;
        max-width: none;
        margin: 0 auto;
        background: #ddf4ea;
        border: 1px solid #9ab8ac;
        padding: 82px 42px 48px;
        font-size: 13px;
        color: #111827;
        box-shadow: 0 10px 30px rgba(0,0,0,.12);
    }
    .bdo-do-head {
        display: grid;
        grid-template-columns: 90px 1fr 150px;
        gap: 12px;
        align-items: start;
        margin-bottom: 24px;
    }
    .bdo-do-logo {
        font-size: 29px;
        font-weight: 900;
        font-style: italic;
        color: #0B4A2D;
        line-height: 1;
        padding-top: 22px;
    }
    .bdo-do-company {
        text-align: left;
    }
    .bdo-do-company h1 {
        margin: 0 0 5px;
        font-size: 27px;
        letter-spacing: 1px;
        line-height: 1;
        font-weight: 900;
    }
    .bdo-do-company div {
        font-size: 9px;
        line-height: 1.3;
    }
    .bdo-do-side {
        text-align: right;
    }
    .bdo-do-badge {
        display: inline-block;
        background: #3e3e3e;
        color: #fff;
        border-radius: 9px;
        padding: 5px 14px;
        font-size: 13px;
        font-weight: 900;
        margin-bottom: 44px;
    }
    .bdo-do-no {
        font-size: 13px;
        white-space: nowrap;
    }
    .bdo-do-no strong {
        color: #ef4444;
        font-size: 20px;
        letter-spacing: 1px;
    }
    .bdo-do-info {
        display: grid;
        grid-template-columns: 1fr 190px;
        gap: 18px;
        align-items: end;
        margin-bottom: 12px;
    }
    .bdo-do-info-row {
        display: grid;
        grid-template-columns: 72px 1fr;
        align-items: end;
        gap: 6px;
        font-size: 14px;
    }
    .bdo-do-info-row.date {
        grid-template-columns: 44px 1fr;
    }
    .bdo-do-info-row strong {
        font-weight: 900;
    }
    .bdo-do-info-line {
        border-bottom: 1px dotted #333;
        min-height: 21px;
        padding-left: 3px;
        font-size: 14px;
    }
    .bdo-do-table {
        width: 100%;
        border-collapse: collapse;
        table-layout: fixed;
        font-size: 13px;
    }
    .bdo-do-table th,
    .bdo-do-table td {
        border: 1px solid #39443e;
        padding: 5px 6px;
        height: 32px;
        vertical-align: middle;
    }
    .bdo-do-table th {
        text-align: center;
        font-weight: 900;
        font-size: 11px;
        line-height: 1.05;
    }
    .bdo-do-table th span {
        display: block;
        font-size: 10px;
        font-weight: 800;
    }
    .bdo-do-table td.center {
        text-align: center;
    }
    .bdo-do-table td.desc {
        font-size: 14px;
    }
    .bdo-do-checks {
        display: flex;
        justify-content: center;
        gap: 7px;
        white-space: nowrap;
        font-size: 11px;
    }
    .bdo-do-box {
        display: inline-block;
        width: 11px;
        height: 11px;
        border: 1px solid #111;
        vertical-align: -1px;
        margin-right: 2px;
        position: relative;
        background: transparent;
    }
    .bdo-do-box.checked::after {
        content: '✓';
        position: absolute;
        left: 0px;
        top: -7px;
        font-size: 20px;
        font-weight: 900;
        color: #111;
    }
    .bdo-do-bottom {
        display: grid;
        grid-template-columns: 1fr 170px;
        gap: 24px;
        margin-top: 30px;
        align-items: end;
    }
    .bdo-do-slogan {
        font-weight: 900;
        font-style: italic;
        font-size: 15px;
        padding-bottom: 30px;
    }
    .bdo-do-totals {
        display: grid;
        grid-template-columns: 1fr 80px;
        gap: 0;
        align-items: center;
    }
    .bdo-do-total-label {
        text-align: right;
        padding-right: 9px;
        font-size: 14px;
        line-height: 1.05;
    }
    .bdo-do-total-label span {
        display: block;
        font-size: 12px;
    }
    .bdo-do-total-box {
        border: 1px solid #39443e;
        height: 32px;
        display: grid;
        place-items: center;
        font-weight: 900;
        background: rgba(255,255,255,.24);
    }
    /* FIXED: Proof image pagination – always starts on a new page */
    .bdo-do-proof-page {
        width: 794px;
        min-width: 794px;
        margin: 18px auto 0;
        background: #fff;
        border: 1px solid #aaa;
        padding: 24px;
        page-break-before: always;
        break-before: page;
    }
    .bdo-do-proof-page h2 { margin:0 0 14px; font-size:20px; }
    .bdo-do-proof-img { width:100%; max-height:980px; object-fit:contain; border:1px solid #ddd; background:#f8f8f8; }

    @media (min-width:720px) { .bdo-wrap { padding-top:22px; } .bdo-card-body { padding:17px; } }
    @media (max-width:390px) { .bdo-stat strong { font-size:20px; } .bdo-stat span { font-size:9.5px; } .bdo-two { grid-template-columns:1fr; } .bdo-quick-grid { grid-template-columns:1fr; } }
    @media print {
        .bdo-do-proof-page {
            width: 210mm !important;
            min-width: 210mm !important;
            margin: 0 auto !important;
            border: 0 !important;
            box-shadow: none !important;
            page-break-before: always !important;
            break-before: page !important;
        }
        #wpadminbar, header, footer, .site-header, .site-footer, .elementor-location-header, .elementor-location-footer,
        #bdo-driver-app > .bdo-wrap, .bdo-bottom-nav, .bdo-receipt-actions, .bdo-do-receipt-actions { display:none !important; }
        .bdo-inline-receipt { position:static !important; inset:auto !important; padding:0 !important; background:#fff !important; }
        .bdo-receipt-paper { width:80mm !important; border:0 !important; padding:5mm !important; }
        .bdo-do-receipt-overlay { position:static !important; inset:auto !important; padding:0 !important; background:#fff !important; overflow:visible !important; }
        .bdo-do-paper { width:210mm !important; min-width:210mm !important; min-height:297mm !important; max-width:none !important; margin:0 auto !important; box-shadow:none !important; border:0 !important; }
        @page { size:A4 portrait; margin:0; }
    }
</style>

<div id="bdo-driver-app"
     data-rest-nonce="__REST_NONCE__"
     data-ajax-url="__AJAX_URL__"
     data-debtor-nonce="__DEBTOR_NONCE__"
     data-item-nonce="__ITEM_NONCE__"
     data-rest-job-post="__REST_JOB_POST__"
     data-rest-job-base="__REST_JOB_BASE__"
     data-rest-receipt-token="__REST_RECEIPT_TOKEN__"
     data-receipt-base="__RECEIPT_BASE__"
     data-default-location="__DEFAULT_LOCATION__"
     data-today="__TODAY__"
     data-initial-tab="__INITIAL_TAB__"
     data-initial-filter="__INITIAL_FILTER__">

    __INLINE_BR_RECEIPT__
    <div id="bdoDoReceiptMount"></div>

    <div class="bdo-wrap">
        <div class="bdo-top">
            <div>
                <div class="bdo-brand">BasketDO Driver</div>
            </div>
            <div class="bdo-date">
                <div>__TODAY_LABEL__</div>
                <div class="bdo-top-actions">
                    <div class="bdo-online"><span class="bdo-dot-live"></span> Online</div>
                    <a class="bdo-logout" href="__LOGOUT_URL__">Logout</a>
                </div>
            </div>
        </div>

        __ALERT_HTML__

        <div class="bdo-hero">
            <div class="bdo-driver-row">
                <div class="bdo-avatar-row">
                    <div class="bdo-avatar">🚚</div>
                    <div>
                        <div class="bdo-greet">Good morning,</div>
                        <div class="bdo-name">__DRIVER_NAME__</div>
                    </div>
                </div>
                <div class="bdo-mini-status">Ready</div>
            </div>

            <div class="bdo-stats">
                <button type="button" class="bdo-stat" data-stat-filter="today"><strong id="bdoStatCreated">0</strong><span>DO<br>Today</span></button>
                <button type="button" class="bdo-stat" data-stat-filter="active"><strong id="bdoStatActive">0</strong><span>Active<br>Delivery</span></button>
                <button type="button" class="bdo-stat" data-stat-filter="delivered"><strong id="bdoStatDelivered">0</strong><span>Delivered<br>Today</span></button>
                <button type="button" class="bdo-stat" data-stat-filter="returns"><strong id="bdoStatReturn">0</strong><span>Return<br>Basket</span></button>
            </div>
        </div>

        <div class="bdo-panel active" data-panel="home">
            <div class="bdo-quick-grid">
                <button type="button" class="bdo-quick" data-open-panel="create"><div class="bdo-quick-icon">+</div><strong>Create DO</strong><span>Use at warehouse after collecting vegetables.</span></button>
                <button type="button" class="bdo-quick" data-open-panel="deliveries"><div class="bdo-quick-icon">✓</div><strong>Deliveries</strong><span>Choose customer first, then capture POD.</span></button>
                <button type="button" class="bdo-quick" data-open-panel="return"><div class="bdo-quick-icon">↩</div><strong>Basket Return</strong><span>Record returned basket with photo proof.</span></button>
                <button type="button" class="bdo-quick" id="bdoRefreshBtn"><div class="bdo-quick-icon">⟳</div><strong>Refresh</strong><span>Reload latest job status from server.</span></button>
            </div>

            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Next Delivery</div><div class="bdo-card-sub">Latest delivery waiting for POD</div></div>
                </div>
                <div class="bdo-card-body">
                   `�;��]��������+��g
??�lySelectedCustomers(rows){
    const selectedCodes = selectedDebtorCodes();
    if (!selectedCodes.length) return rows;
    const selectedSet = new Set(selectedCodes);
    return rows.filter(r => selectedSet.has(debtorKey(r.debtorCode || r.debtor_code)));
  }

  async function apiGet(url){
    const res = await fetch(url, {
      method:'GET',
      credentials:'same-origin',
      headers: { 'Accept':'application/json', 'X-WP-Nonce': REST_NONCE },
      cache:'no-store'
    });

    if (!res.ok) {
      let errMsg = 'HTTP ' + res.status;
      try {
        const errData = await res.json();
        errMsg = errData.message || errData.error || errMsg;
      } catch(e) {}
      throw new Error(errMsg);
    }

    return await res.json();
  }

  function buildSummaryUrl(selectedDebtor=null){
    const url = new URL(REST_SUMMARY_URL, window.location.origin);
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo   = ($('ac_bs_date_to').value || '').trim();
    const debtorCode = selectedDebtor
      ? (selectedDebtor.code || '').trim()
      : (selectedDebtors.length === 1 ? (selectedDebtors[0].code || '').trim() : '');
    const debtorName = selectedDebtor
      ? (selectedDebtor.name || '').trim()
      : (selectedDebtors.length === 1 ? (selectedDebtors[0].name || '').trim() : '');

    if (debtorCode) url.searchParams.set('debtorCode', debtorCode);
    if (!debtorCode && debtorName) url.searchParams.set('q', debtorName);
    if (dateFrom) url.searchParams.set('dateFrom', dateFrom);
    if (dateTo) url.searchParams.set('dateTo', dateTo);
    url.searchParams.set('limit', '500');
    return url.toString();
  }

  function buildLedgerUrl(debtorCode){
    const url = new URL(REST_LEDGER_URL, window.location.origin);
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo   = ($('ac_bs_date_to').value || '').trim();

    url.searchParams.set('debtorCode', debtorCode);
    if (dateFrom) url.searchParams.set('dateFrom', dateFrom);
    if (dateTo) url.searchParams.set('dateTo', dateTo);
    url.searchParams.set('limit', '200');
    return url.toString();
  }

  function dateRangeError(){
    const dateFrom = ($('ac_bs_date_from').value || '').trim();
    const dateTo = ($('ac_bs_date_to').value || '').trim();
    if (dateFrom && dateTo && dateFrom > dateTo) {
      return 'Date From cannot be later than Date To.';
    }
    return '';
  }

  function renderTotals(rows){
    const totalsEl = $('ac_bs_totals');
    if (!rows.length) {
      if (totalsEl) totalsEl.style.display = 'none';
      return;
    }

    if (totalsEl) totalsEl.style.display = 'grid';

    const totals = rows.reduce((acc, r) => ({
      send: acc.send + Number(r.sendQty || 0),
      returned: acc.returned + Number(r.returnQty || 0),
      outstanding: acc.outstanding + Number(r.outstandingQty || 0),
      positiveOutstanding: acc.positiveOutstanding + Math.max(0, Number(r.outstandingQty || 0)),
      overReturn: acc.overReturn + Math.abs(Math.min(0, Number(r.outstandingQty || 0)))
    }), { send: 0, returned: 0, outstanding: 0, positiveOutstanding: 0, overReturn: 0 });

    const selectedCustomer = selectedDebtors.length > 0;
    const issueRows = rows.filter(r => Number(r.outstandingQty || 0) < 0);
    if (selectedCustomer) {
      const latestActivity = rows.reduce((latest, r) => {
        const date = r.lastTxnDate || r.last_txn_date || '';
        return dateSortValue(date) > dateSortValue(latest) ? date : latest;
      }, '');
      const customerOutstanding = totals.positiveOutstanding;
      const outstandingSub = totals.overReturn > 0
        ? 'Excludes ' + fmtQty(totals.overReturn) + ' over-return'
        : (customerOutstanding > 0 ? 'Needs follow-up' : 'Balanced');

      const selectedCountCard = selectedDebtors.length > 1
        ? '<div class="bs-total-box"><div class="bs-total-label">Selected Customers</div><div class="bs-total-value">' + fmtQty(selectedDebtors.length) + '</div><div class="bs-total-sub">Combined basket position</div></div>'
        : '';

      totalsEl.innerHTML =
        selectedCountCard +
        '<div class="bs-total-box"><div class="bs-total-label">Basket Sent</div><div class="bs-total-value">' + fmtQty(totals.send) + '</div></div>' +
        '<div class="bs-total-box"><div class="bs-total-label">Basket Returned</div><div class="bs-total-value">' + fmtQty(totals.returned) + '</div></div>' +
        '<div class="bs-total-box"><div class="bs-total-label">Outstanding Basket</div><div class="bs-total-value">' + fmtQty(customerOutstanding) + '</div><div class="bs-total-sub">' + esc(outstandingSub) + '</div></div>' +
        '<div class="bs-total-box"><div class="bs-total-label">Last Activity</div><div class="bs-total-value">' + esc(latestActivity || '-') + '</div></div>' +
        (totals.overReturn > 0
          ? '<div class="bs-total-box issue"><div class="bs-total-label">Data Issues</div><div class="bs-total-value">' + fmtQty(issueRows.length) + '</div><div class="bs-total-sub">Over-return total: ' + fmtQty(totals.overReturn) + '</div></div>'
          : '');
      return;
    }

    const outstandingRows = rows.filter(r => Number(r.outstandingQty || 0) > 0);
    const highest = outstandingRows.slice().sort((a, b) => {
      const qtyDiff = Number(b.outstandingQty || 0) - Number(a.outstandingQty || 0);
      if (qtyDiff !== 0) return qtyDiff;
      return compareSummaryByLastActivity(a, b);
    })[0] || null;
    const highestName = highest ? (highest.debtorName || highest.debtor_name || highest.debtorCode || highest.debtor_code || '-') : 'None';
    const highestQty = highest ? Number(highest.outstandingQty || 0) : 0;

    totalsEl.innerHTML =
      '<div class="bs-total-box"><div class="bs-total-label">Customers Active</div><div class="bs-total-value">' + fmtQty(rows.length) + '</div><div class="bs-total-sub">Has basket movement in range</div></div>' +
      '<div class="bs-total-box"><div class="bs-total-label">Customers With Outstanding</div><div class="bs-total-value">' + fmtQty(outstandingRows.length) + '</div><div class="bs-total-sub">Needs follow-up</div></div>' +
      '<div class="bs-total-box"><div class="bs-total-label">Total Outstanding</div><div class="bs-total-value">' + fmtQty(totals.positiveOutstanding) + '</div><div class="bs-total-sub">Excludes over-return rows</div></div>' +
      '<div class="bs-total-box"><div class="bs-total-label">Highest Outstanding</div><div class="bs-total-value">' + fmtQty(highestQty) + '</div><div class="bs-total-sub">' + esc(highestName) + '</div></div>' +
      (issueRows.length
        ? '<div class="bs-total-box issue"><div class="bs-total-label">Data Issues</div><div class="bs-total-value">' + fmtQty(issueRows.length) + '</div><div class="bs-total-sub">Over-return total: ' + fmtQty(totals.overReturn) + '</div></div>'
        : '');
  }

  function renderSummary(rows){
    const sortedRows = rows.slice().sort(compareSummaryByLastActivity);
    wrap._lastRows = sortedRows;
    renderTotals(rows);
    const table = $('ac_bs_rows_table');

    if (!sortedRows.length) {
      table.innerHTML = '<tr><td colspan="8" class="bs-empty-cell">No basket records found.</td></tr>';
      return;
    }

    table.innerHTML = sortedRows.map((r, i) => {
      const code = r.debtorCode || '';
      const name = r.debtorName || '';
      const send = Number(r.sendQty || 0);
      const ret  = Number(r.returnQty || 0);
      const out  = Number(r.outstandingQty || 0);
      const lastDate = r.lastTxnDate || '-';
      const cls = chipClass(out);
      const outTitle = outstandingMeaning(out);

      return `<tr data-summary-row="1" data-debtor-code="${esc(code)}">
        <td>${i+1}</td>
        <td>${esc(code)}</td>
        <td>${esc(name)}</td>
        <td>${fmtQty(send)}</td>
        <td>${fmtQty(ret)}</td>
        <td><span class="bs-chip ${cls}" title="${esc(outTitle)}" aria-label="${esc(outTitle)}">${fmtQty(out)}</span></td>
        <td>${esc(lastDate)}</td>
        <td><button class="bs-view-btn" type="button" data-debtor-code="${esc(code)}" data-debtor-name="${esc(name)}">View</button></td>
      </tr>`;
    }).join('');
  }

  function setSummaryLoading(isLoading, message=''){
    const table = $('ac_bs_rows_table');
    const status = $('ac_bs_status');
    if (status) {
      status.style.display = message ? 'block' : 'none';
      status.textContent = message || '';
    }
    if (isLoading && table) {
      table.innerHTML = '<tr><td colspan="8" class="bs-empty-cell">Loading basket summary...</td></tr>';
    }
  }

  function getRowTxnType(row){
    return String(row?.txnType || row?.txn_type || '').toUpperCase();
  }

  function getRowProofImage(row){
    if (!row) return '';
    const possible = [
      row.proofImage, row.proof_image, row.proofImageUrl, row.proof_image_url,
      row.imageUrl, row.image_url, row.proofUrl, row.proof_url,
      row.returnProofImage, row.return_proof_image, row.basketProofImage,
      row.basket_proof_image, row.attachmentUrl, row.attachment_url
    ];

    for (const v of possible) {
      if (v && String(v).trim() !== '') return String(v).trim();
    }

    if (Array.isArray(row.proofImages) && row.proofImages.length) {
      const first = row.proofImages[0];
      return first.imageUrl || first.image_url || first.url || '';
    }

    if (Array.isArray(row.images) && row.images.length) {
      const first = row.images[0];
      return first.imageUrl || first.image_url || first.url || '';
    }

    return '';
  }

  async function fetchBasketProofImage(row){
    const existing = getRowProofImage(row);
    if (existing) return existing;

    const nonce = wrap.dataset.basketProofNonce || '';
    if (!AJAX_URL || !nonce || !row) return '';

    const fd = new FormData();
    fd.append('action', 'ac_bs_get_basket_proof');
    fd.append('nonce', nonce);
    fd.append('ledgerId', row.id || row.ledgerId || row.ledger_id || row.basketLedgerId || row.basket_ledger_id || row.returnLedgerId || row.return_ledger_id || '');
    fd.append('sourceRef', row.sourceRef || row.source_ref || row.refNo || row.ref_no || row.docNo || row.doc_no || '');
    fd.append('debtorCode', row.debtorCode || row.debtor_code || currentLedgerCustomer.code || '');
    fd.append('debtorName', row.debtorName || row.debtor_name || currentLedgerCustomer.name || '');
    fd.append('txnDate', row.txnDate || row.txn_date || row.date || '');

    try {
      const res = await fetch(AJAX_URL, { method:'POST', credentials:'same-origin', body:fd, cache:'no-store' });
      const data = await res.json();
      if (data && data.success && data.data && data.data.imageUrl) return data.data.imageUrl;
    } catch(e) {
      console.error('Basket proof AJAX failed:', e);
    }

    return '';
  }

  function receiptRef(row){
    return row?.sourceRef || row?.source_ref || row?.refNo || row?.ref_no || row?.docNo || row?.doc_no || row?.id || 'basket-return';
  }

  function receiptFileName(receipt){
    const ref = String(receipt?.ref || receipt?.id || 'basket-return').replace(/[^A-Za-z0-9_-]/g, '-');
    return `Basket-Return-${ref}.pdf`;
  }

  function getRowDriverName(row){
    const possible = [
      row?.driverLogin, row?.driver_login,
      row?.assignedDriverLogin, row?.assigned_driver_login,
      row?.driverName, row?.driver_name,
      row?.createdByLogin, row?.created_by_login,
      row?.createdByUserLogin, row?.created_by_user_login,
      row?.userLogin, row?.user_login,
      row?.createdByName, row?.created_by_name,
      row?.userName, row?.user_name,
      row?.vehiclePlate, row?.vehicle_plate
    ];

    for (const value of possible) {
      const s = String(value || '').trim();
      if (s) return s.toUpperCase();
    }

    return '';
  }

  function basketReceiptCardHtml(receipt){
    const proofHtml = receipt.proofImage
      ? `Image Proof<img src="${esc(receipt.proofImage)}" alt="Basket return proof" decoding="sync">`
      : 'No image proof uploaded';

    return `
      <div class="bs-br-card">
        <div class="bs-br-paper">
          <div class="bs-br-head">
            <div><img class="bs-br-logo" src="${esc(RECEIPT_LOGO_URL)}" alt="Company logo"></div>
            <div>
              <div class="bs-br-title">BASKET RETURN</div>
              <div class="bs-br-no">${esc(receipt.ref)}</div>
            </div>
          </div>
          <div class="bs-br-info">
            <div class="bs-br-field"><span>Customer</span><strong>${esc(receipt.customerName || 'Customer')}</strong></div>
            <div class="bs-br-field"><span>Driver</span><strong>${esc(receipt.driverName || '')}</strong></div>
          </div>
          <div class="bs-br-qty">${esc(receipt.qty)} BASKETS</div>
          <div class="bs-br-proof">${proofHtml}</div>
        </div>
      </div>`;
  }

  function basketReceiptHtml(row, proofImage){
    const receipt = {
      ref: receiptRef(row),
      customerName: row?.debtorName || row?.debtor_name || currentLedgerCustomer.name || currentLedgerCustomer.code || 'Customer',
      driverName: getRowDriverName(row),
      qty: fmtQty(row?.qty || 0),
      proofImage: proofImage || ''
    };

    return `
      <div class="bs-br-overlay" id="acBsBasketReceiptOverlay">
        <div class="bs-br-modal" role="dialog" aria-modal="true" aria-label="Basket Return Receipt">
          <div class="bs-br-actions">
            <button type="button" class="bs-br-print" data-print-current-basket-receipt="1">Print / Save PDF</button>
            <button type="button" class="bs-br-share" data-share-current-basket-receipt="1">Share PDF</button>
            <button type="button" class="bs-br-close" onclick="document.getElementById('ac_bs_receipt_mount').innerHTML='';document.body.classList.remove('bs-br-open')">Close</button>
          </div>
          ${basketReceiptCardHtml(receipt)}
        </div>
      </div>`;
  }

  function basketReceiptLoadingHtml(){
    return `
      <div class="bs-br-overlay" id="acBsBasketReceiptOverlay">
        <div class="bs-br-modal" role="dialog" aria-modal="true" aria-label="Loading Basket Return Receipt">
          <div class="bs-br-actions">
            <button type="button" class="bs-br-close" onclick="document.getElementById('ac_bs_receipt_mount').innerHTML='';document.body.classList.remove('bs-br-open')">Close</button>
          </div>
          <div class="bs-br-card"><div class="bs-br-paper"><div class="bs-br-loading">Loading basket receipt...</div></div></div>
        </div>
      </div>`;
  }

  async function openBasketReceiptByIndex(idx){
    const row = currentLedgerRows[Number(idx)];
    if (!row || getRowTxnType(row) !== 'RETURN') {
      showError('Receipt not available', 'Basket receipt is only available for returned basket movement.');
      return;
    }

    const mount = $('ac_bs_receipt_mount');
    if (!mount) return;

    document.body.classList.add('bs-br-open');
    mount.innerHTML = basketReceiptLoadingHtml();
    const proofImage = await fetchBasketProofImage(row);
    currentReceiptForShare = {
      id: row.id || row.ledgerId || row.ledger_id || idx,
      ref: receiptRef(row),
      customerName: row?.debtorName || row?.debtor_name || currentLedgerCustomer.name || currentLedgerCustomer.code || 'Customer',
      driverName: getRowDriverName(row),
      qty: fmtQty(row?.qty || 0),
      proofUrl: proofImage || ''
    };
    mount.innerHTML = basketReceiptHtml(row, proofImage);
  }

  function loadJsPdf(){
    if (window.jspdf && window.jspdf.jsPDF) return Promise.resolve(window.jspdf.jsPDF);
    if (brJsPdfPromise) return brJsPdfPromise;

    brJsPdfPromise = new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
      script.onload = () => window.jspdf && window.jspdf.jsPDF ? resolve(window.jspdf.jsPDF) : reject(new Error('PDF library did not load.'));
      script.onerror = () => rejec+��g��
�^��������	OB
??�e-picker-body">
                <input type="text" id="acd_resp_do_picker_search" class="acd-resp-input acd-resp-picker-search" placeholder="Type to search..." autocomplete="off">
                <div class="acd-resp-picker-results" id="acd_resp_do_picker_results"></div>
            </div>
        </div>
    </div>
</div>

<style>
/* ----------------------------------------------
   RESPONSIVE STYLES (full original button effects + mobile chips)
   With stronger CSS overrides to beat theme styles
---------------------------------------------- */
#acd-resp-root {
    --acd-bg: #f8fafc;
    --acd-card-bg: #ffffff;
    --acd-border: #dbe4ee;
    --acd-border-strong: #c4d0dd;
    --acd-text: #0f172a;
    --acd-muted: #475569;
    --acd-green: #166534;
    --acd-green-light: #dcfce7;
    --acd-green-soft: #f0fdf4;
    --acd-green-dark: #14532d;
    --acd-danger: #dc2626;
    --acd-radius: 0.75rem;
    --acd-shadow: 0 0.75rem 1.75rem rgba(15, 23, 42, 0.08);
    font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
    color: var(--acd-text);
    background: var(--acd-bg);
    font-size: 1rem;
    margin: 0;
    padding: 0;
    max-width: none;
}

#acd-resp-root * {
    box-sizing: border-box;
}

/* Sticky tab bar */
#acd-resp-root .acd-resp-tab-bar {
    position: sticky;
    top: 0;
    z-index: 80;
    background: #ffffff !important;
    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
}

/* Remove original two-column grid because customer moved into Add Item card */
#acd-resp-root .acd-resp-do-grid {
    display: block;
}

@media (min-width: 1024px) {
    #acd-resp-root .acd-resp-do-grid {
        display: block;
    }
}

/* Tab Bar - mobile always grid, desktop flex with underline */
.acd-resp-tab-bar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.45rem;
    background: #fff;
    border: 1px solid var(--acd-border);
    border-radius: 0.9rem;
    padding: 0.45rem;
    margin: 0 0 0.8rem;
}
@media (min-width: 1024px) {
    .acd-resp-tab-bar {
        display: grid;
        grid-template-columns: max-content max-content;
        justify-content: start;
        background: #fff;
        border: 1px solid var(--acd-border);
        border-radius: 0.9rem;
        padding: 0.45rem;
        margin-bottom: 0.8rem;
    }
}
.acd-resp-tab-btn {
    min-width: 0;
    min-height: 3rem;
    padding: 0.65rem 0.75rem;
    font-size: 0.92rem;
    font-weight: 700;
    background: #f8fafc;
    border: 1px solid var(--acd-border);
    border-radius: 0.7rem;
    color: var(--acd-muted);
    cursor: pointer;
    transition: all 0.18s ease;
    text-align: center;
    white-space: normal;
}
.acd-resp-tab-btn:hover {
    background: var(--acd-green-soft);
    color: var(--acd-green);
}
.acd-resp-tab-btn.active {
    background: var(--acd-green-soft);
    border-color: #86efac;
    color: var(--acd-green);
}
@media (min-width: 1024px) {
    .acd-resp-tab-btn:hover {
        background: var(--acd-green-soft);
    }
}

/* Tab Panes */
.acd-resp-tab-pane {
    display: none;
    padding: 0;
}
.acd-resp-tab-pane.active {
    display: block;
}

/* Cards */
.acd-resp-card {
    background: var(--acd-card-bg);
    border: 1px solid var(--acd-border);
    border-radius: var(--acd-radius);
    box-shadow: var(--acd-shadow);
    overflow: hidden;
}
.acd-resp-items-card {
    margin-top: 0.9rem;
}
.acd-resp-stack {
    max-width: 42rem;
}
#acd-resp-basket-tab .acd-resp-stack {
    width: 100%;
    max-width: 64rem;
    margin-left: auto;
    margin-right: auto;
}
.acd-resp-basket-card {
    width: 100%;
}
.acd-resp-card-header {
    padding: 0.85rem 0.9rem;
    border-bottom: 1px solid var(--acd-border);
    background: #fcfdff;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
}
.acd-resp-card-header-stack {
    flex-direction: column;
    align-items: stretch;
}
.acd-resp-card-header h3 {
    margin: 0;
    font-size: 1rem;
    font-weight: 800;
}
.acd-resp-lines-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
}
.acd-resp-lines-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.8rem;
    min-height: 1.8rem;
    padding: 0 0.45rem;
    border-radius: 999px;
    background: var(--acd-green-soft);
    border: 1px solid #bbf7d0;
    color: var(--acd-green);
    font-size: 0.82rem;
    font-weight: 800;
}
.acd-resp-card-body {
    padding: 0.9rem;
}

@media (min-width: 768px) {
    #acd-resp-root .acd-resp-card-body {
        padding: 1rem;
    }

    #acd-resp-root .acd-resp-entry-card .acd-resp-card-body {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 0.9rem 1rem;
        align-items: end;
    }

    #acd-resp-root .acd-resp-entry-card .acd-resp-field,
    #acd-resp-root .acd-resp-entry-card .acd-resp-row-2,
    #acd-resp-root .acd-resp-entry-card .acd-resp-preview,
    #acd-resp-root .acd-resp-entry-card #acd_resp_do_addline {
        margin-bottom: 0;
    }

    #acd-resp-root .acd-resp-entry-card .acd-resp-row-2,
    #acd-resp-root .acd-resp-entry-card .acd-resp-preview,
    #acd-resp-root .acd-resp-entry-card #acd_resp_do_addline {
        grid-column: 1 / -1;
    }
}

/* Fields */
.acd-resp-field {
    margin-bottom: 0.85rem;
}
.acd-resp-field label {
    display: block;
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--acd-muted);
    margin-bottom: 0.35rem;
}
.acd-resp-label-note {
    color: #64748b;
    font-size: 0.78rem;
    font-weight: 700;
}
.acd-resp-input {
    width: 100%;
    min-height: 3rem;
    padding: 0.72rem 0.85rem;
    border: 1px solid var(--acd-border-strong);
    border-radius: 0.65rem;
    background: #fff;
    font-size: 1rem;
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.acd-resp-file-input {
    width: 100%;
    min-height: 3rem;
    padding: 0.66rem 0.75rem;
    border: 1px dashed var(--acd-border-strong);
    border-radius: 0.65rem;
    background: #fff;
    font-size: 0.95rem;
}
.acd-resp-file-input:focus {
    outline: none;
    border-color: var(--acd-green);
    box-shadow: 0 0 0 0.2rem rgba(22, 101, 52, 0.10);
}
.acd-resp-input:focus {
    outline: none;
    border-color: var(--acd-green);
    box-shadow: 0 0 0 0.2rem rgba(22, 101, 52, 0.10);
}
.acd-resp-search-wrap {
    position: relative;
}
.acd-resp-search-wrap .acd-resp-input {
    padding-right: 3.1rem;
    cursor: pointer;
}
.acd-resp-field-clear {
    position: absolute;
    top: 50%;
    right: 0.5rem;
    transform: translateY(-50%);
    width: 2.15rem;
    height: 2.15rem;
    border: 1px solid var(--acd-border);
    background: #fff;
    color: #64748b;
    border-radius: 0.5rem;
    display: none;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    cursor: pointer;
    transition: all 0.18s ease;
}
.acd-resp-field-clear.show {
    display: inline-flex;
}
.acd-resp-field-clear:hover {
    background: var(--acd-green-soft);
    border-color: #bbf7d0;
    color: var(--acd-green);
}

/* Type Toggle Buttons */
.acd-resp-type-toggle {
    display: flex;
    gap: 0.55rem;
}
.acd-resp-type-btn {
    flex: 1;
    min-height: 3rem;
    padding: 0.7rem 0.8rem;
    border: 1px solid var(--acd-border-strong);
    background: #f8fafc;
    color: #334155;
    border-radius: 0.65rem;
    font-weight: 700;
    font-size: 1rem;
    cursor: pointer;
    transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}
.acd-resp-type-btn:hover {
    background: #ecfdf3;
    border-color: #86efac;
    color: var(--acd-green);
}
.acd-resp-type-btn.active {
    background: var(--acd-green-light);
    border-color: #16a34a;
    color: var(--acd-green);
    box-shadow: 0 0 0 1px rgba(22, 101, 52, 0.05) inset;
}
.acd-resp-row-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.8rem;
    margin-bottom: 0.5rem;
}
@media (max-width: 480px) {
    .acd-resp-row-2 {
        grid-template-columns: 1fr;
        gap: 0;
    }
}
.acd-resp-preview {
    background: var(--acd-green-soft);
    border: 1px solid #bbf7d0;
    border-radius: 0.65rem;
    padding: 0.7rem 0.8rem;
    margin: 0.6rem 0;
    font-size: 0.95rem;
}

/* ========== PRIMARY BUTTONS – STRONG OVERRIDES ========== */
#acd-resp-root .acd-resp-btn-primary,
#acd-resp-root button.acd-resp-btn-primary {
    width: 100%;
    min-height: 3.05rem;
    padding: 0.78rem 1rem;
    border: 1px solid var(--acd-green);
    border-radius: 0.7rem;
    background: var(--acd-green);
    color: #ffffff;
    font-weight: 800;
    font-size: 1rem;
    line-height: 1.2;
    font-family: inherit;
    text-align: center;
    cursor: pointer;
    transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
    appearance: none;
    -webkit-appearance: none;
    box-shadow: none;
}

#acd-resp-root .acd-resp-btn-primary:hover,
#acd-resp-root button.acd-resp-btn-primary:hover {
    background: var(--acd-green-dark);
    border-color: var(--acd-green-dark);
    color: #ffffff;
    box-shadow: 0 4px 12px rgba(22, 101, 52, 0.14);
}

#acd-resp-root .acd-resp-btn-primary:focus,
#acd-resp-root button.acd-resp-btn-primary:focus {
    outline: none;
    box-shadow: 0 0 0 0.2rem rgba(22, 101, 52, 0.12);
}

#acd-resp-root .acd-resp-btn-primary:disabled,
#acd-resp-root button.acd-resp-btn-primary:disabled {
    background: #94a3b8;
    border-color: #94a3b8;
    color: #ffffff;
    cursor: not-allowed;
    opacity: 1;
    box-shadow: none;
}

/* Save button inside items card */
#acd-resp-root .acd-resp-save-btn {
    width: 100%;
}

/* Item details table */
.acd-resp-lines-header {
    display: grid;
    grid-template-columns: minmax(12rem, 2fr) 1fr 0.8fr 0.8fr 0.55fr 0.55fr 0.75fr 0.85fr 0.9fr 3rem;
    gap: 0.45rem;
    align-items: center;
    text-align: center;
    background: #f1f5f9;
    border: 1px solid var(--acd-border);
    border-radius: 0.65rem 0.65rem 0 0;
    padding: 0.72rem 0.8rem;
    font-size: 0.85rem;
    font-weight: 800;
    margin-bottom: 0.25rem;
}
.acd-resp-lines-header span:first-child {
    text-align: left;
}

.acd-resp-line {
    display: grid;
    grid-template-columns: minmax(12rem, 2fr) 1fr 0.8fr 0.8fr 0.55fr 0.55fr 0.75fr 0.85fr 0.9fr 3rem;
    gap: 0.45rem;
    align-items: center;
    text-align: center;
    padding: 0.7rem 0.8rem;
    border-right: 1px solid #eef2f6;
    border-left: 1px solid #eef2f6;
    border-bottom: 1px solid #eef2f6;
    font-size: 0.95rem;
}
.acd-resp-line > div:first-child {
    text-align: left;
}

.acd-resp-price-input {
    width: 100%;
    min-height: 2.35rem;
    padding: 0.45rem 0.55rem;
    border: 1px solid var(--acd-border-strong);
    border-radius: 0.55rem;
    background: #fff;
    color: var(--acd-text);
    font: inherit;
    font-weight: 700;
    text-align: center;
}
.acd-resp-price-input:focus {
    outline: none;
    border-color: var(--acd-green);
    box-shadow: 0 0 0 0.18rem rgba(22, 101, 52, 0.10);
}
.acd-resp-money-cell,
.acd-resp-number-cell {
    font-variant-numeric: tabular-nums;
}
.acd-resp-money-cell,
.acd-resp-price-cell,
.acd-resp-number-cell {
    text-align: center;
}
.acd-resp-type-pill {
    display: inline-flex;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    background: var(--acd-green-soft);
    border: 1px solid #bbf7d0;
    color: var(--acd-green);
    font-size: 0.8rem;
    font-weight: 800;
}

/* ========== DESKTOP DELETE BUTTON STYLES ========== */
#acd-resp-root .acd-resp-delete-btn,
#acd-resp-root button.acd-resp-delete-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.4rem;
    min-width: 2.4rem;
    min-height: 2.35rem;
    padding: 0.45rem;
    border: 1px solid #fecaca;
    background: #fff5f5;
    color: #dc2626;
    border-radius: 0.65rem;
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.2;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
    appearance: none;
    -webkit-appearance: none;
    box-shadow: none;
}

#acd-resp-root .acd-resp-delete-btn:hover,
#acd-resp-root button.acd-resp-delete-btn:hover {
    background: #fee2e2;
    border-color: #fca5a5;
    color: #b91c1c;
}

#acd-resp-root .acd-resp-delete-btn:focus,
#acd-resp-root button.acd-resp-delete-btn:focus {
    outline: none;
    box-shadow: 0 0 0 0.2rem rgba(220, 38, 38, 0.12);
}

.acd-resp-lines-container {
    max-height: min(32rem, 64vh);
    overflow: auto;
    padding: 0.15rem;
}
.acd-resp-lines-header,
.acd-resp-line {
    min-width: 88rem;
}
.acd-resp-empty {
    padding: 1.2rem;
    text-align: center;
    color: var(--acd-muted);
    font-style: italic;
}
.acd-resp-status {
    margin-top: 0.8rem;
    font-size: 0.9rem;
    text-align: center;
}

/* ========== SUCCESS ACTIONS PANEL ========== */
#acd-resp-root .acd-resp-success-actions {
    margin-top: 0.75rem;
    padding: 0.85rem;
    border: 1px solid #bbf7d0;
    background: var(--acd-green-soft);
    border-radius: 0.75rem;
}

#acd-resp-root .acd-resp-success-text {
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--acd-green-dark);
    margin-bottom: 0.55rem;
}

#acd-resp-root .acd-resp-success-btns {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.5rem;
}

@media (min-width: 768px) {
    #acd-resp-root .acd-resp-success-btns {
        grid-template-columns: repeat(2, 1fr);
    }
}

#acd-resp-root .acd-resp-action-btn,
#acd-resp-root a.acd-resp-action-btn,
#acd-resp-root button.acd-resp-action-btn {
    min-height: 2.8rem;
    padding: 0.7rem 0.8rem;
    border-radius: 0.65rem;
    font-size: 0.92rem;
    font-weight: 800;
    line-height: 1.2;
    font-family: inherit;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    appearance: none;
    -webkit-appearance: none;
    box-shadow: none;
    transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}

#acd-resp-root .acd-resp-action-green {
    background: var(--acd-green);
    border: 1px solid var(--acd-green);
    color: #ffffff;
}

#acd-resp-root .acd-resp-action-green:hover {
    background: var(--acd-green-dark);
    border-color: var(--acd-green-dark);
    color: #ffffff;
}

#acd-resp-root .acd-resp-action-soft {
    background: #ffffff;
    border: 1px solid #86efac;
    color: var(--acd-green);
}

#acd-resp-root .acd-resp-action-soft:hover {
    background: #dcfce7;
    border-color: #22c55e;
    color: var(--acd-green-dark);
}

#acd-resp-root .acd-resp-action-danger {
    background: #fff5f5;
    border: 1px solid #fecaca;
    color: var(--acd-danger);
}

#acd-resp-root .acd-resp-action-danger:hover {
    background: #fee2e2;
    border-color: #fca5a5;
    color: #b91c1c;
}


/* Picker Modal - Base styles (centered) */
.acd-resp-picker-modal {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 0.75rem;
}
.acd-resp-picker-modal.active {
    display: flex;
}
.acd-resp-picker-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
}
.acd-resp-picker-sheet {
    position: relative;
    width: 100%;
    max-width: 42rem;
    background: #fff;
    border-radius: 0.9rem;
    box-shadow: 0 1.4rem 2.4rem rgba(0, 0, 0, 0.18);
    overflow: hidden;
}
.acd-resp-picker-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.85rem 0.95rem;
    border-bottom: 1	OB�}�_��������)��*
??�r> {
            const test = line ? `${line} ${word}` : word;
            if (ctx.measureText(test).width > maxWidth && line !== '') {
                lines.push(line);
                line = word;
            } else {
                line = test;
            }
        });

        if (line) lines.push(line);
        if (maxLines && lines.length > maxLines) {
            lines = lines.slice(0, maxLines);
            while (lines[lines.length - 1] && ctx.measureText(lines[lines.length - 1] + '...').width > maxWidth) {
                lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
            }
            lines[lines.length - 1] += '...';
        }

        lines.forEach((value, idx) => ctx.fillText(value, x, y + (idx * lineHeight)));
    }

    function doCanvasCheckbox(ctx, x, y, checked) {
        doCanvasRect(ctx, x, y, 14, 14, '#222222', 1.5);
        if (checked) {
            doCanvasText(ctx, '\u2713', x + 1, y + 13, 21, '#111111', '700');
        }
    }

    function deliveryPdfData(job) {
        const lines = Array.isArray(job?.summary?.lines) ? job.summary.lines : [];
        return {
            companyName: doCompanyName,
            companyAddr: doCompanyAddr,
            companyTel: doCompanyTel,
            docNo: job?.docNo || '',
            customerCode: job?.customerCode || '',
            customerName: job?.customerName || 'Customer',
            displayDate: job?.displayDate || '',
            remark: '',
            lines: lines.map(line => {
                const packType = String(line?.packType || '').toUpperCase();
                const isCtn = packType === 'CARTON' || packType === 'CTN' || whole(line?.carton) > 0;
                const isBsk = packType === 'BASKET' || packType === 'BSK' || (!isCtn && whole(line?.basket) > 0);
                return {
                    qty: fmt(isCtn ? line?.carton : (isBsk ? line?.basket : line?.qty)),
                    kg: fmt(line?.unitKg || line?.kg),
                    description: line?.itemName || line?.itemCode || '',
                    isCtn,
                    isBsk,
                    totalKg: fmt(line?.totalKg || line?.kg)
                };
            }),
            totalCtn: fmt(job?.summary?.cartons),
            totalBsk: fmt(job?.summary?.baskets),
            totalKg: fmt(job?.summary?.kg)
        };
    }

    function makeDeliveryOrderCanvas(job, logoImage=null) {
        const data = deliveryPdfData(job);
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        const lines = Array.isArray(data.lines) ? data.lines : [];
        const pageX = 106;
        const pageY = 58;
        const pageW = 1028;
        const pageH = 1638;
        const tableX = 160;
        const tableY = 360;
        const rowH = 47;
        const col = [tableX, tableX + 105, tableX + 210, tableX + 680, tableX + 830, tableX + 930];
        const minRows = Math.max(14, lines.length);

        canvas.width = 1240;
        canvas.height = 1754;
        ctx.imageSmoothingEnabled = true;
        ctx.imageSmoothingQuality = 'high';

        doCanvasFillRect(ctx, 0, 0, canvas.width, canvas.height, '#ffffff');
        doCanvasFillRect(ctx, pageX, pageY, pageW, pageH, '#ffffff');
        doCanvasRect(ctx, pageX, pageY, pageW, pageH, '#d1d5db', 1);

        if (!logoImage) {
            doCanvasText(ctx, 'Vege', pageX + 86, pageY + 154, 44, '#164f38', '700');
        } else {
            drawCanvasImageContained(ctx, logoImage, pageX + 44, pageY + 74, 180, 118);
        }
        doCanvasText(ctx, data.companyName, pageX + 240, pageY + 125, 38, '#0f172a', '900');
        doCanvasText(ctx, data.companyAddr, pageX + 242, pageY + 157, 13, '#111111', '400');
        doCanvasText(ctx, 'H/P: ' + data.companyTel, pageX + 242, pageY + 181, 13, '#111111', '700');

        doCanvasFillRect(ctx, pageX + 785, pageY + 36, 230, 36, '#444444');
        doCanvasText(ctx, 'DELIVERY ORDER', pageX + 900, pageY + 62, 18, '#ffffff', '700', 'center');
        doCanvasText(ctx, 'No', pageX + 805, pageY + 190, 20, '#111111', '400');
        doCanvasText(ctx, data.docNo, pageX + 835, pageY + 190, 27, '#ef4444', '700');

        doCanvasText(ctx, 'Customer', pageX + 63, pageY + 250, 18, '#111111', '700');
        doCanvasText(ctx, data.customerName + (data.customerCode ? ' (' + data.customerCode + ')' : ''), pageX + 170, pageY + 250, 18, '#111111', '400');
        doCanvasLine(ctx, pageX + 150, pageY + 260, pageX + 650, pageY + 260, '#666666', 1);
        doCanvasText(ctx, 'Date', pageX + 770, pageY + 250, 18, '#111111', '700');
        doCanvasText(ctx, data.displayDate, pageX + 835, pageY + 250, 18, '#111111', '400');
        doCanvasLine(ctx, pageX + 830, pageY + 260, pageX + 980, pageY + 260, '#666666', 1);

        doCanvasRect(ctx, tableX, tableY, col[5] - col[0], rowH * (minRows + 1), '#333333', 1.2);
        for (let i = 1; i < col.length - 1; i++) doCanvasLine(ctx, col[i], tableY, col[i], tableY + rowH * (minRows + 1), '#333333', 1);
        for (let i = 1; i <= minRows + 1; i++) doCanvasLine(ctx, tableX, tableY + rowH * i, col[5], tableY + rowH * i, '#333333', 1);

        doCanvasText(ctx, '\u6570\u91cf', tableX + 55, tableY + 20, 15, '#111111', '700', 'center');
        doCanvasText(ctx, 'Quantity', tableX + 55, tableY + 39, 13, '#111111', '700', 'center');
        doCanvasText(ctx, '\u516c\u65a4', col[1] + 55, tableY + 20, 15, '#111111', '700', 'center');
        doCanvasText(ctx, 'Kg', col[1] + 55, tableY + 39, 13, '#111111', '700', 'center');
        doCanvasText(ctx, '\u8d27\u7269\u540d\u79f0', col[2] + 235, tableY + 20, 15, '#111111', '700', 'center');
        doCanvasText(ctx, 'Description', col[2] + 235, tableY + 39, 13, '#111111', '700', 'center');
        doCanvasText(ctx, '\u7bb1 / \u7bee', col[3] + 75, tableY + 20, 15, '#111111', '700', 'center');
        doCanvasText(ctx, 'Box / Basket', col[3] + 75, tableY + 39, 13, '#111111', '700', 'center');
        doCanvasText(ctx, '\u603b\u516c\u65a4', col[4] + 55, tableY + 20, 15, '#111111', '700', 'center');
        doCanvasText(ctx, 'Total Kg', col[4] + 55, tableY + 39, 13, '#111111', '700', 'center');

        for (let i = 0; i < minRows; i++) {
            const y = tableY + rowH * (i + 1);
            const item = lines[i] || {};
            doCanvasText(ctx, item.qty || '', tableX + 55, y + 30, 18, '#111111', '400', 'center');
            doCanvasText(ctx, item.kg || '', col[1] + 55, y + 30, 18, '#111111', '400', 'center');
            doCanvasWrap(ctx, item.description || '', col[2] + 10, y + 22, 450, 18, 18, '#111111', '400', 2);
            doCanvasCheckbox(ctx, col[3] + 25, y + 16, !!item.isCtn);
            doCanvasText(ctx, 'Ctn', col[3] + 43, y + 29, 14, '#111111', '400');
            doCanvasCheckbox(ctx, col[3] + 86, y + 16, !!item.isBsk);
            doCanvasText(ctx, 'Bsk', col[3] + 104, y + 29, 14, '#111111', '400');
            doCanvasText(ctx, item.totalKg || '', col[4] + 55, y + 30, 18, '#111111', '400', 'center');
        }

        const afterTableY = tableY + rowH * (minRows + 1) + 35;
        doCanvasText(ctx, 'We Do The EXCELLENT Way', tableX, afterTableY + 25, 23, '#111111', '700');

        const totalX = pageX + 705;
        const totalY = afterTableY;
        const totalRows = [
            ['\u603b\u7bb1', 'Total Ctn', data.totalCtn || ''],
            ['\u603b\u7bee', 'Total Bsk', data.totalBsk || ''],
            ['\u603b\u516c\u65a4', 'Total Kg', data.totalKg || '']
        ];
        totalRows.forEach((row, idx) => {
            doCanvasText(ctx, row[0], totalX, totalY + 18 + idx * 48, 18, '#111111', '700', 'right');
            doCanvasText(ctx, row[1], totalX, totalY + 38 + idx * 48, 16, '#111111', '400', 'right');
            doCanvasFillRect(ctx, totalX + 20, totalY + 4 + idx * 48, 100, 37, 'rgba(255,255,255,0.25)');
            doCanvasRect(ctx, totalX + 20, totalY + 4 + idx * 48, 100, 37, '#333333', 1);
            doCanvasText(ctx, row[2], totalX + 70, totalY + 30 + idx * 48, 18, '#111111', '700', 'center');
        });

        doCanvasLine(ctx, tableX, pageY + pageH - 140, tableX + 260, pageY + pageH - 140, '#555555', 1);
        doCanvasLine(ctx, pageX + pageW - 420, pageY + pageH - 140, pageX + pageW - 160, pageY + pageH - 140, '#555555', 1);
        doCanvasText(ctx, '\u7ecf\u624b\u4eba Issued by', tableX, pageY + pageH - 110, 17, '#111111', '400');
        doCanvasText(ctx, '\u6536\u8d27\u4eba Received by', pageX + pageW - 420, pageY + pageH - 110, 17, '#111111', '400');

        return canvas;
    }

    function makeDeliveryProofCanvas(job, proofImage=null) {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        const pageX = 106;
        const pageY = 58;
        const pageW = 1028;
        const pageH = 1638;

        canvas.width = 1240;
        canvas.height = 1754;
        doCanvasFillRect(ctx, 0, 0, canvas.width, canvas.height, '#ffffff');
        doCanvasFillRect(ctx, pageX, pageY, pageW, pageH, '#ffffff');
        doCanvasRect(ctx, pageX, pageY, pageW, pageH, '#d1d5db', 1);

        doCanvasText(ctx, 'Proof of Delivery', pageX + 58, pageY + 105, 36, '#111111', '700');
        doCanvasText(ctx, doCompanyName, pageX + 58, pageY + 142, 18, '#111111', '400');
        doCanvasText(ctx, 'DO No: ' + (job?.docNo || ''), pageX + pageW - 60, pageY + 100, 18, '#111111', '700', 'right');
        doCanvasText(ctx, 'Customer: ' + (job?.customerName || ''), pageX + pageW - 60, pageY + 132, 18, '#111111', '400', 'right');
        doCanvasText(ctx, 'Date: ' + (job?.displayDate || ''), pageX + pageW - 60, pageY + 164, 18, '#111111', '400', 'right');
        doCanvasLine(ctx, pageX + 58, pageY + 190, pageX + pageW - 58, pageY + 190, '#333333', 3);
        doCanvasRect(ctx, pageX + 58, pageY + 240, pageW - 116, 1210, '#333333', 1.5);
        if (proofImage) {
            drawCanvasImageContained(ctx, proofImage, pageX + 90, pageY + 280, pageW - 180, 1120);
        } else {
            doCanvasText(ctx, 'No proof of delivery image uploaded yet.', pageX + pageW / 2, pageY + 850, 24, '#555555', '700', 'center');
        }
        doCanvasLine(ctx, pageX + 58, pageY + pageH - 120, pageX + 410, pageY + pageH - 120, '#555555', 1);
        doCanvasLine(ctx, pageX + pageW - 410, pageY + pageH - 120, pageX + pageW - 58, pageY + pageH - 120, '#555555', 1);
        doCanvasText(ctx, 'Driver / Issued by', pageX + 58, pageY + pageH - 90, 17, '#111111', '400');
        doCanvasText(ctx, 'Customer / Received by', pageX + pageW - 410, pageY + pageH - 90, 17, '#111111', '400');

        return canvas;
    }

    function buildDeliveryOrderPdf(job) {
        return Promise.all([loadJsPdf(), loadProofImage(receiptLogoUrl), loadProofImage(job?.proofUrl || '')])
            .then(([jsPDF, logoImage, proofImage]) => {
                const pdf = new jsPDF({orientation:'portrait', unit:'mm', format:'a5'});
                pdf.addImage(makeDeliveryOrderCanvas(job, logoImage).toDataURL('image/jpeg', 0.95), 'JPEG', 0, 0, 148, 210);
                pdf.addPage();
                pdf.addImage(makeDeliveryProofCanvas(job, proofImage).toDataURL('image/jpeg', 0.95), 'JPEG', 0, 0, 148, 210);
                return pdf.output('blob');
            });
    }

    function makeReceiptCanvas(receipt, proofImage=null, logoImage=null) {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = 1240;
        canvas.height = 1754;

        ctx.fillStyle = '#ffffff';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.strokeStyle = '#111111';
        ctx.lineWidth = 2;
        ctx.strokeRect(82, 70, 1076, 1614);

        if (logoImage) {
            drawCanvasImageContained(ctx, logoImage, 138, 112, 360, 128);
        } else {
            drawText(ctx, 'BASKET RETURN', 140, 130, 30, '#111', '900');
        }
        drawText(ctx, 'BASKET RETURN', 1100, 130, 24, '#111', '900', 'right');
        drawText(ctx, receipt.sourceRef || ('BR-' + receipt.id), 1100, 172, 22, '#111', '900', 'right');

        ctx.strokeStyle = '#111111';
        ctx.lineWidth = 4;
        ctx.beginPath();
        ctx.moveTo(140, 278);
        ctx.lineTo(1100, 278);
        ctx.stroke();

        let y = 350;
        drawText(ctx, 'Customer', 160, y, 22, '#555', '700');
        drawWrappedText(ctx, receipt.customerName || 'Customer', 160, y + 34, 410, 30, 24, '#111', '800');
        drawText(ctx, 'Driver', 660, y, 22, '#555', '700');
        drawWrappedText(ctx, receipt.driverName || '', 660, y + 34, 410, 30, 24, '#111', '800');
        y += 105;
        ctx.strokeStyle = '#e5e7eb';
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(160, y);
        ctx.lineTo(1080, y);
        ctx.stroke();

        drawText(ctx, `${fmt(receipt.qty)} BASKETS`, 620, y + 54, 74, '#111', '900', 'center');
        y += 210;

        ctx.strokeStyle = '#cbd5e1';
        ctx.setLineDash([12, 10]);
        ctx.strokeRect(160, y, 920, 560);
        ctx.setLineDash([]);

        if (proofImage) {
            drawText(ctx, 'Image Proof', 620, y + 28, 22, '#334155', '800', 'center');
            drawCanvasImageContained(ctx, proofImage, 210, y + 82, 820, 420);
        } else {
            drawText(ctx, 'No image proof uploaded', 620, y + 255, 28, '#64748b', '800', 'center');
        }

        return canvas;
    }

    function buildBasketReceiptPdf(receipt) {
        return Promise.all([loadJsPdf(), loadProofImage(receipt.proofUrl), loadProofImage(receiptLogoUrl)])
            .then(([jsPDF, proofImage, logoImage]) => {
                const pdf = new jsPDF({orientation:'portrait', unit:'mm', format:'a5'});
                const canvas = makeReceiptCanvas(receipt, proofImage, logoImage);
                pdf.addImage(canvas.toDataURL('image/jpeg', 0.95), 'JPEG', 0, 0, 148, 210);
                return pdf.output('blob');
            });
    }

    function downloadBlob(blob, fileName) {
        const url = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = fileName;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        setTimeout(() => URL.revokeObjectURL(url), 1000);
    }

    function printBasketReceipt(id) {
        const receipt = basketReceipts[String(id)];
        if (!receipt) {
            toast('error', 'Receipt not found');
            return;
        }

        buildBasketReceiptPdf(receipt)
            .then(blob => {
                const fileName = receiptFileName(receipt);
                const url = URL.createObjectURL(blob);
                const opened = window.open(url, '_blank', 'noopener');

                if (!opened) {
                    downloadBlob(blob, fileName);
                    toast('info', 'PDF downloaded', 'Open the downloaded PDF to print or share.');
                }

                setTimeout(() => URL.revokeObjectURL(url), 60000);
            })
            .catch(() => {
                toast('error', 'Unable to prepare PDF', 'Please try again.');
            });
    }

    function shareBasketReceipt(id, button) {
        const receipt = basketReceipts[String(id)];
        if (!receipt) {
            toast('error', 'Receipt not found');
            return;
        }

        if (!navigator.share) {
            toast('error', 'Sharing not supported', 'Print or download the PDF, then attach it in WhatsApp.');
            return;
        }

        const originalText = button ? button.textContent : '';
        if (button) {
            button.disabled = true;
            button.textContent = 'Preparing PDF...';
        }

        buildBasketReceiptPdf(receipt)
            .then(blob => {
                const fileName = receiptFileName(receipt);
                const file = new File([blob], fileName, {type:'application/pdf'});
                if (!navigator.canS)��*u�`��������`�/
??�a <div id="bdoHomeActiveMount"></div>
                </div>
            </div>
        </div>

        <div class="bdo-panel" data-panel="create">
            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Create Delivery Order</div><div class="bdo-card-sub">Key in customer, vegetables, basket/carton and KG</div></div>
                </div>
                <div class="bdo-card-body">
                    <input type="hidden" id="bdo_do_date" value="__TODAY__">
                    <input type="hidden" id="bdo_do_customer_code" value="">
                    <input type="hidden" id="bdo_do_customer_name" value="">
                    <input type="hidden" id="bdo_do_sales_agent" value="">
                    <input type="hidden" id="bdo_do_location" value="__DEFAULT_LOCATION__">

                    <div class="bdo-field">
                        <label>Customer</label>
                        <div class="bdo-search-wrap">
                            <input type="text" id="bdoCustomerInput" class="bdo-input" placeholder="Search customer..." readonly autocomplete="off">
                            <button type="button" class="bdo-clear" id="bdoCustomerClear">×</button>
                        </div>
                    </div>

                    <div class="bdo-field">
                        <label>Item Name</label>
                        <div class="bdo-search-wrap">
                            <input type="text" id="bdoItemInput" class="bdo-input" placeholder="Search item..." readonly autocomplete="off">
                            <button type="button" class="bdo-clear" id="bdoItemClear">×</button>
                            <input type="hidden" id="bdo_item_code" value="">
                            <input type="hidden" id="bdo_item_name" value="">
                        </div>
                    </div>

                    <div class="bdo-field">
                        <label>Type</label>
                        <div class="bdo-type-toggle">
                            <button type="button" class="bdo-type-btn active" data-pack-type="BASKET">Basket</button>
                            <button type="button" class="bdo-type-btn" data-pack-type="CARTON">Carton</button>
                        </div>
                        <input type="hidden" id="bdo_pack_type" value="BASKET">
                    </div>

                    <div class="bdo-two">
                        <div class="bdo-field"><label>Qty</label><input type="number" id="bdo_qty" class="bdo-input" min="0" step="1" placeholder="Qty"></div>
                        <div class="bdo-field"><label>Weight / Unit (KG)</label><input type="number" id="bdo_kg" class="bdo-input" min="0" step="1" placeholder="KG"></div>
                    </div>

                    <div class="bdo-preview" id="bdoLinePreview"></div>
                    <button type="button" class="bdo-btn-soft" id="bdoAddLineBtn">Add Item</button>
                </div>
            </div>

            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Items Detail</div><div class="bdo-card-sub"><span id="bdoLinesCount">0</span> item line(s)</div></div>
                </div>
                <div class="bdo-card-body">
                    <div id="bdoLines" class="bdo-lines"><div class="bdo-empty">No items added</div></div>
                    <div style="height:12px"></div>
                    <button type="button" class="bdo-btn" id="bdoSubmitDoBtn">Save Delivery Order</button>
                    <div id="bdoDoSuccess" style="display:none;margin-top:12px;">
                        <div class="bdo-alert ok" id="bdoDoSuccessText"></div>
                        <div class="bdo-two">
                            <button type="button" class="bdo-btn-soft" id="bdoContinueDeliveryBtn">View Delivery List</button>
                            <button type="button" class="bdo-btn-danger" id="bdoClearDoBtn">Clear / New DO</button>
                        </div>
                        <div style="height:9px"></div>
                        <a href="#" target="_blank" rel="noopener" class="bdo-btn-soft" id="bdoOpenDoReceiptBtn" style="display:none;">Open DO Receipt</a>
                    </div>
                </div>
            </div>
        </div>

        <div class="bdo-panel" data-panel="deliveries">
            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Delivery List</div><div class="bdo-card-sub">Choose which customer to deliver first</div></div>
                </div>
                <div class="bdo-card-body">
                    <div id="bdoDeliveryListMount" class="bdo-delivery-list"></div>
                </div>
            </div>
        </div>

        <div class="bdo-panel" data-panel="active">
            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Delivery Proof</div><div class="bdo-card-sub">Capture POD before marking delivered</div></div>
                </div>
                <div class="bdo-card-body">
                    <div id="bdoActiveMount"></div>
                </div>
            </div>
        </div>

        <div class="bdo-panel" data-panel="return">
            <div class="bdo-card">
                <div class="bdo-card-head">
                    <div><div class="bdo-card-title">Basket Return</div><div class="bdo-card-sub">Only tracks basket, not carton</div></div>
                </div>
                <form method="post" enctype="multipart/form-data" class="bdo-card-body" id="bdoBasketReturnForm">
                    <input type="hidden" name="bdo_driver_action" value="basket_return">
                    <input type="hidden" name="bdo_driver_nonce" value="__FORM_NONCE__">
                    <input type="hidden" id="bdo_br_debtor_code" name="br_debtor_code" value="">
                    <input type="hidden" id="bdo_br_debtor_name" name="br_debtor_name" value="">

                    <div class="bdo-field">
                        <label>Customer</label>
                        <div class="bdo-search-wrap">
                            <input type="text" id="bdoBrCustomerInput" class="bdo-input" placeholder="Search customer..." readonly autocomplete="off">
                            <button type="button" class="bdo-clear" id="bdoBrCustomerClear">×</button>
                        </div>
                    </div>

                    <div class="bdo-field">
                        <label>Basket Return Qty</label>
                        <input type="number" class="bdo-input" name="br_basket_qty" id="bdoBrQty" min="1" step="1" placeholder="Basket qty" required>
                    </div>

                    <div class="bdo-proof-box">
                        <strong>Photo proof required</strong>
                        <p>Take a photo of the returned baskets before saving.</p>
                        <input type="file" class="bdo-file" name="basket_return_proof" accept="image/*" capture="environment" required>
                    </div>

                    <div style="height:12px"></div>
                    <button type="submit" class="bdo-btn">Save Basket Return</button>
                </form>
            </div>
        </div>
    </div>

    <div class="bdo-bottom-nav">
        <button type="button" class="bdo-nav active" data-open-panel="home"><i>⌂</i><span>Home</span></button>
        <button type="button" class="bdo-nav" data-open-panel="create"><i>+</i><span>Create DO</span></button>
        <button type="button" class="bdo-nav" data-open-panel="deliveries"><i>☷</i><span>Deliveries</span></button>
        <button type="button" class="bdo-nav" data-open-panel="return"><i>↩</i><span>Return</span></button>
    </div>

    <div class="bdo-picker-modal" id="bdoPickerModal" aria-hidden="true">
        <div class="bdo-picker-backdrop" id="bdoPickerBackdrop"></div>
        <div class="bdo-picker-sheet">
            <div class="bdo-picker-head">
                <div class="bdo-picker-title" id="bdoPickerTitle">Search</div>
                <button type="button" class="bdo-picker-close" id="bdoPickerClose">×</button>
            </div>
            <div class="bdo-picker-body">
                <input type="text" class="bdo-input" id="bdoPickerSearch" placeholder="Type to search..." autocomplete="off">
                <div class="bdo-picker-results" id="bdoPickerResults"></div>
            </div>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<script>
(function(){
    const root = document.getElementById('bdo-driver-app');
    if (!root || root.dataset.ready === '1') return;
    root.dataset.ready = '1';

    const cfg = {
        restNonce: root.dataset.restNonce,
        ajaxUrl: root.dataset.ajaxUrl,
        debtorNonce: root.dataset.debtorNonce,
        itemNonce: root.dataset.itemNonce,
        restJobPost: root.dataset.restJobPost,
        restJobBase: root.dataset.restJobBase,
        restReceiptToken: root.dataset.restReceiptToken,
        receiptBase: root.dataset.receiptBase,
        defaultLocation: root.dataset.defaultLocation || 'HQ',
        today: root.dataset.today
    };

    const initialStats = __STATS_JSON__;
    let activeJob = __INITIAL_ACTIVE_JSON__;
    let activeJobs = __INITIAL_ACTIVE_JOBS_JSON__;
    if (!Array.isArray(activeJobs)) activeJobs = activeJob && activeJob.id ? [activeJob] : [];
    let todayJobs = __INITIAL_TODAY_JOBS_JSON__;
    if (!Array.isArray(todayJobs)) todayJobs = [];
    let deliveredJobs = __INITIAL_DELIVERED_JOBS_JSON__;
    if (!Array.isArray(deliveredJobs)) deliveredJobs = [];
    let returnRows = __INITIAL_RETURN_ROWS_JSON__;
    if (!Array.isArray(returnRows)) returnRows = [];
    let deliveryFilter = root.dataset.initialFilter || 'active';
    let doLines = [];
    let submittingDo = false;
    let doReceiptUrl = '';
    const shared = { basketCustomerManuallyCleared:false };

    const $ = (id) => document.getElementById(id);
    const esc = (s) => String(s ?? '').replace(/[&<>'"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;',"'":'&#039;','"':'&quot;'}[c]));
    const whole = (n) => { const x = Number(n); return (!Number.isFinite(x) || x < 0) ? 0 : Math.round(x); };
    const fmt = (n) => String(whole(n));
    const toast = (icon,title,text='') => window.Swal ? Swal.fire({toast:true,position:'top-end',icon,title,text,showConfirmButton:false,timer:2300}) : alert(title + (text ? '\n' + text : ''));
    const modal = (icon,title,html='') => window.Swal ? Swal.fire({icon,title,html,confirmButtonText:'OK'}) : alert(title);

    // ============================================================
    // PDF share helper
    // ============================================================
    async function bdoShareReceiptPdf(selector, filenamePrefix){
        const paper = document.querySelector(selector);

        if (!paper) {
            modal('error', 'Receipt Error', 'Receipt not found.');
            return;
        }

        if (typeof html2pdf === 'undefined') {
            modal('error', 'PDF Error', 'PDF generator failed to load. Please refresh.');
            return;
        }

        const filename = String(filenamePrefix || 'Receipt')
            .replace(/[^a-z0-9\- ]/gi, '')
            .replace(/\s+/g, '-')
            + '-' + new Date().toISOString().slice(0,10) + '.pdf';

        try {
            toast('info', 'Preparing PDF', 'Please wait...');

            const blob = await html2pdf()
                .set({
                    margin: 0,
                    filename: filename,
                    image: { type: 'jpeg', quality: 0.98 },
                    html2canvas: {
                        scale: 2,
                        useCORS: true,
                        backgroundColor: '#ffffff'
                    },
                    jsPDF: {
                        unit: 'mm',
                        format: 'a4',
                        orientation: 'portrait'
                    }
                })
                .from(paper)
                .outputPdf('blob');

            const file = new File([blob], filename, { type: 'application/pdf' });

            if (navigator.canShare && navigator.canShare({ files: [file] })) {
                await navigator.share({
                    title: filenamePrefix,
                    text: filenamePrefix,
                    files: [file]
                });
                return;
            }

            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();

            setTimeout(function(){
                URL.revokeObjectURL(url);
                a.remove();
            }, 1000);

        } catch (err) {
            console.error(err);
            modal('error', 'PDF Error', 'Failed to create PDF.');
        }
    }

    window.bdoShareDoReceiptPdf = function(){
        bdoShareReceiptPdf('.bdo-do-paper', 'DO-Receipt');
    };

    window.bdoShareBasketReceiptPdf = function(){
        bdoShareReceiptPdf('.bdo-inline-receipt .bdo-receipt-paper', 'Basket-Receipt');
    };
    // ============================================================

    // ============================================================
    // Basket receipt print helper (iframe version)
    // ============================================================
    window.bdoPrintBasketReceipt = function() {
        const paper = document.querySelector('.bdo-inline-receipt .bdo-receipt-paper');

        if (!paper) {
            window.print();
            return;
        }

        const iframe = document.createElement('iframe');
        iframe.setAttribute('title', 'Basket Receipt Print');
        iframe.style.position = 'fixed';
        iframe.style.right = '0';
        iframe.style.bottom = '0';
        iframe.style.width = '1px';
        iframe.style.height = '1px';
        iframe.style.border = '0';
        iframe.style.opacity = '0';

        document.body.appendChild(iframe);

        const doc = iframe.contentWindow.document;

        doc.open();
        doc.write(`
            <!doctype html>
            <html>
            <head>
                <meta charset="utf-8">
                <meta name="viewport" content="width=device-width, initial-scale=1">
                <style>
                    html, body {
                        margin: 0 !important;
                        padding: 0 !important;
                        background: #fff !important;
                        width: 100% !important;
                        height: auto !important;
                        min-height: 0 !important;
                        overflow: visible !important;
                        font-family: Arial, sans-serif;
                    }

                    .bdo-receipt-paper {
                        width: 80mm !important;
                        max-width: 80mm !important;
                        margin: 0 auto !important;
                        padding: 5mm !important;
                        border: 0 !important;
                        box-shadow: none !important;
                        background: #fff !important;
                        color: #222 !important;
                        text-align: center !important;
                        font-size: 13px !important;
                        box-sizing: border-box !important;
                        page-break-after: avoid !important;
                        break-after: avoid-page !important;
                        page-break-inside: avoid !important;
                        break-inside: av`�/�߯Ra��������a B
??�boid !important;
                    }

                    .bdo-receipt-company {
                        text-align: center;
                        font-weight: 900;
                        font-size: 15px;
                        margin-bottom: 6px;
                    }

                    .bdo-receipt-title {
                        text-align: center;
                        font-weight: 900;
                        font-size: 14px;
                        border-top: 1px dashed #555;
                        border-bottom: 1px dashed #555;
                        padding: 7px 0;
                        margin-bottom: 12px;
                    }

                    .bdo-receipt-row {
                        display: block;
                        margin-bottom: 9px;
                    }

                    .bdo-receipt-row strong {
                        display: block;
                        font-size: 11px;
                        text-transform: uppercase;
                        color: #555;
                        margin-bottom: 3px;
                    }

                    .bdo-receipt-row span {
                        display: block;
                        font-weight: 800;
                    }

                    .bdo-receipt-total {
                        margin: 14px 0;
                        padding: 12px;
                        border: 1px solid #222;
                        text-align: center;
                    }

                    .bdo-receipt-total span {
                        display: block;
                        font-size: 12px;
                        font-weight: 800;
                        margin-bottom: 4px;
                    }

                    .bdo-receipt-total strong {
                        display: block;
                        font-size: 34px;
                        font-weight: 950;
                        line-height: 1;
                    }

                    .bdo-receipt-proof {
                        margin-top: 14px;
                        padding-top: 12px;
                        border-top: 1px dashed #555;
                        text-align: center;
                    }

                    .bdo-receipt-proof-title {
                        font-weight: 900;
                        font-size: 12px;
                        margin-bottom: 8px;
                    }

                    .bdo-receipt-proof-img {
                        display: block;
                        width: 100% !important;
                        height: auto !important;
                        max-height: 105mm !important;
                        object-fit: contain !important;
                        border: 1px solid #ddd;
                        background: #f8f8f8;
                        margin: 0 auto;
                    }

                    .bdo-receipt-proof-empty {
                        padding: 12px;
                        border: 1px dashed #bbb;
                        color: #666;
                        font-size: 12px;
                    }

                    .bdo-receipt-footer {
                        margin-top: 12px;
                        margin-bottom: 0;
                        text-align: center;
                        font-size: 11px;
                        color: #555;
                    }

                    @page {
                        margin: 0;
                    }
                </style>
            </head>
            <body>
                ${paper.outerHTML}
            </body>
            </html>
        `);
        doc.close();

        const printFrame = function() {
            iframe.contentWindow.focus();
            iframe.contentWindow.print();

            setTimeout(function() {
                iframe.remove();
            }, 1500);
        };

        const imgs = doc.images;
        if (!imgs.length) {
            setTimeout(printFrame, 250);
            return;
        }

        let loaded = 0;
        const done = function() {
            loaded++;
            if (loaded >= imgs.length) {
                setTimeout(printFrame, 250);
            }
        };

        Array.from(imgs).forEach(function(img) {
            if (img.complete) {
                done();
            } else {
                img.onload = done;
                img.onerror = done;
            }
        });
    };
    // ============================================================

    // ============================================================
    // ADDED: JavaScript image compressor for delivery & basket return
    // ============================================================
    async function bdoCompressImageInput(input) {
        const file = input?.files?.[0];

        if (!file || !file.type.startsWith('image/')) {
            return;
        }

        // Already small enough.
        if (file.size <= 250 * 1024) {
            return;
        }

        const loadImage = (file) => new Promise((resolve, reject) => {
            const url = URL.createObjectURL(file);
            const img = new Image();

            img.onload = () => {
                URL.revokeObjectURL(url);
                resolve(img);
            };

            img.onerror = () => {
                URL.revokeObjectURL(url);
                reject(new Error('Image load failed'));
            };

            img.src = url;
        });

        try {
            input.dataset.compressing = '1';

            const img = await loadImage(file);

            let width = img.naturalWidth || img.width;
            let height = img.naturalHeight || img.height;

            if (!width || !height) {
                return;
            }

            const maxSide = 1000;
            const ratio = Math.min(maxSide / width, maxSide / height, 1);

            const targetWidth = Math.round(width * ratio);
            const targetHeight = Math.round(height * ratio);

            const canvas = document.createElement('canvas');
            canvas.width = targetWidth;
            canvas.height = targetHeight;

            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0, targetWidth, targetHeight);

            const blob = await new Promise(resolve => {
                canvas.toBlob(resolve, 'image/jpeg', 0.62);
            });

            if (!blob || blob.size >= file.size) {
                return;
            }

            const newName = file.name.replace(/\.[^.]+$/, '') + '-compressed.jpg';

            const compressedFile = new File([blob], newName, {
                type: 'image/jpeg',
                lastModified: Date.now()
            });

            const dt = new DataTransfer();
            dt.items.add(compressedFile);
            input.files = dt.files;

            toast('success', 'Image optimized', 'Photo size reduced before upload.');
        } catch (err) {
            console.warn('Image compression skipped:', err);
        } finally {
            input.dataset.compressing = '0';
        }
    }

    document.addEventListener('change', function(e) {
        const input = e.target;

        if (
            input &&
            input.type === 'file' &&
            ['delivery_proof', 'basket_return_proof'].includes(input.name)
        ) {
            input._bdoCompressPromise = bdoCompressImageInput(input);
        }
    }, true);

    document.addEventListener('submit', async function(e) {
        const form = e.target;

        if (!form || form.dataset.bdoResubmitting === '1') {
            return;
        }

        const fileInput = form.querySelector?.('input[type="file"]');

        if (
            fileInput &&
            fileInput._bdoCompressPromise &&
            fileInput.dataset.compressing === '1'
        ) {
            e.preventDefault();

            toast('info', 'Preparing image', 'Uploading after image is optimized.');

            try {
                await fileInput._bdoCompressPromise;
            } catch (err) {
                console.warn('Compression wait failed:', err);
            }

            form.dataset.bdoResubmitting = '1';
            form.requestSubmit();
        }
    }, true);
    // ============================================================

    function setStats(s){
        $('bdoStatCreated').textContent = s.created_today || 0;
        $('bdoStatActive').textContent = s.active || 0;
        $('bdoStatDelivered').textContent = s.delivered || 0;
        $('bdoStatReturn').textContent = s.return_baskets || 0;
    }

    function openPanel(name){
        document.querySelectorAll('#bdo-driver-app .bdo-panel').forEach(p => p.classList.toggle('active', p.dataset.panel === name));
        document.querySelectorAll('#bdo-driver-app .bdo-nav').forEach(b => b.classList.toggle('active', b.dataset.openPanel === name));
        window.scrollTo({top:0, behavior:'smooth'});
    }

    document.addEventListener('click', function(e) {
        const btn = e.target.closest('[data-open-panel]');
        if (!btn || !root.contains(btn)) return;
        e.preventDefault();
        openPanel(btn.dataset.openPanel);
    });
    $('bdoRefreshBtn')?.addEventListener('click', () => location.reload());

    document.querySelectorAll('[data-stat-filter]').forEach(btn => {
        btn.addEventListener('click', () => {
            deliveryFilter = btn.dataset.statFilter || 'active';
            renderDeliveryList();
            openPanel(deliveryFilter === 'returns' ? 'deliveries' : 'deliveries');
        });
    });

    // FIXED: renderItemsTable with proper grid layout (no broken table HTML)
    function renderItemsTable(job){
        const lines = job?.summary?.lines || [];

        if (!lines.length) {
            return '<div class="bdo-empty">No item detail found.</div>';
        }

        return `
            <div class="bdo-pod-items">
                ${lines.map(l => `
                    <div class="bdo-pod-item">
                        <div class="bdo-pod-item-name">${esc(l.itemName || 'Item')}</div>
                        <div class="bdo-pod-item-grid">
                            <div><small>Type</small><strong>${esc(l.packType || '-')}</strong></div>
                            <div><small>Basket</small><strong>${fmt(l.basket)}</strong></div>
                            <div><small>Carton</small><strong>${fmt(l.carton)}</strong></div>
                            <div><small>KG</small><strong>${fmt(l.kg)}</strong></div>
                        </div>
                    </div>
                `).join('')}
            </div>
        `;
    }

    function activeCardHtml(job, compact=false){
        if (!job || !job.id) {
            return `<div class="bdo-empty">No active delivery yet. Create a DO at the warehouse first.</div>`;
        }

        const status = String(job.status || '').toUpperCase();
        const deliveryStatus = String(job.deliveryStatus || '').toUpperCase();
        const isDelivered = deliveryStatus === 'DELIVERED';
        const canComplete = status === 'SUCCESS' && !isDelivered;
        const chipClass = canComplete || isDelivered ? '' : ' wait';
        const chipText = isDelivered ? 'DELIVERED' : (canComplete ? (deliveryStatus.replaceAll('_',' ') || 'READY FOR DELIVERY') : 'WAITING AUTOCOUNT');
        const docText = job.docNo || ('Job #' + job.id);

        let html = `
            <div class="bdo-active-mini">
                <span class="bdo-status-chip${chipClass}">${esc(chipText)}</span>
                <div class="bdo-job-main">
                    <h3>${esc(job.customerName || 'Customer')}</h3>
                    <p>${esc(docText)}</p>
                </div>
                <div class="bdo-job-metrics">
                    <div class="bdo-metric"><strong>${fmt(job.summary?.items)}</strong><span>Items</span></div>
                    <div class="bdo-metric"><strong>${fmt(job.summary?.baskets)}</strong><span>Baskets</span></div>
                    <div class="bdo-metric"><strong>${fmt(job.summary?.cartons)}</strong><span>Cartons</span></div>
                </div>
                ${compact ? '' : renderItemsTable(job)}`;

        if (!compact) {
            html += `
                <div style="height:12px"></div>
                <form method="post" enctype="multipart/form-data" class="bdo-proof-box" id="bdoCompleteDeliveryForm">
                    <input type="hidden" name="bdo_driver_action" value="complete_delivery">
                    <input type="hidden" name="bdo_driver_nonce" value="__FORM_NONCE__">
                    <input type="hidden" name="job_id" value="${job.id}">
                    <strong>Proof of delivery required</strong>
                    <p>Take photo after vegetables are dropped at the customer place.</p>
                    <input type="file" class="bdo-file" name="delivery_proof" accept="image/*" capture="environment" required ${canComplete ? '' : 'disabled'}>
                    <div style="height:12px"></div>
                    <button type="submit" class="bdo-btn" ${canComplete ? '' : 'disabled'}>Mark as Delivered</button>
                </form>`;
        } else {
            html += `<div style="height:10px"></div><button type="button" class="bdo-btn" data-open-panel="active">Take POD</button>`;
        }

        html += '</div>';
        return html;
    }

    function deliveryListCard(job){
        const status = String(job.status || '').toUpperCase();
        const deliveryStatus = String(job.deliveryStatus || '').toUpperCase();
        const isDelivered = deliveryStatus === 'DELIVERED';
        const canComplete = status === 'SUCCESS' && !isDelivered;
        const docText = job.docNo || ('Job #' + job.id);
        const chipText = isDelivered ? 'DELIVERED' : (canComplete ? 'READY' : 'WAITING');
        return `
            <div class="bdo-delivery-row">
                <div class="bdo-delivery-top">
                    <div>
                        <div class="bdo-delivery-title">${esc(job.customerName || 'Customer')}</div>
                        <div class="bdo-delivery-sub">${esc(docText)}</div>
                    </div>
                    <span class="bdo-status-chip${canComplete || isDelivered ? '' : ' wait'}">${chipText}</span>
                </div>
                <div class="bdo-delivery-metrics">
                    <span>${fmt(job.summary?.items)} Items</span>
                    <span>${fmt(job.summary?.baskets)} Baskets</span>
                    <span>${fmt(job.summary?.cartons)} Cartons</span>
                </div>
                ${isDelivered
                    ? `<button type="button" class="bdo-btn-soft bdoReceiptBtn" data-job-id="${job.id}">View DO Receipt</button>`
                    : `<button type="button" class="bdo-btn" data-select-delivery="${job.id}">Take POD</button>`}
            </div>`;
    }

    function returnListCard(row){
        return `
            <div class="bdo-delivery-row">
                <div class="bdo-delivery-top">
                    <div>
                        <div class="bdo-delivery-title">${esc(row.customerName || 'Customer')}</div>
                        <div class="bdo-delivery-sub">Basket return record</div>
                    </div>
                    <span class="bdo-status-chip">RETURN</span>
                </div>
                <div class="bdo-delivery-metrics bdo-return-metrics">
                    <span>${fmt(row.qty)} Baskets</span>
                </div>
                <button type="button" class="bdo-btn-soft bdoBasketReceiptBtn" data-ledger-id="${row.id}">View Basket Receipt</button>
            </div>`;
    }

    function formatReceiptDate(value) {
        const s = String(value || '');
        const m = s.match(/^(\d{4})-(\d{2})-(\d{2})/);
        if (m) {
            return `${m[3]}/${m[2]}/${m[1]}`;
        }
        return new Date().toLocaleDateString('en-GB');
    }

    function basketReceiptHtml(row) a B.#Vb��������abU
??�c{
        const receiptDate = formatReceiptDate(row.date);
        const proofHtml = row.proofImage
            ? `<div class="bdo-receipt-proof">
                    <div class="bdo-receipt-proof-title">Basket Return Proof</div>
                    <img class="bdo-receipt-proof-img" src="${esc(row.proofImage)}" alt="Basket Return Proof" decoding="async" loading="lazy">
               </div>`
            : `<div class="bdo-receipt-proof">
                    <div class="bdo-receipt-proof-title">Basket Return Proof</div>
                    <div class="bdo-receipt-proof-empty">No basket proof image found.</div>
               </div>`;

        return `
            <div class="bdo-inline-receipt" id="bdoInlineBrReceiptJs">
                <div class="bdo-receipt-actions">
                    <button type="button" onclick="window.bdoPrintBasketReceipt ? window.bdoPrintBasketReceipt() : window.print()">Print / Save PDF</button>
                    <button type="button" onclick="window.bdoShareBasketReceiptPdf && window.bdoShareBasketReceiptPdf()">Share WhatsApp</button>
                    <button type="button" onclick="document.getElementById('bdoDoReceiptMount').innerHTML=''">Close</button>
                </div>
                <div class="bdo-receipt-paper">
                    <div class="bdo-receipt-company">EXCELLENT VEGE SDN. BHD.</div>
                    <div class="bdo-receipt-title">BASKET RETURN RECEIPT</div>
                    <div class="bdo-receipt-row"><strong>Date</strong><span>${esc(receiptDate)}</span></div>
                    <div class="bdo-receipt-row"><strong>Customer</strong><span>${esc(row.customerName || 'Customer')}</span></div>
                    <div class="bdo-receipt-total"><span>RETURNED BASKET</span><strong>${fmt(row.qty)}</strong></div>
                    ${proofHtml}
                    <div class="bdo-receipt-footer">Thank you</div>
                </div>
            </div>`;
    }

    function openBasketReceipt(ledgerId) {
        const row = returnRows.find(r => Number(r.id) === Number(ledgerId));

        if (!row) {
            modal('error', 'Receipt Error', 'Basket return record not found. Please refresh.');
            return;
        }

        $('bdoDoReceiptMount').innerHTML = basketReceiptHtml(row);
    }

    function renderDeliveryList(){
        const mount = $('bdoDeliveryListMount');
        if (!mount) return;

        document.querySelectorAll('[data-stat-filter]').forEach(btn => btn.classList.toggle('active', btn.dataset.statFilter === deliveryFilter));

        let title = 'Active deliveries waiting for POD';
        let rows = activeJobs;
        let renderer = deliveryListCard;

        if (deliveryFilter === 'today') {
            title = 'All DO created today';
            rows = todayJobs;
        } else if (deliveryFilter === 'delivered') {
            title = 'Delivered today';
            rows = deliveredJobs;
        } else if (deliveryFilter === 'returns') {
            title = 'Basket returns today';
            rows = returnRows;
            renderer = returnListCard;
        }

        if (!rows.length) {
            mount.innerHTML = `<div class="bdo-list-filter-title">${esc(title)}</div><div class="bdo-empty">No record found for this filter.</div>`;
            return;
        }
        mount.innerHTML = `<div class="bdo-list-filter-title">${esc(title)}</div>` + rows.map(renderer).join('');
        bindReceiptButtons();
        bindBasketReceiptButtons();
    }

    document.addEventListener('click', function(e) {
        const btn = e.target.closest('[data-select-delivery]');
        if (!btn || !root.contains(btn)) return;
        const id = Number(btn.dataset.selectDelivery);
        const found = activeJobs.find(j => Number(j.id) === id);
        if (found) {
            activeJob = found;
            renderActive();
            openPanel('active');
        }
    });

    function renderActive(){
        $('bdoHomeActiveMount').innerHTML = activeCardHtml(activeJob, true);
        $('bdoActiveMount').innerHTML = activeCardHtml(activeJob, false);
        renderDeliveryList();
        bindReceiptButtons();
    }

    async function apiGet(url){
        const res = await fetch(url, {method:'GET', credentials:'same-origin', headers:{'Accept':'application/json','X-WP-Nonce':cfg.restNonce}, cache:'no-store'});
        if (!res.ok) throw new Error('HTTP ' + res.status);
        const text = await res.text();
        return text ? JSON.parse(text) : null;
    }

    async function apiPost(url, body){
        const res = await fetch(url, {method:'POST', credentials:'same-origin', headers:{'Content-Type':'application/json','X-WP-Nonce':cfg.restNonce}, body:JSON.stringify(body)});
        if (!res.ok) throw new Error(await res.text());
        return await res.json();
    }

    async function createReceiptToken(jobId){
        if (!cfg.restReceiptToken) throw new Error('Receipt token endpoint missing');
        return await apiPost(cfg.restReceiptToken, {job_id:jobId});
    }

    // ============================================================
    // FIXED: doReceiptRows – properly builds table rows with minimum 10 rows
    // ============================================================
    function doReceiptRows(job){
        const lines = job?.summary?.lines || [];
        const minRows = 10;
        const rowCount = Math.max(lines.length, minRows);
        const rows = [];

        for (let i = 0; i < rowCount; i++) {
            const l = lines[i] || null;

            if (!l) {
                rows.push(`
                    <tr>
                        <td class="center"></td>
                        <td class="center"></td>
                        <td class="desc"></td>
                        <td>
                            <div class="bdo-do-checks">
                                <span><i class="bdo-do-box"></i>Ctn</span>
                                <span><i class="bdo-do-box"></i>Bsk</span>
                            </div>
                        </td>
                        <td class="center"></td>
                    </tr>
                `);
                continue;
            }

            const isCarton = String(l.packType || '').toUpperCase() === 'CARTON';
            const isBasket = String(l.packType || '').toUpperCase() === 'BASKET';

            const qtyCount = whole(Number(l.basket || 0) + Number(l.carton || 0));
            const totalKg = whole(l.kg || 0);
            const kgEach = qtyCount > 0 ? whole(totalKg / qtyCount) : totalKg;

            rows.push(`
                <tr>
                    <td class="center">${qtyCount || ''}</td>
                    <td class="center">${kgEach || ''}</td>
                    <td class="desc">${esc(l.itemName || 'Item')}</td>
                    <td>
                        <div class="bdo-do-checks">
                            <span><i class="bdo-do-box ${isCarton ? 'checked' : ''}"></i>Ctn</span>
                            <span><i class="bdo-do-box ${isBasket ? 'checked' : ''}"></i>Bsk</span>
                        </div>
                    </td>
                    <td class="center">${totalKg || ''}</td>
                </tr>
            `);
        }

        return rows.join('');
    }

    function openDoReceipt(jobId){
        const job = activeJobs.find(j => Number(j.id) === Number(jobId)) || deliveredJobs.find(j => Number(j.id) === Number(jobId)) || todayJobs.find(j => Number(j.id) === Number(jobId)) || (activeJob && Number(activeJob.id) === Number(jobId) ? activeJob : null);
        if (!job) {
            modal('error', 'Receipt Error', 'Delivery record not found on this page. Please refresh.');
            return;
        }
        if (String(job.deliveryStatus || '').toUpperCase() !== 'DELIVERED') {
            modal('warning', 'Receipt Not Ready', 'DO receipt is only available after this delivery is marked as delivered.');
            return;
        }
        const s = job.summary || {};
        const totalCtn = fmt(s.cartons || 0);
        const totalBsk = fmt(s.baskets || 0);
        const totalKg = fmt(s.kg || 0);
        const docNo = job.docNo || ('Job #' + job.id);
        const todayText = new Date().toLocaleDateString('en-GB');
        const mount = $('bdoDoReceiptMount');
        mount.innerHTML = `
            <div class="bdo-do-receipt-overlay" id="bdoDoReceiptOverlay">
                <div class="bdo-do-receipt-actions">
                    <button type="button" onclick="window.print()">Print / Save PDF</button>
                    <button type="button" onclick="window.bdoShareDoReceiptPdf && window.bdoShareDoReceiptPdf()">Share WhatsApp</button>
                    <button type="button" onclick="document.getElementById('bdoDoReceiptMount').innerHTML=''">Close</button>
                </div>
                <div class="bdo-do-paper">
                    <div class="bdo-do-head">
                        <div class="bdo-do-logo">Vege</div>
                        <div class="bdo-do-company">
                            <h1>EXCELLENT VEGE SDN. BHD.</h1>
                            <div>No. 45, 47, Complex Pasar Borong, 3rd Miles, Jalan Ipoh, 51200 Kuala Lumpur</div>
                            <div>H/P: 017-4373 752 / 016-963 752 / 012-3013 752</div>
                        </div>
                        <div class="bdo-do-side">
                            <div class="bdo-do-badge">DELIVERY ORDER</div>
                            <div class="bdo-do-no">No <strong>${esc(docNo)}</strong></div>
                        </div>
                    </div>
                    <div class="bdo-do-info">
                        <div class="bdo-do-info-row"><strong>Customer</strong><div class="bdo-do-info-line">${esc(job.customerName || 'Customer')}</div></div>
                        <div class="bdo-do-info-row date"><strong>Date</strong><div class="bdo-do-info-line">${todayText}</div></div>
                    </div>
                    <table class="bdo-do-table">
                        <colgroup>
                            <col style="width:73px"><col style="width:73px"><col><col style="width:106px"><col style="width:74px">
                        </colgroup>
                        <thead>
                            <tr>
                                <th>数量<span>Quantity</span></th>
                                <th>公斤<span>Kg</span></th>
                                <th>货物名称<span>Description</span></th>
                                <th>箱 / 篮<span>Box / Basket</span></th>
                                <th>总公斤<span>Total Kg</span></th>
                            </tr>
                        </thead>
                        <tbody>${doReceiptRows(job)}</tbody>
                    </table>
                    <div class="bdo-do-bottom">
                        <div class="bdo-do-slogan">We Do The EXCELLENT Way</div>
                        <div class="bdo-do-totals">
                            <div class="bdo-do-total-label">总箱<span>Total Ctn</span></div><div class="bdo-do-total-box">${totalCtn}</div>
                            <div class="bdo-do-total-label">总篮<span>Total Bsk</span></div><div class="bdo-do-total-box">${totalBsk}</div>
                            <div class="bdo-do-total-label">总公斤<span>Total Kg</span></div><div class="bdo-do-total-box">${totalKg}</div>
                        </div>
                    </div>
                </div>
                ${job.proofImage ? `<div class="bdo-do-proof-page"><h2>Proof of Delivery</h2><img class="bdo-do-proof-img" src="${esc(job.proofImage)}" alt="Proof of Delivery" decoding="async" loading="lazy"></div>` : `<div class="bdo-do-proof-page"><h2>Proof of Delivery</h2><div class="bdo-empty">No POD image found for this delivery.</div></div>`}
            </div>`;
    }

    function openReceipt(jobId){
        openDoReceipt(jobId);
    }

    function bindReceiptButtons(){
        document.querySelectorAll('.bdoReceiptBtn').forEach(btn => {
            if (btn.dataset.bound === '1') return;
            btn.dataset.bound = '1';
            btn.addEventListener('click', () => openReceipt(btn.dataset.jobId));
        });
    }

    function bindBasketReceiptButtons(){
        document.querySelectorAll('.bdoBasketReceiptBtn').forEach(btn => {
            if (btn.dataset.bound === '1') return;
            btn.dataset.bound = '1';
            btn.addEventListener('click', () => openBasketReceipt(btn.dataset.ledgerId));
        });
    }

    async function pollJob(jobId){
        for (let i = 0; i < 45; i++) {
            await new Promise(r => setTimeout(r, 2000));
            const r = await apiGet(cfg.restJobBase + jobId + '?_t=' + Date.now());
            const job = r?.job || r?.data?.job || r;
            const st = String(job?.status || '').toUpperCase();
            if (st === 'SUCCESS') return job;
            if (st === 'FAILED' || st === 'FAILED_FINAL' || st === 'CANCELLED') throw new Error(job?.error_message || job?.error || 'Job failed');
        }
        throw new Error('Queue timeout. Please refresh and check job status.');
    }

    function normalizeJobFromPoll(job, jobId){
        let payload = job.payload || {};
        let result = job.result || {};
        if (typeof payload === 'string') { try { payload = JSON.parse(payload); } catch(e) { payload = {}; } }
        if (typeof result === 'string') { try { result = JSON.parse(result); } catch(e) { result = {}; } }
        const lines = payload.lines || [];
        let items=0,baskets=0,cartons=0,kg=0, viewLines=[];
        lines.forEach(l => {
            const pack = String(l.packType || '').toUpperCase();
            const b = Number(l.basketQty || 0);
            const c = Number(l.cartonQty || 0);
            const k = Number(l.totalKg || l.qty || 0);
            items++; baskets += b; cartons += c; kg += k;
            viewLines.push({itemCode:l.itemCode||'', itemName:l.itemName||l.description||'Item', packType:pack, basket:b, carton:c, kg:k});
        });
        return {
            id:Number(job.id || jobId),
            docNo:result.docNo || result.DocNo || '',
            docKey:Number(result.docKey || result.DocKey || 0),
            customerName:payload.customerName || payload.debtorName || 'Customer',
            customerCode:payload.customerCode || payload.debtorCode || '',
            address:payload.deliveryAddress || payload.address || '',
            location:payload.location || 'HQ',
            status:job.status || 'SUCCESS',
            deliveryStatus:job.delivery_status || 'PENDING_DELIVERY',
            summary:{items,baskets,cartons,kg,lines:viewLines},
            proofImage:''
        };
    }

    // Picker system
    const picker = {items:[], fetchFn:null, onPick:null, timer:null};
    function pickerNote(msg){ $('bdoPickerResults').innerHTML = `<div class="bdo-picker-note">${esc(msg)}</div>`; }
    function renderPickerItems(items){
        if (!items.length) return pickerNote('No result found');
        $('bdoPickerResults').innerHTML = items.map((it,idx) => `<button type="button" class="bdo-picker-item" data-idx="${idx}"><span class="bdo-picker-main">${esc(it.label)}</span>${it.meta ? `<span class="bdo-picker-sub">${esc(it.meta)}</span>` : ''}</button>`).join('');
    }
    function openPicker(opts){
        picker.items=[]; picker.fetchFn=opts.fetchFn; picker.onPick=opts.onPick;
        $('bdoPickerTitle').textContent = opts.title || 'Search';
        $('bdoPickerSearch').placeholder = opts.placeholder || 'Type to search...';
        $('bdoPickerSearch').value='';
        $('bdoPickerModal').classList.add('active');
        pickerNote('Type to search');
        setTimeout(() => $('bdoPickerSearch').focus(), 80);
    }
    function closePicker(){
        $('bdoPickerModal').classList.remove('active');
        $('bdoPickerSearch').value=''; $('bdoPickerResults').innerHTML='';
        picker.items=[]; picker.fetchFn=null; picker.onPick=nabU&�S�c��������ajH
??�dull;
    }
    async function runPickerSearch(q){
        clearTimeout(picker.timer);
        q = String(q || '').trim();
        if (q.length < 1) { picker.items=[]; pickerNote('Type to search'); return; }
        picker.timer = setTimeout(async () => {
            pickerNote('Searching...');
            try { picker.items = await picker.fetchFn(q); renderPickerItems(picker.items || []); }
            catch(e) { picker.items=[]; pickerNote('Failed to load'); }
        }, 220);
    }
    $('bdoPickerClose').addEventListener('click', closePicker);
    $('bdoPickerBackdrop').addEventListener('click', closePicker);
    $('bdoPickerSearch').addEventListener('input', e => runPickerSearch(e.target.value));
    $('bdoPickerResults').addEventListener('click', e => {
        const btn = e.target.closest('[data-idx]');
        if (!btn) return;
        const item = picker.items[Number(btn.dataset.idx)];
        if (item && picker.onPick) picker.onPick(item.raw);
    });

    async function searchDebtors(q){
        const url = `${cfg.ajaxUrl}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(cfg.debtorNonce)}&q=${encodeURIComponent(q)}`;
        const res = await fetch(url, {credentials:'same-origin'});
        const data = await res.json();
        if (!data.success) throw new Error(data.data?.error || 'Search failed');
        return (data.data?.items || []).map(it => {
            const name = it.name || it.debtorName || '';
            const code = it.code || it.debtorCode || '';
            const salesAgent = it.salesAgent || it.sales_agent || '';
            return {label:name || code, meta:'', raw:{name, code, salesAgent}};
        });
    }

    async function searchItems(q){
        const fd = new FormData();
        fd.append('action','ac_itemcode_suggest');
        fd.append('nonce',cfg.itemNonce);
        fd.append('term',q);
        const res = await fetch(cfg.ajaxUrl, {method:'POST', body:fd, credentials:'same-origin'});
        const data = await res.json();
        if (data?.success && data.data?.items) {
            return data.data.items.map(it => ({label:(it.desc || it.name || it.code || '').trim(), meta:'', raw:{code:it.code || '', name:(it.desc || it.name || it.code || '').trim()}}));
        }
        return [];
    }

    function updateClearButtons(){
        $('bdoCustomerClear').classList.toggle('show', !!$('bdoCustomerInput').value.trim());
        $('bdoItemClear').classList.toggle('show', !!$('bdoItemInput').value.trim());
        $('bdoBrCustomerClear').classList.toggle('show', !!$('bdoBrCustomerInput').value.trim());
    }

    function updateCustomerLockState() {
        const hasItems = doLines.length > 0;
        const customerInput = $('bdoCustomerInput');

        if (!customerInput) {
            return;
        }

        customerInput.classList.toggle('bdo-customer-locked', hasItems);

        if (hasItems) {
            customerInput.title = 'Customer is locked because item lines have been added';
        } else {
            customerInput.title = '';
        }
    }

    function setDoCustomer(c){
        $('bdoCustomerInput').value = c.name || c.code || '';
        $('bdo_do_customer_code').value = c.code || '';
        $('bdo_do_customer_name').value = c.name || '';
        $('bdo_do_sales_agent').value = c.salesAgent || '';
        if (!shared.basketCustomerManuallyCleared) setBrCustomer(c);
        updateClearButtons();
    }

    function clearDoCustomer(){
        $('bdoCustomerInput').value=''; $('bdo_do_customer_code').value=''; $('bdo_do_customer_name').value=''; $('bdo_do_sales_agent').value='';
        shared.basketCustomerManuallyCleared = true;
        clearBrCustomer(false);
        updateClearButtons();
    }

    function setBrCustomer(c){
        $('bdoBrCustomerInput').value = c.name || c.code || '';
        $('bdo_br_debtor_code').value = c.code || '';
        $('bdo_br_debtor_name').value = c.name || '';
        updateClearButtons();
    }

    function clearBrCustomer(manual=true){
        $('bdoBrCustomerInput').value=''; $('bdo_br_debtor_code').value=''; $('bdo_br_debtor_name').value='';
        if (manual) shared.basketCustomerManuallyCleared = true;
        updateClearButtons();
    }

    async function confirmClearItemsForCustomerChange() {
        if (!doLines.length) {
            return true;
        }

        if (!window.Swal) {
            return confirm('Changing customer will clear current item lines. Continue?');
        }

        const result = await Swal.fire({
            icon: 'warning',
            title: 'Change customer?',
            html: `You already added <strong>${doLines.length}</strong> item line(s).<br>Changing customer will clear the current items.`,
            showCancelButton: true,
            confirmButtonText: 'Clear items & change',
            cancelButtonText: 'Cancel',
            confirmButtonColor: '#0B4A2D',
            cancelButtonColor: '#64748b'
        });

        return result.isConfirmed;
    }

    function clearDoItemsOnly() {
        doLines = [];
        renderDoLines();
        clearLineEntry();
        $('bdoDoSuccess').style.display = 'none';
    }

    async function openCustomerPickerSafely() {
        const ok = await confirmClearItemsForCustomerChange();

        if (!ok) {
            return;
        }

        if (doLines.length) {
            clearDoItemsOnly();
            toast('info', 'Items cleared', 'Select the correct customer now.');
        }

        openPicker({
            title: 'Select Customer',
            placeholder: 'Search customer...',
            fetchFn: searchDebtors,
            onPick: (raw) => {
                setDoCustomer(raw);
                shared.basketCustomerManuallyCleared = false;
                closePicker();
            }
        });
    }

    async function clearDoCustomerSafely() {
        const ok = await confirmClearItemsForCustomerChange();

        if (!ok) {
            return;
        }

        if (doLines.length) {
            clearDoItemsOnly();
        }

        clearDoCustomer();
    }

    $('bdoCustomerInput').addEventListener('click', openCustomerPickerSafely);
    $('bdoCustomerClear').addEventListener('click', clearDoCustomerSafely);
    $('bdoBrCustomerInput').addEventListener('click', () => openPicker({title:'Select Customer', placeholder:'Search customer...', fetchFn:searchDebtors, onPick:(raw)=>{setBrCustomer(raw); shared.basketCustomerManuallyCleared=false; closePicker();}}));
    $('bdoItemInput').addEventListener('click', () => openPicker({title:'Select Item', placeholder:'Search item...', fetchFn:searchItems, onPick:(raw)=>{$('bdoItemInput').value=raw.name||raw.code||''; $('bdo_item_code').value=raw.code||''; $('bdo_item_name').value=raw.name||raw.code||''; updateLinePreview(); updateClearButtons(); closePicker();}}));
    $('bdoBrCustomerClear').addEventListener('click', () => clearBrCustomer(true));
    $('bdoItemClear').addEventListener('click', () => {$('bdoItemInput').value=''; $('bdo_item_code').value=''; $('bdo_item_name').value=''; updateLinePreview(); updateClearButtons();});

    function setPackType(type){
        type = String(type || '').toUpperCase() === 'CARTON' ? 'CARTON' : 'BASKET';
        $('bdo_pack_type').value = type;
        document.querySelectorAll('.bdo-type-btn').forEach(b => b.classList.toggle('active', b.dataset.packType === type));
        updateLinePreview();
    }
    document.querySelectorAll('.bdo-type-btn').forEach(btn => btn.addEventListener('click', () => setPackType(btn.dataset.packType)));

    function updateLinePreview(){
        const code = $('bdo_item_code').value.trim();
        const name = $('bdo_item_name').value.trim() || code;
        const type = $('bdo_pack_type').value;
        const qty = whole($('bdo_qty').value);
        const kg = whole($('bdo_kg').value);
        const box = $('bdoLinePreview');
        if (!code || !qty || !kg) { box.style.display='none'; box.innerHTML=''; return; }
        box.style.display='block';
        box.innerHTML = `<strong>${esc(name)}</strong><br>${esc(type)} Qty: ${qty} • KG / Unit: ${kg} • Total KG: ${qty * kg}`;
    }
    $('bdo_qty').addEventListener('input', updateLinePreview);
    $('bdo_kg').addEventListener('input', updateLinePreview);

    function clearLineEntry(){
        $('bdoItemInput').value=''; $('bdo_item_code').value=''; $('bdo_item_name').value=''; $('bdo_qty').value=''; $('bdo_kg').value=''; setPackType('BASKET'); updateLinePreview(); updateClearButtons();
    }

    function renderDoLines(){
        $('bdoLinesCount').textContent = doLines.length;
        if (!doLines.length) {
            $('bdoLines').innerHTML = '<div class="bdo-empty">No items added</div>';
            updateCustomerLockState();
            return;
        }

        $('bdoLines').innerHTML = doLines.map((l,idx) => `
            <div class="bdo-line">
                <div class="bdo-line-top"><div class="bdo-line-name">${esc(l.itemName || 'Item')}</div><button type="button" class="bdo-line-del" data-line-del="${idx}">Delete</button></div>
                <div class="bdo-line-meta">
                    <div class="bdo-line-chip"><small>Type</small><strong>${esc(l.packType)}</strong></div>
                    <div class="bdo-line-chip"><small>Qty</small><strong>${fmt(l.qty)}</strong></div>
                    <div class="bdo-line-chip"><small>KG</small><strong>${fmt(l.kg)}</strong></div>
                    <div class="bdo-line-chip"><small>Total</small><strong>${fmt(l.total)}</strong></div>
                </div>
            </div>`).join('');

        updateCustomerLockState();
    }

    $('bdoAddLineBtn').addEventListener('click', () => {
        const itemCode = $('bdo_item_code').value.trim();
        const itemName = $('bdo_item_name').value.trim() || itemCode;
        const packType = $('bdo_pack_type').value;
        const qty = whole($('bdo_qty').value);
        const kg = whole($('bdo_kg').value);
        if (!itemCode) return toast('error','Select an item');
        if (qty <= 0) return toast('error','Qty must be more than 0');
        if (kg <= 0) return toast('error','KG must be more than 0');
        doLines.push({itemCode,itemName,packType,qty,kg,total:qty*kg});
        renderDoLines();
        clearLineEntry();
        updateCustomerLockState();
        toast('success','Item added');
    });

    $('bdoLines').addEventListener('click', e => {
        const btn = e.target.closest('[data-line-del]');
        if (!btn) return;
        doLines.splice(Number(btn.dataset.lineDel), 1);
        renderDoLines();
        updateCustomerLockState();
        toast('info','Item removed');
    });

    function makeClientRequestId(){ return 'DO-' + Date.now() + '-' + Math.random().toString(36).slice(2); }

    $('bdoSubmitDoBtn').addEventListener('click', async () => {
        if (submittingDo) return;
        const btn = $('bdoSubmitDoBtn');
        const customerCode = $('bdo_do_customer_code').value.trim();
        const customerName = $('bdo_do_customer_name').value.trim();
        const salesAgent = $('bdo_do_sales_agent').value.trim();
        const location = $('bdo_do_location').value.trim() || cfg.defaultLocation;
        const docDate = $('bdo_do_date').value.trim() || cfg.today;
        if (!customerCode) return toast('error','Select customer');
        if (!doLines.length) return toast('error','Add at least one item');

        submittingDo = true;
        btn.disabled = true; btn.textContent = 'Saving...';
        $('bdoDoSuccess').style.display = 'none';
        doReceiptUrl = '';

        try {
            const payloadLines = doLines.map(l => {
                const isBasket = l.packType === 'BASKET';
                return {
                    itemCode:l.itemCode,
                    description:l.itemName,
                    itemName:l.itemName,
                    ItemName:l.itemName,
                    itemDesc:l.itemName,
                    uom:'KG',
                    unitPrice:0,
                    amount:0,
                    taxCode:'SR-0',
                    taxRate:0,
                    packType:l.packType,
                    qty:l.total,
                    kg:l.kg,
                    totalKg:l.total,
                    unitQty:l.qty,
                    basketQty:isBasket ? l.qty : 0,
                    cartonQty:!isBasket ? l.qty : 0,
                    location
                };
            });

            const payload = {
                customerCode, customerName, salesAgent,
                debtorCode:customerCode, DebtorCode:customerCode,
                debtorName:customerName, DebtorName:customerName,
                SalesAgent:salesAgent,
                location, Location:location,
                docDate,
                remark:'',
                lines:payloadLines
            };

            const r = await apiPost(cfg.restJobPost, {type:'DELIVERY_ORDER', client_request_id:makeClientRequestId(), source:'wp-ui', payload});
            const jobId = r.jobId || r.id;
            if (!jobId) throw new Error('No job ID returned');
            toast('info','Job queued','Waiting AutoCount...');
            const job = await pollJob(jobId);
            activeJob = normalizeJobFromPoll(job, jobId);
            activeJobs = [activeJob].concat(activeJobs.filter(j => Number(j.id) !== Number(activeJob.id)));
            todayJobs = [activeJob].concat(todayJobs.filter(j => Number(j.id) !== Number(activeJob.id)));
            deliveryFilter = 'active';
            renderActive();

            doReceiptUrl = '';
            $('bdoDoSuccessText').innerHTML = `DO created: <strong>${esc(activeJob.docNo || ('Job #' + jobId))}</strong>. This delivery is now active.`;
            $('bdoDoSuccess').style.display = 'block';
            $('bdoOpenDoReceiptBtn').href = '#';
            $('bdoOpenDoReceiptBtn').dataset.jobId = String(activeJob.id || jobId);
            $('bdoOpenDoReceiptBtn').style.display = 'inline-flex';
            toast('success','Delivery Order created');
            initialStats.created_today = Number(initialStats.created_today || 0) + 1;
            initialStats.active = Number(initialStats.active || 0) + 1;
            setStats(initialStats);
        } catch(e) {
            modal('error','Submit Error', esc(e.message));
        } finally {
            submittingDo = false;
            btn.disabled = false; btn.textContent = 'Save Delivery Order';
        }
    });

    $('bdoContinueDeliveryBtn').addEventListener('click', () => openPanel('deliveries'));
    $('bdoOpenDoReceiptBtn').addEventListener('click', (e) => { e.preventDefault(); const id = $('bdoOpenDoReceiptBtn').dataset.jobId || (activeJob && activeJob.id); if (id) openDoReceipt(id); });
    $('bdoClearDoBtn').addEventListener('click', () => {
        clearDoCustomer(); clearLineEntry(); doLines=[]; renderDoLines(); $('bdoDoSuccess').style.display='none'; toast('info','Ready for new DO');
    });

    $('bdoBasketReturnForm').addEventListener('submit', e => {
        if (!$('bdo_br_debtor_code').value.trim()) { e.preventDefault(); toast('error','Select customer'); return; }
        if (whole($('bdoBrQty').value) <= 0) { e.preventDefault(); toast('error','Basket qty must be more than 0'); return; }
    });

    document.addEventListener('submit', e => {
        if (e.target && e.target.id === 'bdoCompleteDeliveryForm') {
            const file = e.target.querySelector('input[type="file"]');
            if (!file || !file.files || !file.files.length) {
                e.preventDefault(); toast('error','Photo proof is required');
            }
        }
    });

    setStats(initialStats);
    renderDoLines();
    renderActive();
    updateClearButtons();
    const initialTab = root.dataset.initialTab || 'home';
    if (['home','create','deliveries','active','return'].includes(initialTab)) openPanel(initialTab);
})();
</script>
HTML;

$alert_html = '';
if ($message === 'deliveredajH�,$<d��������ajH
?y����') {
    $alert_html = '<div class="bdo-alert ok">Delivery marked as DELIVERED. You can open the DO receipt from the delivery panel.</div>';
} elseif ($message === 'br_saved') {
    $alert_html = '<div class="bdo-alert ok">Basket return saved. Receipt is shown on this page.</div>';
}

if ($error !== '') {
    $alert_html = '<div class="bdo-alert err">' . esc_html($error) . '</div>';
}

$replacements = [
    '__REST_NONCE__'         => esc_attr($rest_nonce),
    '__AJAX_URL__'           => esc_url($ajax_url),
    '__DEBTOR_NONCE__'       => esc_attr($debtor_nonce),
    '__ITEM_NONCE__'         => esc_attr($item_nonce),
    '__REST_JOB_POST__'      => esc_url($REST_JOB_POST),
    '__REST_JOB_BASE__'      => esc_url($REST_JOB_BASE),
    '__REST_RECEIPT_TOKEN__' => esc_url($REST_RECEIPT_TOKEN),
    '__RECEIPT_BASE__'       => esc_url($receipt_page_base),
    '__DEFAULT_LOCATION__'   => esc_attr($default_location),
    '__TODAY__'              => esc_attr($today),
    '__TODAY_LABEL__'        => esc_html($today_label),
    '__DRIVER_NAME__'        => esc_html($driver_name),
    '__FORM_NONCE__'         => esc_attr($form_nonce),
    '__LOGOUT_URL__'         => esc_url(wp_logout_url(wp_login_url())),
    '__INITIAL_ACTIVE_JSON__'=> $initial_active_json ?: '{}',
    '__INITIAL_ACTIVE_JOBS_JSON__'=> $active_jobs_json ?: '[]',
    '__INITIAL_TODAY_JOBS_JSON__'=> $today_jobs_json ?: '[]',
    '__INITIAL_DELIVERED_JOBS_JSON__'=> $delivered_jobs_json ?: '[]',
    '__INITIAL_RETURN_ROWS_JSON__'=> $return_rows_json ?: '[]',
    '__STATS_JSON__'         => $stats_json ?: '{}',
    '__INLINE_BR_RECEIPT__'  => $inline_br_receipt_html,
    '__ALERT_HTML__'         => $alert_html,
    '__INITIAL_TAB__'        => esc_attr($initial_tab),
    '__INITIAL_FILTER__'     => esc_attr($message === 'delivered' ? 'delivered' : 'active'),
];

echo strtr($html, $replacements);ajH��e��������	�U
??�fpx solid var(--acd-border);
}
.acd-resp-picker-title {
    font-size: 1.05rem;
    font-weight: 800;
}
/* Picker close button - fixed alignment */
.acd-resp-picker-close {
    flex: 0 0 auto;
    width: 2.35rem;
    height: 2.35rem;
    padding: 0;
    border: 1px solid var(--acd-border-strong);
    background: #fff;
    color: var(--acd-text);
    border-radius: 0.55rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    font-size: 1.35rem;
    font-weight: 500;
    font-family: Arial, sans-serif;
    cursor: pointer;
    transition: all 0.18s ease;
    appearance: none;
    -webkit-appearance: none;
}
.acd-resp-picker-close span {
    display: block;
    line-height: 1;
    transform: translateY(-1px);
}
.acd-resp-picker-close:hover {
    background: var(--acd-green-soft);
    border-color: #bbf7d0;
    color: var(--acd-green);
}
.acd-resp-picker-body {
    padding: 0.85rem 0.95rem 0.95rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}
.acd-resp-picker-results {
    max-height: min(24rem, calc(86vh - 9rem));
    overflow-y: auto;
}
/* Override modal text colours to ensure dark text on white background */
.acd-resp-picker-title,
.acd-resp-picker-search,
.acd-resp-picker-results,
.acd-resp-picker-item,
.acd-resp-picker-item-main {
    color: var(--acd-text);
}
.acd-resp-picker-note,
.acd-resp-picker-item-sub {
    color: var(--acd-muted);
}
.acd-resp-picker-item {
    color: var(--acd-text);
}
.acd-resp-picker-item {
    display: block;
    width: 100%;
    text-align: left;
    min-height: 3rem;
    padding: 0.78rem 0.85rem;
    border: 1px solid var(--acd-border);
    border-radius: 0.65rem;
    background: #fff;
    margin-bottom: 0.5rem;
    cursor: pointer;
    transition: all 0.18s ease;
}
.acd-resp-picker-item:hover {
    background: var(--acd-green-soft);
    border-color: #bbf7d0;
}
.acd-resp-picker-item-main {
    font-weight: 800;
}
.acd-resp-picker-item-sub {
    font-size: 0.8rem;
    color: var(--acd-muted);
}

/* Force picker modal to stay centered on desktop, tablet, and mobile (overrides previous bottom-sheet behavior) */
#acd-resp-root .acd-resp-picker-modal {
    align-items: center !important;
    justify-content: center !important;
    padding: 0.75rem !important;
}

#acd-resp-root .acd-resp-picker-sheet {
    width: 100% !important;
    max-width: min(42rem, calc(100vw - 2rem)) !important;
    border-radius: 0.9rem !important;
    max-height: 86vh !important;
    overflow: hidden !important;
}
</style>

<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

<script>
(function(){
    // --------------------------------------------------------------
    // TAB SWITCHING
    // --------------------------------------------------------------
    const tabs = document.querySelectorAll('#acd-resp-root .acd-resp-tab-btn');
    const panes = {
        delivery: document.getElementById('acd-resp-delivery-tab'),
        basket: document.getElementById('acd-resp-basket-tab')
    };
    function activateTab(tabId) {
        tabs.forEach(btn => btn.classList.toggle('active', btn.dataset.tab === tabId));
        Object.keys(panes).forEach(id => panes[id].classList.toggle('active', id === tabId));
    }
    tabs.forEach(btn => btn.addEventListener('click', () => {
        const tabId = btn.dataset.tab;
        if (tabId && panes[tabId]) activateTab(tabId);
    }));

    // --------------------------------------------------------------
    // DELIVERY ORDER MODULE (with customer moved into Add Item)
    // --------------------------------------------------------------
    const root = document.getElementById('acd-resp-root');

    // Shared state between Delivery Order and Basket Return for customer sync
    const sharedCustomerState = {
        basketCustomerManuallyCleared: false
    };

    const doContainer = document.getElementById('acd-resp-delivery-tab');
    if (doContainer && !doContainer.dataset.doInit) {
        doContainer.dataset.doInit = '1';

        const REST_NONCE    = root.dataset.restNonce;
        const RECEIPT_BASE  = root.dataset.receiptBase;
        const RECORDS_BASE  = root.dataset.recordsBase || '';
        const REST_JOB_POST = root.dataset.restJobPost;
        const REST_JOB_BASE = root.dataset.restJobBase;
        const REST_RECEIPT_TOKEN = root.dataset.restReceiptToken;
        const AJAX_URL      = root.dataset.ajaxUrl;
        const DEBTOR_NONCE  = root.dataset.debtorNonce;
        const ITEM_NONCE    = root.dataset.itemNonce;
        let DRIVER_ITEMS = [];
        try {
            DRIVER_ITEMS = JSON.parse(root.dataset.drivers || '[]');
        } catch(e) {
            DRIVER_ITEMS = [];
        }
        const DROPDOWN_META = {
            showDebtorCode: root.dataset.showDebtorCode === '1',
            showItemCode: root.dataset.showItemCode === '1',
            showSalesAgent: false
        };

        const state = {
            lines: [],
            jobFinished: false,
            isSubmitting: false,
            savedPendingClear: false,
        };
        const pickerState = { items: [], defaultItems: [], fetchFn: null, onPick: null };
        let pickerTimer = null;

        function $(id) { return document.getElementById(id); }
        function submitIdleText() { return 'Save Delivery Order'; }
        function submitDoneText() { return 'Saved - Ready for Next Batch'; }
        function submitProgressText() { return 'Queuing...'; }
        function successToastText(count = 1) { return count === 1 ? 'Delivery Order queued' : `${count} Delivery Orders queued`; }

        function escapeHtml(s) {
            if (!s) return '';
            return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
        }

        // NEW helper functions for quantity and KG (decimal support)
        function fmtQty(n) {
            const x = Number(n);
            return (!isFinite(x)) ? '0' : String(Math.round(x));
        }

        function fmtKg(n) {
            const x = Number(n);
            return (!isFinite(x)) ? '0.00' : x.toFixed(2);
        }

        function fmtMoney(n) {
            const x = Number(n);
            return (!isFinite(x)) ? '0.00' : x.toFixed(2);
        }

        function parseQty(n) {
            const x = Number(n);
            return (!isFinite(x) || x < 0) ? 0 : Math.round(x);
        }

        function parseKg(n) {
            const x = Number(n);
            return (!isFinite(x) || x < 0) ? 0 : Number(x.toFixed(2));
        }

        function parseMoney(n) {
            const x = Number(n);
            return (!isFinite(x) || x < 0) ? 0 : x;
        }

        function roundMoney(n) {
            return Number(parseMoney(n).toFixed(2));
        }

        function calcTotalKg(qty, kg) {
            return Number(((parseQty(qty) || 0) * (parseKg(kg) || 0)).toFixed(2));
        }

        function kgKey(n) {
            return fmtKg(parseKg(n));
        }

        function calcTotalPrice(line) {
            return parseMoney(line?.price) * (parseFloat(line?.total) || 0);
        }

        function normalizeBatchId(value) {
            return String(value || '').replace(/[^a-zA-Z0-9\-_:.]/g, '').slice(0, 80);
        }
        function makeBulkBatchId() {
            return normalizeBatchId(`DOBULK-${Date.now()}-${Math.random().toString(36).slice(2)}`);
        }
        function buildRecordsUrl(bulkBatchId) {
            const base = RECORDS_BASE || '/VegeBasketDO/delivery-order-records/';
            const url = new URL(base, window.location.origin);
            url.searchParams.set('bulkBatchId', normalizeBatchId(bulkBatchId));
            url.searchParams.set('print', '1');
            return url.toString();
        }

        function showToast(icon, title, text='') {
            if (window.Swal) Swal.fire({ toast: true, position: 'center', icon, title, text, showConfirmButton: false, timer: 2600, timerProgressBar: true });
        }
        function showModal(icon, title, html) {
            if (window.Swal) Swal.fire({ icon, title, html, confirmButtonText: 'OK' });
        }
        function showBulkSuccessModal(result) {
            const count = result?.count || 0;
            const recordsUrl = result?.recordsUrl || '#';
            const label = count === 1 ? '1 delivery order' : `${count} delivery orders`;

            if (window.Swal) {
                Swal.fire({
                    icon: 'success',
                    title: 'Delivery Orders Queued',
                    html: `<p>${escapeHtml(label)} queued for AutoCount.</p><p>DO numbers are still generating. Use the status page to print when ready.</p>`,
                    showCancelButton: true,
                    confirmButtonText: 'View Status / Print When Ready',
                    cancelButtonText: 'Close'
                }).then(res => {
                    if (res.isConfirmed && recordsUrl !== '#') {
                        window.open(recordsUrl, '_blank', 'noopener');
                    }
                });
            } else if (recordsUrl !== '#') {
                window.open(recordsUrl, '_blank', 'noopener');
            }
        }

        function savedJobListHtml(savedJobs) {
            if (!savedJobs.length) return '<p>No delivery orders were queued.</p>';

            const rows = savedJobs.map(job => {
                const customer = escapeHtml(job.customerName || job.customerCode || '-');
                const driver = escapeHtml(job.assignedDriverLabel || '-');
                const docNo = escapeHtml(job.docNo || 'Queued');
                const jobId = escapeHtml(job.jobId || '-');

                return `<li><strong>${docNo}</strong> | ${customer} | Driver: ${driver} | Job #${jobId}</li>`;
            }).join('');

            return `<ul style="text-align:left;margin:.75rem 0 0;padding-left:1.25rem;">${rows}</ul>`;
        }

        function showBulkPartialFailureModal(result) {
            const savedJobs = result?.savedJobs || [];
            const recordsUrl = result?.recordsUrl || '#';
            const errorMessage = result?.errorMessage || 'Submit failed';
            const savedCount = savedJobs.length;
            const title = savedCount
                ? `${savedCount} DO${savedCount === 1 ? '' : 's'} already queued`
                : 'Delivery Order submit failed';
            const html = `
                <p>${escapeHtml(errorMessage)}</p>
                ${savedCount ? '<p><strong>Do not resubmit these queued DOs.</strong> They were removed from the form, so retry will submit only the remaining unsaved items.</p>' : ''}
                ${savedJobListHtml(savedJobs)}
            `;

            if (window.Swal) {
                Swal.fire({
                    icon: savedCount ? 'warning' : 'error',
                    title,
                    html,
                    showCancelButton: savedCount && recordsUrl !== '#',
                    confirmButtonText: 'OK',
                    cancelButtonText: 'View Queued DOs'
                }).then(res => {
                    if (res.dismiss === Swal.DismissReason.cancel && recordsUrl !== '#') {
                        window.open(recordsUrl, '_blank', 'noopener');
                    }
                });
                return;
            }

            showModal(savedCount ? 'warning' : 'error', title, html);
        }

        function updateEntryTotal() {
            const itemCode = ($('acd_resp_do_item').value || '').trim();
            const itemName = ($('acd_resp_do_item_display').value || '').trim() || itemCode;
            const packType = ($('acd_resp_do_pack_type').value || '').trim();
            const qtyRaw = ($('acd_resp_do_qty').value || '').trim();
            const kgRaw = ($('acd_resp_do_kg').value || '').trim();
            const qty = parseQty(qtyRaw || '0');
            const kg = parseKg(kgRaw || '0');
            const total = calcTotalKg(qty, kg);
            const pv = $('acd_resp_do_line_preview');
            if (!itemCode || qtyRaw === '' || kgRaw === '') {
                pv.style.display = 'none';
                pv.innerHTML = '';
                return;
            }
            pv.style.display = 'block';
            pv.innerHTML = `<div><b>${escapeHtml(itemName)}</b></div>
                            <div>Type: ${escapeHtml(packType)} | Qty: ${fmtQty(qty)} | KG: ${fmtKg(kg)} | Total KG: ${fmtKg(total)}</div>`;
        }

        function setPackType(type) {
            const nextType = String(type || '').toUpperCase() === 'BASKET' ? 'BASKET' : 'CARTON';
            $('acd_resp_do_pack_type').value = nextType;
            document.querySelectorAll('#acd_resp_do_pack_type_toggle .acd-resp-type-btn').forEach(btn => {
                btn.classList.toggle('active', (btn.dataset.packType || '').toUpperCase() === nextType);
            });
            updateEntryTotal();
        }

        function updateUI() {
            const lines = state.lines;
            const badge = document.getElementById('acd_resp_do_lines_count_badge');
            if (badge) badge.innerText = lines.length;

            const container = $('acd_resp_do_lines');
            if (!lines.length) {
                container.innerHTML = '<div class="acd-resp-empty">No items added</div>';
                return;
            }

            container.innerHTML = lines.map((l, idx) => `
                <div class="acd-resp-line" data-idx="${idx}">
                    <div><strong>${escapeHtml(l.itemName || l.itemCode)}</strong></div>
                    <div><strong>${escapeHtml(l.customerName || l.customerCode)}</strong></div>
                    <div>${escapeHtml(l.assignedDriverLabel || l.assignedDriverLogin)}</div>
                    <div><span class="acd-resp-type-pill">${l.packType}</span></div>
                    <div class="acd-resp-number-cell">${fmtQty(l.qty)}</div>
                    <div class="acd-resp-number-cell">${fmtKg(l.kg)}</div>
                    <div class="acd-resp-number-cell">${fmtKg(l.total)}</div>
                    <div class="acd-resp-price-cell"><input type="number" class="acd-resp-price-input" data-price-idx="${idx}" value="${fmtMoney(l.price)}" min="0" step="0.01" inputmode="decimal" aria-label="Price for ${escapeHtml(l.itemName || l.itemCode)}"></div>
                    <div class="acd-resp-money-cell" data-total-price-idx="${idx}">${fmtMoney(calcTotalPrice(l))}</div>
                    <div><button type="button" class="acd-resp-delete-btn" data-idx="${idx}" aria-label="Delete ${escapeHtml(l.itemName || l.itemCode)}" title="Delete">&#128465;</button></div>
                </div>
            `).join('');
        }

        function updateLinePrice(idx, value, shouldFormatInput = false) {
            if (isNaN(idx) || !state.lines[idx]) return;
            state.lines[idx].price = parseMoney(value);
            const nextTotal = fmtMoney(calcTotalPrice(state.lines[idx]));
            document.querySelectorAll(`[data-total-price-idx="${idx}"]`).forEach(el => {
                el.textContent = nextTotal;
            });
            if (shouldFormatInput) {
                document.querySelectorAll(`[data-price-idx="${idx}"]`).forEach(input => {
                    input.value = fmtMoney(state.lines[idx].price);
                });
            }
        }

        async function apiGet(url) {
            const res = await fetch(url, { method: 'GET', credentials: 'same-origin', headers: { 'Accept': 'application/json', 'X-WP-Nonce': REST_NONCE }, cache: 'no-store' });
            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            const text = await res.text();
            return text ? JSON.parse(text) : null;
        }
        async function apiPost(url, body) {
            const res = await fetch(url, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': REST_NONC	�U�+~f��������	�i
??�gE }, body: JSON.stringify(body) });
            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            return await res.json();
        }

        async function createReceiptToken(jobId) {
            if (!REST_RECEIPT_TOKEN) {
                throw new Error('Receipt token endpoint missing');
            }
            return await apiPost(REST_RECEIPT_TOKEN, { job_id: jobId });
        }

        function buildPayloadLine(l, location) {
            const displayName = String(l.itemName || l.itemCode || '').trim();
            const isBasket = (l.packType === 'BASKET');
            const count = l.qty;
            const weightPerUnit = l.kg;
            const totalWeight = l.total;
            const unitPrice = roundMoney(l.price || 0);
            const amount = roundMoney(unitPrice * totalWeight);

            return {
                itemCode: l.itemCode,
                description: displayName,
                itemName: displayName,
                ItemName: displayName,
                itemDesc: displayName,
                uom: 'KG',
                unitPrice,
                amount,
                taxCode: 'SR-0',
                taxRate: 0,
                packType: l.packType,
                qty: totalWeight,
                kg: weightPerUnit,
                totalKg: totalWeight,
                unitQty: count,
                basketQty: isBasket ? count : 0,
                cartonQty: !isBasket ? count : 0,
                location
            };
        }

        function groupKey(customerCode, assignedDriverId) {
            return `${customerCode}::${assignedDriverId}`;
        }

        function groupLinesByCustomerDriver(lines) {
            const groups = new Map();
            lines.forEach(line => {
                const key = groupKey(line.customerCode, line.assignedDriverId);
                if (!groups.has(key)) {
                    groups.set(key, {
                        key,
                        customerCode: line.customerCode,
                        customerName: line.customerName,
                        salesAgent: line.salesAgent || '',
                        assignedDriverId: line.assignedDriverId,
                        assignedDriverLabel: line.assignedDriverLabel,
                        assignedDriverLogin: line.assignedDriverLogin,
                        lines: []
                    });
                }
                groups.get(key).lines.push(line);
            });
            return Array.from(groups.values());
        }

        function removeSavedGroupsFromForm(savedJobs) {
            const savedKeys = new Set(savedJobs.map(job => job.groupKey).filter(Boolean));
            if (!savedKeys.size) return;

            state.lines = state.lines.filter(line => !savedKeys.has(groupKey(line.customerCode, line.assignedDriverId)));
            updateUI();
        }

        function clearDeliveryFormAfterSave() {
            clearCustomerSelection();
            clearDriverSelection();
            clearLineEntry();
            state.lines = [];
            state.jobFinished = false;
            state.savedPendingClear = false;
            const submitBtn = $('acd_resp_do_submit');
            if (submitBtn) {
                submitBtn.disabled = false;
                submitBtn.textContent = submitIdleText();
            }
            updateUI();
            updateClearButtons();
        }

        async function searchItemsLive(q) {
            if (!AJAX_URL || !ITEM_NONCE) return [];
            const fd = new FormData();
            fd.append('action', 'ac_itemcode_suggest');
            fd.append('nonce', ITEM_NONCE);
            fd.append('term', q);
            const res = await fetch(AJAX_URL, { method: 'POST', body: fd, credentials: 'same-origin' });
            const data = await res.json();
            if (data?.success && data.data?.items) {
                return data.data.items.map(it => ({
                    code: it.code || '',
                    name: (it.desc || it.name || '').trim(),
                    price: parseMoney(it.price ?? it.Price ?? 0)
                }));
            }
            return [];
        }

        async function searchDebtorsLive(q) {
            const wrapper = $('acdRespDebtorWrapper');
            const url = `${wrapper.dataset.ajaxUrl}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(wrapper.dataset.nonce)}&q=${encodeURIComponent(q)}`;
            const res = await fetch(url, { credentials: 'same-origin' });
            const data = await res.json();
            if (!data.success) throw new Error(data.data?.error || 'Search failed');
            const items = data.data?.items || [];
            return items.map(it => {
                const name = it.name || it.debtorName || '';
                const code = it.code || it.debtorCode || '';
                const sa = (it.salesAgent || it.sales_agent || '').trim();
                const meta = [];
                if (DROPDOWN_META.showDebtorCode && code) meta.push(code);
                if (DROPDOWN_META.showSalesAgent && sa) meta.push('SA: ' + sa);
                return { label: name || code, meta: meta.join(' • '), raw: { name, code, salesAgent: sa } };
            });
        }

        function renderPickerNote(msg) { $('acd_resp_do_picker_results').innerHTML = `<div class="acd-resp-picker-note">${escapeHtml(msg)}</div>`; }
        function renderPickerItems(items) {
            const box = $('acd_resp_do_picker_results');
            if (!items.length) { renderPickerNote('No result found'); return; }
            box.innerHTML = items.map((it, idx) => `<button type="button" class="acd-resp-picker-item" data-picker-idx="${idx}"><span class="acd-resp-picker-item-main">${escapeHtml(it.label || '')}</span>${it.meta ? `<span class="acd-resp-picker-item-sub">${escapeHtml(it.meta)}</span>` : ''}</button>`).join('');
        }
        async function runPickerSearch(q) {
            const query = (q || '').trim();
            clearTimeout(pickerTimer);
            if (query.length < 1) {
                pickerState.items = pickerState.defaultItems || [];
                if (pickerState.items.length) {
                    renderPickerItems(pickerState.items);
                } else {
                    renderPickerNote('Type to search');
                }
                return;
            }
            pickerTimer = setTimeout(async () => {
                renderPickerNote('Searching...');
                try {
                    const items = await pickerState.fetchFn(query);
                    pickerState.items = items || [];
                    renderPickerItems(pickerState.items);
                } catch(e) { pickerState.items = []; renderPickerNote('Failed to load'); }
            }, 220);
        }
        function openPicker(opts) {
            pickerState.defaultItems = opts.initialItems || [];
            pickerState.items = pickerState.defaultItems;
            pickerState.fetchFn = opts.fetchFn;
            pickerState.onPick = opts.onPick;
            $('acd_resp_do_picker_title').textContent = opts.title || 'Search';
            $('acd_resp_do_picker_search').placeholder = opts.placeholder || 'Type to search...';
            $('acd_resp_do_picker_search').value = '';
            $('acd_resp_do_picker_modal').classList.add('active');
            if (pickerState.items.length) {
                renderPickerItems(pickerState.items);
            } else {
                renderPickerNote('Type to search');
            }
            setTimeout(() => $('acd_resp_do_picker_search').focus(), 80);
        }
        function closePicker() {
            $('acd_resp_do_picker_modal').classList.remove('active');
            $('acd_resp_do_picker_search').value = '';
            $('acd_resp_do_picker_results').innerHTML = '';
            pickerState.items = []; pickerState.defaultItems = []; pickerState.fetchFn = null; pickerState.onPick = null;
        }
        function updateClearButtons() {
            const debtorHas = !!($('acdRespDebtorInput')?.value.trim());
            const driverHas = !!($('acd_resp_do_driver_name')?.value.trim());
            const itemHas = !!($('acd_resp_do_item_name')?.value.trim());
            $('acdRespDebtorClear')?.classList.toggle('show', debtorHas);
            $('acdRespDriverClear')?.classList.toggle('show', driverHas);
            $('acdRespItemClear')?.classList.toggle('show', itemHas);
        }

        // ---- Customer sync functions ----
        function setBasketCustomerFromDelivery(customer) {
            if (sharedCustomerState.basketCustomerManuallyCleared) {
                return;
            }
            const brInput = $('acdRespBrDebtorInput');
            const brCode = $('acd_resp_br_debtor_code');
            const brName = $('acd_resp_br_debtor_name');
            const brClear = $('acdRespBrDebtorClear');

            const name = customer?.name || '';
            const code = customer?.code || '';

            if (brInput) brInput.value = name || code || '';
            if (brCode) brCode.value = code;
            if (brName) brName.value = name;
            if (brClear) {
                brClear.classList.toggle('show', !!(name || code));
            }
        }

        function clearBasketCustomerFromDelivery() {
            const brInput = $('acdRespBrDebtorInput');
            const brCode = $('acd_resp_br_debtor_code');
            const brName = $('acd_resp_br_debtor_name');
            const brClear = $('acdRespBrDebtorClear');

            if (brInput) brInput.value = '';
            if (brCode) brCode.value = '';
            if (brName) brName.value = '';
            if (brClear) brClear.classList.remove('show');
        }

        function setDeliveryCustomer(picked) {
            const name = picked?.name || '';
            const code = picked?.code || '';
            const salesAgent = picked?.salesAgent || '';

            $('acdRespDebtorInput').value = name || code || '';
            $('acd_resp_do_customer').value = code;
            $('acd_resp_do_customer_name').value = name;
            $('acd_resp_do_sales_agent').value = salesAgent;

            sharedCustomerState.basketCustomerManuallyCleared = false;

            setBasketCustomerFromDelivery({
                name,
                code
            });

            updateClearButtons();
        }

        function clearCustomerSelection() {
            $('acdRespDebtorInput').value = '';
            $('acd_resp_do_customer').value = '';
            $('acd_resp_do_customer_name').value = '';
            $('acd_resp_do_sales_agent').value = '';

            sharedCustomerState.basketCustomerManuallyCleared = true;
            clearBasketCustomerFromDelivery();

            updateClearButtons();
        }

        function searchDriversLive(q) {
            const query = String(q || '').trim().toLowerCase();
            if (!query) return Promise.resolve(DRIVER_ITEMS);
            return Promise.resolve(DRIVER_ITEMS.filter(driver => {
                const haystack = [
                    String(driver.label || '').toUpperCase(),
                    String(driver.name || '').toUpperCase(),
                    driver.login || ''
                ].join(' ').toLowerCase();
                return haystack.includes(query);
            }));
        }

        function setDeliveryDriver(picked) {
            const id = parseInt(picked?.id || 0, 10) || 0;
            const label = String(picked?.login || picked?.label || picked?.name || '').toUpperCase();
            const login = picked?.login || '';
            $('acd_resp_do_driver_name').value = label;
            $('acd_resp_do_driver').value = id ? String(id) : '';
            $('acd_resp_do_driver_login').value = login;
            updateClearButtons();
        }

        function clearDriverSelection() {
            $('acd_resp_do_driver_name').value = '';
            $('acd_resp_do_driver').value = '';
            $('acd_resp_do_driver_login').value = '';
            updateClearButtons();
        }

        function openDebtorPicker() {
            openPicker({
                title: 'Select Customer',
                placeholder: 'Search customer...',
                fetchFn: searchDebtorsLive,
                onPick: (picked) => {
                    if (!picked) return;
                    setDeliveryCustomer(picked);
                    closePicker();
                }
            });
        }

        function openDriverPicker() {
            const driverOptions = DRIVER_ITEMS.map(driver => ({
                label: String(driver.login || '').toUpperCase(),
                meta: '',
                raw: driver
            }));
            openPicker({
                title: 'Select Driver',
                placeholder: 'Search driver...',
                initialItems: driverOptions,
                fetchFn: async (q) => {
                    const drivers = await searchDriversLive(q);
                    return drivers.map(driver => ({
                        label: String(driver.login || '').toUpperCase(),
                        meta: '',
                        raw: driver
                    }));
                },
                onPick: (picked) => {
                    if (!picked) return;
                    setDeliveryDriver(picked);
                    closePicker();
                }
            });
        }

        function clearItemSelection() {
            $('acd_resp_do_item_name').value = '';
            $('acd_resp_do_item').value = '';
            $('acd_resp_do_item_display').value = '';
            $('acd_resp_do_item_price').value = '0';
            updateEntryTotal();
            updateClearButtons();
        }

        function openItemPicker() {
            openPicker({
                title: 'Select Item',
                placeholder: 'Search item...',
                fetchFn: async (q) => {
                    const items = await searchItemsLive(q);
                    return items.map(it => ({
                        label: it.name || it.code,
                        meta: (DROPDOWN_META.showItemCode && it.code) ? it.code : '',
                        raw: { code: it.code, name: it.name || it.code, price: it.price || 0 }
                    }));
                },
                onPick: (picked) => {
                    if (!picked) return;
                    $('acd_resp_do_item_name').value = picked.name || picked.code || '';
                    $('acd_resp_do_item').value = picked.code || '';
                    $('acd_resp_do_item_display').value = picked.name || picked.code || '';
                    $('acd_resp_do_item_price').value = fmtMoney(picked.price || 0);
                    updateEntryTotal();
                    updateClearButtons();
                    closePicker();
                }
            });
        }

        function initPickerModal() {
            $('acd_resp_do_picker_close').addEventListener('click', closePicker);
            $('acd_resp_do_picker_backdrop').addEventListener('click', closePicker);
            $('acd_resp_do_picker_search').addEventListener('input', function() { runPickerSearch(this.value); });
            $('acd_resp_do_picker_results').addEventListener('click', (e) => {
                const btn = e.target.closest('[data-picker-idx]');
                if (!btn) return;
                const idx = parseInt(btn.dataset.pickerIdx);
                if (!isNaN(idx) && pickerState.items[idx] && pickerState.onPick) pickerState.onPick(pickerState.items[idx].raw);
            });
        }
        function initPickerTriggers() {
            $('acdRespDebtorInput').setAttribute('readonly', 'readonly');
            $('acd_resp_do_driver_name').setAttribute('readonly', 'readonly');
            $('acd_resp_do_item_name').setAttribute('readonly', 'readonly');
            $('acdRespDebtorInput').addEventListener('click', openDebtorPicker);
	�i�)3g��������	��
??�o            $('acd_resp_do_driver_name').addEventListener('click', openDriverPicker);
            $('acd_resp_do_item_name').addEventListener('click', openItemPicker);
            $('acdRespDebtorClear')?.addEventListener('click', (e) => { e.preventDefault(); clearCustomerSelection(); });
            $('acdRespDriverClear')?.addEventListener('click', (e) => { e.preventDefault(); clearDriverSelection(); });
            $('acdRespItemClear')?.addEventListener('click', (e) => { e.preventDefault(); clearItemSelection(); });
        }
        function makeClientRequestId(prefix='DO') { return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`; }

        function clearLineEntry() {
            $('acd_resp_do_qty').value = '';
            $('acd_resp_do_kg').value = '';
            $('acd_resp_do_item_name').value = '';
            $('acd_resp_do_item').value = '';
            $('acd_resp_do_item_display').value = '';
            $('acd_resp_do_item_price').value = '0';
            updateEntryTotal();
            updateClearButtons();
        }

        // ---- MERGE LOGIC (same customer+driver+item+type+KG) ----
        function findMergeableLineIndex(nextLine) {
            return state.lines.findIndex(line => {
                return String(line.customerCode || '') === String(nextLine.customerCode || '')
                    && String(line.assignedDriverId || '') === String(nextLine.assignedDriverId || '')
                    && String(line.itemCode || '').toUpperCase() === String(nextLine.itemCode || '').toUpperCase()
                    && String(line.packType || '').toUpperCase() === String(nextLine.packType || '').toUpperCase()
                    && kgKey(line.kg) === kgKey(nextLine.kg);
            });
        }

        function mergeLine(existingLine, nextLine) {
            const mergedQty = parseQty(existingLine.qty || 0) + parseQty(nextLine.qty || 0);
            const sameKg = parseKg(existingLine.kg || 0);

            existingLine.qty = mergedQty;
            existingLine.kg = sameKg;
            existingLine.total = calcTotalKg(mergedQty, sameKg);

            if (parseMoney(existingLine.price || 0) <= 0 && parseMoney(nextLine.price || 0) > 0) {
                existingLine.price = parseMoney(nextLine.price || 0);
            }

            return existingLine;
        }

        // ---- Success actions panel ----
        function hideDeliverySuccessActions() {
            const box = $('acd_resp_do_success_actions');
            const docNoEl = $('acd_resp_do_success_docno');
            const receiptBtn = $('acd_resp_do_receipt_btn');

            if (box) box.style.display = 'none';
            if (docNoEl) docNoEl.textContent = '-';
            if (receiptBtn) {
                receiptBtn.href = '#';
                receiptBtn.style.display = 'none';
            }
        }

        function showDeliverySuccessActions(data) {
            const box = $('acd_resp_do_success_actions');
            const docNoEl = $('acd_resp_do_success_docno');
            const receiptBtn = $('acd_resp_do_receipt_btn');

            const docNo = data?.docNo || data?.batchLabel || '-';
            const receiptUrl = data?.receiptUrl || '';

            if (docNoEl) docNoEl.textContent = docNo;
            if (receiptBtn && receiptUrl) {
                receiptBtn.href = receiptUrl;
                receiptBtn.style.display = 'inline-flex';
            }
            if (box) box.style.display = 'block';
        }

        function resetDeliveryOrderForm() {
            clearCustomerSelection();
            clearLineEntry();

            state.lines = [];
            state.jobFinished = false;
            state.savedPendingClear = false;

            const dateField = $('acd_resp_do_date');
            if (dateField) dateField.value = root.dataset.today || '';
            const driverSelect = $('acd_resp_do_driver');
            if (driverSelect) clearDriverSelection();

            const submitBtn = $('acd_resp_do_submit');
            if (submitBtn) {
                submitBtn.disabled = false;
                submitBtn.textContent = submitIdleText();
            }

            hideDeliverySuccessActions();
            updateUI();
            updateClearButtons();
        }

        // Init
        initPickerModal();
        initPickerTriggers();
        $('acd_resp_do_qty').addEventListener('input', updateEntryTotal);
        $('acd_resp_do_kg').addEventListener('input', updateEntryTotal);
        $('acd_resp_do_pack_type').addEventListener('change', () => setPackType($('acd_resp_do_pack_type').value));
        document.querySelectorAll('#acd_resp_do_pack_type_toggle .acd-resp-type-btn').forEach(btn => btn.addEventListener('click', () => setPackType(btn.dataset.packType)));
        setPackType('BASKET');
        updateUI();

        // Add Item click with merge
        $('acd_resp_do_addline').addEventListener('click', () => {
            const itemCode = ($('acd_resp_do_item').value || '').trim();
            const itemName = ($('acd_resp_do_item_display').value || '').trim() || itemCode;
            const packType = ($('acd_resp_do_pack_type').value || '').trim();
            const qtyRaw = ($('acd_resp_do_qty').value || '').trim();
            const kgRaw = ($('acd_resp_do_kg').value || '').trim();
            const qty = parseQty(qtyRaw || '0');
            const kg = parseKg(kgRaw || '0');
            const price = parseMoney($('acd_resp_do_item_price').value || '0');
            const customerCode = ($('acd_resp_do_customer').value || '').trim();
            const customerName = ($('acd_resp_do_customer_name').value || '').trim();
            const salesAgent = ($('acd_resp_do_sales_agent').value || '').trim();
            const assignedDriverId = parseInt($('acd_resp_do_driver')?.value || '0', 10) || 0;
            const assignedDriverLabel = ($('acd_resp_do_driver_name')?.value || '').trim();
            const assignedDriverLogin = ($('acd_resp_do_driver_login')?.value || '').trim();
            if (!customerCode) { showToast('error', 'Select customer'); return; }
            if (!assignedDriverId) { showToast('error', 'Select driver'); return; }
            if (!itemCode) { showToast('error', 'Select an item'); return; }
            if (qty <= 0) { showToast('error', 'Qty must be >0'); return; }
            if (kg <= 0) { showToast('error', 'KG must be >0'); return; }

            const nextLine = {
                customerCode,
                customerName,
                salesAgent,
                assignedDriverId,
                assignedDriverLabel,
                assignedDriverLogin,
                itemCode,
                itemName,
                packType,
                qty,
                kg,
                total: calcTotalKg(qty, kg),
                price
            };

            const existingIdx = findMergeableLineIndex(nextLine);

            if (existingIdx >= 0) {
                mergeLine(state.lines[existingIdx], nextLine);
                updateUI();
                clearLineEntry();
                showToast(
                    'warning',
                    'Same item + KG merged',
                    `${itemName} ${fmtKg(kg)}KG already exists for ${customerName || customerCode}. Quantity has been added into the same row.`
                );
                return;
            }

            state.lines.push(nextLine);
            updateUI();
            clearLineEntry();
            showToast('success', 'Item added');
        });

        // Item detail events for editable price and delete buttons.
        document.getElementById('acd_resp_do_lines').addEventListener('click', (e) => {
            const btn = e.target.closest('.acd-resp-delete-btn');
            if (!btn) return;
            const idx = parseInt(btn.dataset.idx);
            if (!isNaN(idx)) {
                state.lines.splice(idx, 1);
                updateUI();
                showToast('info', 'Item removed');
            }
        });
        document.getElementById('acd_resp_do_lines').addEventListener('input', (e) => {
            const input = e.target.closest('.acd-resp-price-input');
            if (!input) return;
            updateLinePrice(parseInt(input.dataset.priceIdx, 10), input.value, false);
        });
        document.getElementById('acd_resp_do_lines').addEventListener('change', (e) => {
            const input = e.target.closest('.acd-resp-price-input');
            if (!input) return;
            updateLinePrice(parseInt(input.dataset.priceIdx, 10), input.value, true);
        });

        const clearNewBtn = $('acd_resp_do_clear_new_btn');
        if (clearNewBtn) {
            clearNewBtn.addEventListener('click', () => {
                resetDeliveryOrderForm();
                showToast('info', 'Ready for new DO');
            });
        }

        $('acd_resp_do_submit').addEventListener('click', async () => {
            if (state.isSubmitting) { showToast('warning', 'Already submitting'); return; }

            const submitBtn = $('acd_resp_do_submit');
            let saveSucceeded = false;
            state.isSubmitting = true;
            state.jobFinished = false;
            submitBtn.disabled = true;
            submitBtn.textContent = submitProgressText();

            const savedJobs = [];
            let recordsUrl = '#';

            try {
                const location = ($('acd_resp_do_location').value || '').trim();
                const docDate = ($('acd_resp_do_date').value || '').trim();
                if (!state.lines.length) throw new Error('Add at least one item');

                const groups = groupLinesByCustomerDriver(state.lines);
                if (!groups.length) throw new Error('Add at least one valid item');

                groups.forEach((group, groupIdx) => {
                    if (!group.customerCode) throw new Error(`Group ${groupIdx + 1}: customer missing`);
                    if (!group.assignedDriverId) throw new Error(`Group ${groupIdx + 1}: driver missing`);
                    if (!group.lines.length) throw new Error(`Group ${groupIdx + 1}: item lines missing`);
                    group.lines.forEach((line, lineIdx) => {
                        if (!line.itemCode) throw new Error(`Group ${groupIdx + 1}, line ${lineIdx + 1}: item missing`);
                        if (line.qty <= 0 || line.kg <= 0 || line.total <= 0) {
                            throw new Error(`Group ${groupIdx + 1}, line ${lineIdx + 1}: quantity and KG must be >0`);
                        }
                    });
                });

                const bulkBatchId = makeBulkBatchId();
                recordsUrl = buildRecordsUrl(bulkBatchId);

                for (let i = 0; i < groups.length; i++) {
                    const group = groups[i];
                    const payloadLines = group.lines.map(line => buildPayloadLine(line, location));
                    const payload = {
                        bulkBatchId,
                        customerCode: group.customerCode,
                        customerName: group.customerName,
                        salesAgent: group.salesAgent,
                        debtorCode: group.customerCode,
                        DebtorCode: group.customerCode,
                        debtorName: group.customerName,
                        DebtorName: group.customerName,
                        SalesAgent: group.salesAgent,
                        location,
                        Location: location,
                        docDate,
                        remark: '',
                        assignedDriverId: group.assignedDriverId,
                        assignedDriverName: group.assignedDriverLabel,
                        assignedDriverLogin: group.assignedDriverLogin,
                        driverId: group.assignedDriverId,
                        driverName: group.assignedDriverLabel,
                        driverLogin: group.assignedDriverLogin,
                        lines: payloadLines
                    };
                    const body = {
                        type: 'DELIVERY_ORDER',
                        bulkBatchId,
                        client_request_id: normalizeBatchId(`${bulkBatchId}-${i + 1}`),
                        source: 'wp-ui',
                        assignedDriverId: group.assignedDriverId,
                        payload
                    };
                    const r = await apiPost(REST_JOB_POST, body);
                    const jobId = r.jobId || r.id;
                    if (!jobId) throw new Error(`No job ID returned for ${group.customerName || group.customerCode}`);
                    showToast('info', 'Job queued', `${group.customerName || group.customerCode} | Job #${jobId}`);

                    savedJobs.push({
                        jobId,
                        groupKey: group.key,
                        docNo: '',
                        customerCode: group.customerCode,
                        customerName: group.customerName,
                        assignedDriverLabel: group.assignedDriverLabel
                    });
                }

                showDeliverySuccessActions({
                    batchLabel: `${savedJobs.length} DO${savedJobs.length === 1 ? '' : 's'} | ${bulkBatchId}`,
                    receiptUrl: recordsUrl
                });
                showBulkSuccessModal({ count: savedJobs.length, recordsUrl });
                clearDeliveryFormAfterSave();
                saveSucceeded = true;
                submitBtn.textContent = submitDoneText();
            } catch(err) {
                if (savedJobs.length) {
                    removeSavedGroupsFromForm(savedJobs);
                }
                showBulkPartialFailureModal({
                    savedJobs,
                    recordsUrl,
                    errorMessage: err.message
                });
                submitBtn.disabled = false;
                submitBtn.textContent = submitIdleText();
            } finally {
                state.isSubmitting = false;
                if (!saveSucceeded) {
                    submitBtn.disabled = false;
                    submitBtn.textContent = submitIdleText();
                }
            }
        });
    }

    // --------------------------------------------------------------
    // BASKET RETURN MODULE (unchanged except integer qty)
    // --------------------------------------------------------------
    const basketContainer = document.getElementById('acd-resp-basket-tab');
    if (basketContainer && !basketContainer.dataset.brInit) {
        basketContainer.dataset.brInit = '1';
        const REST_NONCE = root.dataset.restNonce;
        const REST_RETURN_URL = root.dataset.restReturnPost;
        const SHOW_DEBTOR_CODE = root.dataset.showDebtorCode === '1';
        const AJAX_URL = root.dataset.ajaxUrl;
        const DEBTOR_NONCE = root.dataset.debtorNonce;

        let isSubmitting = false;
        const pickerState = { items: [], fetchFn: null, onPick: null };
        let pickerTimer = null;

        function esc(s) { return s ? String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c])) : ''; }
        function toast(icon, title, text='') { if(window.Swal) Swal.fire({ toast: true, position: 'center', icon, title, text, showConfirmButton: false, timer: 2200, timerProgressBar: true }); }
        function whole(n) { const x = Number(n); return (!isFinite(x) || x < 0) ? 0 : Math.round(x); }

        function renderPickerNote(msg) { const div = $('acd_resp_br_picker_results'); if(div) div.innerHTML = `<div class="acd-resp-picker-note">${esc(msg)}</div>`; }
        function renderPickerItems(items) {
            const box = $('acd_resp_br_picker_results');
            if (!box) return;
            if (!items.length) { renderPickerNote('No result found'); return; }
            box.innerHTML = items.map((it, idx) => `<button type="button" class="acd-	�ʯ[��h��������)h��
?���px;
    color:#64748b;
    font-size:11px;
    font-weight:800;
    word-break:break-word;
}

.wst-dod-muted,
.wst-dod-empty{
    color:var(--dod-muted);
    font-weight:800;
}

.wst-dod-empty{
    text-align:center;
    padding:22px 12px !important;
}

@media (max-width:760px){
    .wst-dod-wrap{
        padding:6px;
    }

    .wst-dod-filter-grid{
        grid-template-columns:1fr 1fr;
    }

    .wst-dod-summary{
        align-items:flex-start;
        flex-direction:column;
    }

    .wst-dod-table{
        min-width:1120px;
    }
}

@media (max-width:480px){
    .wst-dod-filter-grid{
        grid-template-columns:1fr;
    }

    .wst-dod-table{
        min-width:1080px;
    }
}
</style>)h��4�i��������+"P�
??�nrgin-top:.9rem}.acd-resp-card-header{padding:.85rem .9rem;border-bottom:1px solid var(--acd-border);background:#fcfdff;display:flex;align-items:center;justify-content:space-between;gap:.6rem}.acd-resp-card-header-stack{flex-direction:column;align-items:stretch}.acd-resp-card-header h3{margin:0;font-size:1rem;font-weight:800}.acd-resp-lines-head{display:flex;align-items:center;justify-content:space-between;width:100%}.acd-resp-lines-badge{display:inline-flex;align-items:center;justify-content:center;min-width:1.8rem;min-height:1.8rem;padding:0 .45rem;border-radius:999px;background:var(--acd-green-soft);border:1px solid #bbf7d0;color:var(--acd-green);font-size:.82rem;font-weight:800}.acd-resp-card-body{padding:.9rem}
@media (min-width:768px){#acd-resp-root .acd-resp-card-body{padding:1rem}#acd-resp-root .acd-resp-entry-card .acd-resp-card-body{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.9rem 1rem;align-items:end}#acd-resp-root .acd-resp-entry-card .acd-resp-field,#acd-resp-root .acd-resp-entry-card .acd-resp-row-2,#acd-resp-root .acd-resp-entry-card .acd-resp-preview,#acd-resp-root .acd-resp-entry-card #acd_resp_do_addline{margin-bottom:0}#acd-resp-root .acd-resp-entry-card .acd-resp-row-2,#acd-resp-root .acd-resp-entry-card .acd-resp-preview,#acd-resp-root .acd-resp-entry-card #acd_resp_do_addline{grid-column:1 / -1}}
.acd-resp-field{margin-bottom:.85rem}.acd-resp-field label{display:block;font-size:.88rem;font-weight:700;color:var(--acd-muted);margin-bottom:.35rem}.acd-resp-input{width:100%;min-height:3rem;padding:.72rem .85rem;border:1px solid var(--acd-border-strong);border-radius:.65rem;background:#fff;font-size:1rem}.acd-resp-input:focus{outline:none;border-color:var(--acd-green);box-shadow:0 0 0 .2rem rgba(22,101,52,.10)}.acd-resp-search-wrap{position:relative}.acd-resp-search-wrap .acd-resp-input{padding-right:3.1rem;cursor:pointer}.acd-resp-readonly-wrap .acd-resp-input-readonly{padding-right:.85rem;cursor:default;background:#f8fafc;color:#334155}.acd-resp-field-clear{position:absolute;top:50%;right:.5rem;transform:translateY(-50%);width:2.15rem;height:2.15rem;border:1px solid var(--acd-border);background:#fff;color:#64748b;border-radius:.5rem;display:none;align-items:center;justify-content:center;font-size:1.2rem;cursor:pointer}.acd-resp-field-clear.show{display:inline-flex}.acd-resp-field-clear:hover{background:var(--acd-green-soft);border-color:#bbf7d0;color:var(--acd-green)}
.acd-resp-type-toggle{display:flex;gap:.55rem}.acd-resp-type-btn{flex:1;min-height:3rem;padding:.7rem .8rem;border:1px solid var(--acd-border-strong);background:#f8fafc;color:#334155;border-radius:.65rem;font-weight:700;font-size:1rem;cursor:pointer}.acd-resp-type-btn:hover{background:#ecfdf3;border-color:#86efac;color:var(--acd-green)}.acd-resp-type-btn.active{background:var(--acd-green-light);border-color:#16a34a;color:var(--acd-green);box-shadow:0 0 0 1px rgba(22,101,52,.05) inset}.acd-resp-row-2{display:grid;grid-template-columns:1fr 1fr;gap:.8rem;margin-bottom:.5rem}@media(max-width:480px){.acd-resp-row-2{grid-template-columns:1fr;gap:0}.acd-resp-edit-banner{flex-direction:column}.acd-resp-edit-banner>div:last-child{text-align:left}}
.acd-resp-preview{background:var(--acd-green-soft);border:1px solid #bbf7d0;border-radius:.65rem;padding:.7rem .8rem;margin:.6rem 0;font-size:.95rem}#acd-resp-root .acd-resp-btn-primary,#acd-resp-root button.acd-resp-btn-primary{width:100%;min-height:3.05rem;padding:.78rem 1rem;border:1px solid var(--acd-green);border-radius:.7rem;background:var(--acd-green);color:#fff;font-weight:800;font-size:1rem;line-height:1.2;font-family:inherit;text-align:center;cursor:pointer;appearance:none;-webkit-appearance:none;box-shadow:none}#acd-resp-root .acd-resp-btn-primary:hover{background:var(--acd-green-dark);border-color:var(--acd-green-dark);color:#fff;box-shadow:0 4px 12px rgba(22,101,52,.14)}#acd-resp-root .acd-resp-btn-primary:disabled{background:#94a3b8;border-color:#94a3b8;cursor:not-allowed}.acd-resp-lines-header{display:none}@media(min-width:768px){.acd-resp-lines-header{display:grid;grid-template-columns:minmax(14rem,2fr) .85fr .65fr .65fr .85fr 5.5rem;gap:.45rem;align-items:center;text-align:center;background:#f1f5f9;border:1px solid var(--acd-border);border-radius:.65rem .65rem 0 0;padding:.72rem .8rem;font-size:.85rem;font-weight:800;margin-bottom:.25rem}.acd-resp-lines-header span:first-child{text-align:left}.acd-resp-lines-header,.acd-resp-line{min-width:54rem}.acd-resp-line{display:grid;grid-template-columns:minmax(14rem,2fr) .85fr .65fr .65fr .85fr 5.5rem;gap:.45rem;align-items:center;text-align:center;padding:.7rem .8rem;border-right:1px solid #eef2f6;border-left:1px solid #eef2f6;border-bottom:1px solid #eef2f6;font-size:.95rem}.acd-resp-line>div:first-child{text-align:left}}
.acd-resp-mobile-line-item{display:block;padding:.85rem;border:1px solid var(--acd-border);border-radius:.7rem;margin-bottom:.6rem;background:#fff;box-shadow:0 .45rem 1rem rgba(15,23,42,.05)}@media(min-width:768px){.acd-resp-mobile-line-item{display:none}}.acd-resp-mobile-line-top{display:flex;align-items:flex-start;justify-content:space-between;gap:.7rem;margin-bottom:.45rem}.acd-resp-mobile-line-name{font-size:.94rem;font-weight:800;word-break:break-word}.acd-resp-mobile-delete-btn{flex-shrink:0;min-height:2.35rem;padding:.5rem .8rem;background:#fff5f5;color:var(--acd-danger);border:1px solid #fecaca;border-radius:.6rem;font-size:.82rem;font-weight:800;cursor:pointer}.acd-resp-mobile-line-meta{display:grid;grid-template-columns:1fr 1fr;gap:.5rem}.acd-resp-mobile-chip{border:1px solid var(--acd-border);background:#f8fafc;border-radius:.62rem;padding:.5rem .55rem}.acd-resp-mobile-chip-label{display:block;font-size:.67rem;color:var(--acd-muted);margin-bottom:.08rem}.acd-resp-mobile-chip-value{display:block;font-size:.84rem;font-weight:800}.acd-resp-number-cell{text-align:center;font-variant-numeric:tabular-nums}.acd-resp-type-pill{display:inline-flex;padding:.3rem .7rem;border-radius:999px;background:var(--acd-green-soft);border:1px solid #bbf7d0;color:var(--acd-green);font-size:.8rem;font-weight:800}
#acd-resp-root .acd-resp-delete-btn{display:inline-flex;align-items:center;justify-content:center;width:2.4rem;min-width:2.4rem;min-height:2.35rem;padding:.45rem;border:1px solid #fecaca;background:#fff5f5;color:#dc2626;border-radius:.65rem;font-size:1rem;font-weight:700;cursor:pointer}.acd-resp-lines-container{max-height:min(32rem,64vh);overflow:auto;padding:.15rem}.acd-resp-empty{padding:1.2rem;text-align:center;color:var(--acd-muted);font-style:italic}#acd-resp-root .acd-resp-success-actions{margin-top:.75rem;padding:.85rem;border:1px solid #bbf7d0;background:var(--acd-green-soft);border-radius:.75rem}.acd-resp-success-text{font-size:.9rem;font-weight:700;color:var(--acd-green-dark);margin-bottom:.55rem}.acd-resp-success-btns{display:grid;grid-template-columns:1fr;gap:.5rem}#acd-resp-root .acd-resp-action-btn{min-height:2.8rem;padding:.7rem .8rem;border-radius:.65rem;font-size:.92rem;font-weight:800;text-align:center;text-decoration:none;display:inline-flex;align-items:center;justify-content:center}.acd-resp-action-soft{background:#fff;border:1px solid #86efac;color:var(--acd-green)}
.acd-resp-picker-modal{position:fixed;inset:0;z-index:9999;display:none;align-items:center;justify-content:center;padding:.75rem}.acd-resp-picker-modal.active{display:flex}.acd-resp-picker-backdrop{position:absolute;inset:0;background:rgba(15,23,42,.45)}.acd-resp-picker-sheet{position:relative;width:100%;max-width:min(42rem,calc(100vw - 2rem));max-height:86vh;background:#fff;border-radius:.9rem;box-shadow:0 1.4rem 2.4rem rgba(0,0,0,.18);overflow:hidden}.acd-resp-picker-head{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.85rem .95rem;border-bottom:1px solid var(--acd-border)}.acd-resp-picker-title{font-size:1.05rem;font-weight:800}.acd-resp-picker-close{width:2.35rem;height:2.35rem;padding:0;border:1px solid var(--acd-border-strong);background:#fff;color:var(--acd-text);border-radius:.55rem;display:inline-flex;align-items:center;justify-content:center;line-height:1;font-size:1.35rem;cursor:pointer}.acd-resp-picker-body{padding:.85rem .95rem .95rem;display:flex;flex-direction:column;gap:.6rem}.acd-resp-picker-results{max-height:min(24rem,calc(86vh - 9rem));overflow-y:auto}.acd-resp-picker-item{display:block;width:100%;text-align:left;min-height:3rem;padding:.78rem .85rem;border:1px solid var(--acd-border);border-radius:.65rem;background:#fff;margin-bottom:.5rem;cursor:pointer;color:var(--acd-text)}.acd-resp-picker-item:hover{background:var(--acd-green-soft);border-color:#bbf7d0}.acd-resp-picker-item-main{display:block;font-weight:800;color:var(--acd-text)}.acd-resp-picker-item-sub{display:block;font-size:.8rem;color:var(--acd-muted)}.acd-resp-picker-note{padding:.9rem;text-align:center;color:var(--acd-muted);font-weight:800}
.acd-resp-dialog{position:fixed;inset:0;z-index:10000;display:flex;align-items:center;justify-content:center;padding:1rem}.acd-resp-dialog-backdrop{position:absolute;inset:0;background:rgba(15,23,42,.62)}.acd-resp-dialog-card{position:relative;width:100%;max-width:34rem;max-height:min(86vh,44rem);display:flex;flex-direction:column;background:#fff;border-radius:.9rem;box-shadow:0 1.4rem 3rem rgba(15,23,42,.28);overflow:hidden}.acd-resp-dialog-head{display:flex;align-items:flex-start;gap:.75rem;padding:1rem 1rem .75rem;border-bottom:1px solid #dbe4ee}.acd-resp-dialog-icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 2.25rem;width:2.25rem;height:2.25rem;border-radius:999px;background:#f0fdf4;color:#166534;font-weight:900}.acd-resp-dialog-icon-warning{background:#fef3c7;color:#92400e}.acd-resp-dialog-icon-error{background:#fee2e2;color:#991b1b}.acd-resp-dialog-title{margin:.15rem 0 0;font-size:1.1rem;line-height:1.25;font-weight:900;color:#0f172a}.acd-resp-dialog-body{padding:1rem;overflow:auto;color:#0f172a;font-size:.95rem;line-height:1.45}.acd-resp-dialog-body p{margin:.4rem 0 .75rem}.acd-resp-dialog-actions{display:flex;justify-content:flex-end;gap:.65rem;padding:.85rem 1rem 1rem;border-top:1px solid #dbe4ee;background:#f8fafc}.acd-resp-dialog-btn{min-height:2.65rem;padding:.65rem 1rem;border-radius:.65rem;border:1px solid #c4d0dd;font-weight:900;cursor:pointer}.acd-resp-dialog-btn-primary{background:#166534;border-color:#166534;color:#fff}.acd-resp-dialog-btn-primary:hover{background:#14532d;border-color:#14532d;color:#fff}.acd-resp-dialog-btn-secondary{background:#fff;color:#334155}.acd-resp-dialog-btn-secondary:hover{background:#f1f5f9;color:#0f172a}@media(max-width:480px){.acd-resp-dialog-actions{flex-direction:column-reverse}.acd-resp-dialog-btn{width:100%}}
.acd-resp-dialog .acd-resp-dialog-btn,
.acd-resp-dialog button.acd-resp-dialog-btn{
    min-height:2.8rem !important;
    padding:.72rem 1rem !important;
    border-radius:.7rem !important;
    font-family:inherit !important;
    font-size:.96rem !important;
    font-weight:900 !important;
    line-height:1.2 !important;
    text-align:center !important;
    text-decoration:none !important;
    opacity:1 !important;
    cursor:pointer !important;
    appearance:none !important;
    -webkit-appearance:none !important;
    box-shadow:none !important;
    transition:background .18s ease,border-color .18s ease,box-shadow .18s ease,color .18s ease !important;
}
.acd-resp-dialog .acd-resp-dialog-btn-primary,
.acd-resp-dialog button.acd-resp-dialog-btn-primary{
    background:#166534 !important;
    border:1px solid #166534 !important;
    color:#fff !important;
}
.acd-resp-dialog .acd-resp-dialog-btn-primary:hover,
.acd-resp-dialog button.acd-resp-dialog-btn-primary:hover{
    background:#14532d !important;
    border-color:#14532d !important;
    color:#fff !important;
    box-shadow:0 4px 12px rgba(22,101,52,.14) !important;
}
.acd-resp-dialog .acd-resp-dialog-btn-primary:focus,
.acd-resp-dialog button.acd-resp-dialog-btn-primary:focus{
    outline:none !important;
    color:#fff !important;
    box-shadow:0 0 0 .2rem rgba(22,101,52,.18) !important;
}
.acd-resp-dialog .acd-resp-dialog-btn-secondary,
.acd-resp-dialog button.acd-resp-dialog-btn-secondary{
    background:#fff !important;
    border:1px solid #c4d0dd !important;
    color:#334155 !important;
}
.acd-resp-dialog .acd-resp-dialog-btn-secondary:hover,
.acd-resp-dialog button.acd-resp-dialog-btn-secondary:hover,
.acd-resp-dialog .acd-resp-dialog-btn-secondary:focus,
.acd-resp-dialog button.acd-resp-dialog-btn-secondary:focus{
    background:#f1f5f9 !important;
    border-color:#94a3b8 !important;
    color:#0f172a !important;
    outline:none !important;
}
</style>
<script>
(function(){
    const root = document.getElementById('acd-resp-root');
    if (!root) return;
    const $ = id => document.getElementById(id);
    const esc = s => String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[c]));
    const AJAX_URL = root.dataset.ajaxUrl || '';
    const ITEM_NONCE = root.dataset.itemNonce || '';
    let DRIVER_ITEMS = [];
    try { DRIVER_ITEMS = JSON.parse(root.dataset.drivers || '[]'); } catch(e) { DRIVER_ITEMS = []; }
    let pickerTimer = null;
    const pickerState = { items: [], defaultItems: [], fetchFn: null, onPick: null };
    const state = { lines: [], editingIndex: -1, isSubmitting: false };
    try { state.lines = JSON.parse($('acd_resp_existing_lines_json')?.textContent || '[]'); } catch(e) { state.lines = []; }

    function fmtQty(n){ const x = Number(n); return (!isFinite(x)) ? '0' : String(Math.round(x)); }
    function fmtKg(n){ const x = Number(n); return (!isFinite(x)) ? '0.00' : x.toFixed(2); }
    function parseQty(n){ const x = Number(n); return (!isFinite(x) || x < 0) ? 0 : Math.round(x); }
    function parseKg(n){ const x = Number(n); return (!isFinite(x) || x < 0) ? 0 : Number(x.toFixed(2)); }
    function calcTotalKg(qty, kg){ return Number(((parseQty(qty) || 0) * (parseKg(kg) || 0)).toFixed(2)); }
    function toast(icon,title,text=''){ if(window.Swal) Swal.fire({toast:true,position:'center',icon,title,text,showConfirmButton:false,timer:2400,timerProgressBar:true}); }
    function localDialog(opts){
        return new Promise(resolve => {
            const icon = String(opts.icon || 'info').toLowerCase();
            const overlay = document.createElement('div');
            overlay.className = 'acd-resp-dialog';
            overlay.setAttribute('role', 'dialog');
            overlay.setAttribute('aria-modal', 'true');
            overlay.innerHTML = `
                <div class="acd-resp-dialog-backdrop" data-dialog-cancel="1"></div>
                <div class="acd-resp-dialog-card">
                    <div class="acd-resp-dialog-head">
                        <span class="acd-resp-dialog-icon acd-resp-dialog-icon-${esc(icon)}">${icon === 'warning' ? '!' : icon === 'error' ? 'x' : '?'}</span>
                        <h3 class="acd-resp-dialog-title">${esc(opts.title || 'Confirm')}</h3>
                    </div>
                    <div class="acd-resp-dialog-body">${opts.html || esc(opts.text || '')}</div>
                    <div class="acd-resp-dialog-actions">
                        ${opts.showCancel ? `<button type="button" class="acd-resp-dialog-btn acd-resp-dialog-btn-secondary" data-dialog-cancel="1">${esc(opts.cancelText || 'Cancel')}</button>` : ''}
                        <button type="button" class="acd-resp-dialog-btn acd-resp-dialog-btn-primary" data-dialog-confirm="1">${esc(opts.confirmText || 'OK')}</button>
                    </div>
                </div>`;
            const close = value => {
                overlay.remove();
                document.removeEventListener('keydown', onKeydown);
                resolve(value);
            };
            const onKeydown = event => {
                if (event.key === 'Escape') close(false);
            };
            overlay.addEventListener('click', event => {
                if (event.target.closest('[data-dialog-confirm]')) close(true);
                if (event.target.closest('[data-dialog-cancel]')) close(false);
            });
            document.addEventListener('keyd+"P�͋j��������S�|
??�k            $remaining_rows = max(0, $minimum_rows - count($lines));

                for ($i = 0; $i < $remaining_rows; $i++):
                ?>
                    <tr>
                        <td class="ac-do-qty">&nbsp;</td>
                        <td class="ac-do-kg"></td>
                        <td class="ac-do-desc"></td>
                        <td class="ac-do-pack">
                            <span class="ac-do-checkbox"></span>Ctn
                            &nbsp;
                            <span class="ac-do-checkbox"></span>Bsk
                        </td>
                        <td class="ac-do-total"></td>
                    </tr>
                <?php endfor; ?>
            </tbody>
        </table>

        <div class="ac-do-bottom-area">
            <div>
                <div class="ac-do-slogan">We Do The EXCELLENT Way</div>

                <?php if ($remark !== ''): ?>
                    <div class="ac-do-remark">
                        <strong>Remark:</strong> <?php echo ac_do_h($remark); ?>
                    </div>
                <?php endif; ?>
            </div>

            <div class="ac-do-totals">
                <div class="ac-do-total-label">总箱<br>Total Ctn</div>
                <div class="ac-do-total-box"><?php echo ac_do_h(ac_do_num($total_ctn)); ?></div>

                <div class="ac-do-total-label">总篮<br>Total Bsk</div>
                <div class="ac-do-total-box"><?php echo ac_do_h(ac_do_num($total_bsk)); ?></div>

                <div class="ac-do-total-label">总公斤<br>Total Kg</div>
                <div class="ac-do-total-box"><?php echo ac_do_h(ac_do_num($total_kg)); ?></div>
            </div>
        </div>

        <div class="ac-do-signature-row">
            <div class="ac-do-signature">经手人 Issued by</div>
            <div class="ac-do-signature">收货人 Received by</div>
        </div>
    </div>
    </div>

    <div class="ac-do-preview-page">
    <div class="ac-do-proof-paper">
        <div class="ac-proof-title-row">
            <div class="ac-proof-title">
                <h2>Proof of Delivery</h2>
                <p><?php echo ac_do_h($company_name); ?></p>
            </div>

            <div class="ac-proof-meta">
                <strong>DO No:</strong> <?php echo ac_do_h($doc_no); ?><br>
                <strong>Customer:</strong> <?php echo ac_do_h($customer_name); ?><br>
                <strong>Date:</strong> <?php echo ac_do_h($display_date); ?>
            </div>
        </div>

        <div class="ac-proof-image-box">
            <?php if ($proof_url !== ''): ?>
                <img
                    src="<?php echo esc_url($proof_url); ?>"
                    alt="Proof of Delivery"
                    loading="eager"
                    decoding="sync"
                >
            <?php else: ?>
                <div class="ac-proof-empty">
                    No proof of delivery image uploaded yet.
                </div>
            <?php endif; ?>
        </div>

        <div class="ac-proof-footer">
            <div class="ac-proof-footer-line">Driver / Issued by</div>
            <div class="ac-proof-footer-line">Customer / Received by</div>
        </div>
    </div>
    </div>
</div>

<script>
var acDoPdfFileName = <?php echo wp_json_encode($pdf_file_name); ?>;
var acDoPdfData = <?php echo wp_json_encode($pdf_payload); ?>;
var acDoJsPdfPromise = null;

function acDoWaitImage(img) {
    return new Promise(function(resolve) {
        if (!img) {
            resolve();
            return;
        }

        if (img.complete && img.naturalWidth > 0) {
            resolve();
            return;
        }

        var done = false;

        function finish() {
            if (done) return;
            done = true;
            resolve();
        }

        img.onload = finish;
        img.onerror = finish;

        setTimeout(finish, 4000);
    });
}

function acDoLoadJsPdf() {
    if (window.jspdf && window.jspdf.jsPDF) {
        return Promise.resolve(window.jspdf.jsPDF);
    }

    if (acDoJsPdfPromise) {
        return acDoJsPdfPromise;
    }

    acDoJsPdfPromise = new Promise(function(resolve, reject) {
        var script = document.createElement('script');
        script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
        script.async = true;
        script.onload = function() {
            if (window.jspdf && window.jspdf.jsPDF) {
                resolve(window.jspdf.jsPDF);
                return;
            }

            reject(new Error('jsPDF library did not load.'));
        };
        script.onerror = function() {
            reject(new Error('PDF library could not be loaded.'));
        };
        document.head.appendChild(script);
    });

    return acDoJsPdfPromise;
}

function acDoCanvas(width, height) {
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');

    canvas.width = width;
    canvas.height = height;
    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = 'high';

    return { canvas: canvas, ctx: ctx };
}

function acDoText(ctx, text, x, y, size, color, weight, align) {
    ctx.fillStyle = color || '#111111';
    ctx.font = (weight || '400') + ' ' + size + 'px Arial, Helvetica, sans-serif';
    ctx.textAlign = align || 'left';
    ctx.textBaseline = 'alphabetic';
    ctx.fillText(String(text || ''), x, y);
}

function acDoLine(ctx, x1, y1, x2, y2, color, width) {
    ctx.strokeStyle = color || '#333333';
    ctx.lineWidth = width || 1;
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.stroke();
}

function acDoRect(ctx, x, y, width, height, color, lineWidth) {
    ctx.strokeStyle = color || '#333333';
    ctx.lineWidth = lineWidth || 1;
    ctx.strokeRect(x, y, width, height);
}

function acDoFillRect(ctx, x, y, width, height, color) {
    ctx.fillStyle = color;
    ctx.fillRect(x, y, width, height);
}

function acDoWrap(ctx, text, x, y, maxWidth, lineHeight, size, color, weight, maxLines) {
    var words = String(text || '').split(/\s+/);
    var line = '';
    var currentY = y;
    var lines = [];
    var i;
    var test;

    ctx.fillStyle = color || '#111111';
    ctx.font = (weight || '400') + ' ' + size + 'px Arial, Helvetica, sans-serif';
    ctx.textAlign = 'left';
    ctx.textBaseline = 'alphabetic';

    for (i = 0; i < words.length; i++) {
        test = line ? line + ' ' + words[i] : words[i];

        if (ctx.measureText(test).width > maxWidth && line !== '') {
            lines.push(line);
            line = words[i];
        } else {
            line = test;
        }
    }

    if (line) {
        lines.push(line);
    }

    if (maxLines && lines.length > maxLines) {
        lines = lines.slice(0, maxLines);
        while (lines[lines.length - 1] && ctx.measureText(lines[lines.length - 1] + '...').width > maxWidth) {
            lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
        }

        lines[lines.length - 1] = lines[lines.length - 1] + '...';
    }

    for (i = 0; i < lines.length; i++) {
        ctx.fillText(lines[i], x, currentY + (i * lineHeight));
    }
}

function acDoLoadCanvasImage(url) {
    return new Promise(function(resolve) {
        if (!url) {
            resolve(null);
            return;
        }

        var img = new Image();
        var done = false;

        function finish(result) {
            if (done) return;
            done = true;
            resolve(result);
        }

        img.crossOrigin = 'anonymous';
        img.onload = function() {
            finish(img);
        };
        img.onerror = function() {
            finish(null);
        };
        img.src = url;

        setTimeout(function() {
            finish(null);
        }, 4000);
    });
}

function acDoDrawContainImage(ctx, img, x, y, maxWidth, maxHeight) {
    var ratio;
    var width;
    var height;

    if (!img || !img.naturalWidth || !img.naturalHeight) {
        return false;
    }

    ratio = Math.min(maxWidth / img.naturalWidth, maxHeight / img.naturalHeight);
    width = img.naturalWidth * ratio;
    height = img.naturalHeight * ratio;

    ctx.drawImage(img, x + ((maxWidth - width) / 2), y + ((maxHeight - height) / 2), width, height);
    return true;
}

function acDoDrawCheckbox(ctx, x, y, checked) {
    acDoRect(ctx, x, y, 14, 14, '#222222', 1.5);

    if (checked) {
        acDoText(ctx, '\u2713', x + 1, y + 13, 21, '#111111', '700');
    }
}

function acDoDrawReceiptCanvas(logoImage) {
    var out = acDoCanvas(1240, 1754);
    var canvas = out.canvas;
    var ctx = out.ctx;
    var data = acDoPdfData || {};
    var lines = Array.isArray(data.lines) ? data.lines : [];
    var pageX = 106;
    var pageY = 58;
    var pageW = 1028;
    var pageH = 1638;
    var tableX = 160;
    var tableY = 360;
    var rowH = 47;
    var col = [tableX, tableX + 105, tableX + 210, tableX + 680, tableX + 830, tableX + 930];
    var minRows = Math.max(14, lines.length);
    var i;
    var y;
    var item;

    acDoFillRect(ctx, 0, 0, canvas.width, canvas.height, '#ffffff');
    acDoFillRect(ctx, pageX, pageY, pageW, pageH, '#ffffff');
    acDoRect(ctx, pageX, pageY, pageW, pageH, '#d1d5db', 1);

    if (!acDoDrawContainImage(ctx, logoImage, pageX + 44, pageY + 74, 180, 118)) {
        acDoText(ctx, 'Vege', pageX + 86, pageY + 154, 44, '#164f38', '700');
    }
    acDoText(ctx, data.companyName || '', pageX + 240, pageY + 125, 38, '#0f172a', '900');
    acDoText(ctx, data.companyAddr || '', pageX + 242, pageY + 157, 13, '#111111', '400');
    acDoText(ctx, 'H/P: ' + (data.companyTel || ''), pageX + 242, pageY + 181, 13, '#111111', '700');

    acDoFillRect(ctx, pageX + 785, pageY + 36, 230, 36, '#444444');
    acDoText(ctx, 'DELIVERY ORDER', pageX + 900, pageY + 62, 18, '#ffffff', '700', 'center');
    acDoText(ctx, 'No', pageX + 805, pageY + 190, 20, '#111111', '400');
    acDoText(ctx, data.docNo || '', pageX + 835, pageY + 190, 27, '#ef4444', '700');

    acDoText(ctx, 'Customer', pageX + 63, pageY + 250, 18, '#111111', '700');
    acDoText(ctx, (data.customerName || '') + (data.customerCode ? ' (' + data.customerCode + ')' : ''), pageX + 170, pageY + 250, 18, '#111111', '400');
    acDoLine(ctx, pageX + 150, pageY + 260, pageX + 650, pageY + 260, '#666666', 1);
    acDoText(ctx, 'Date', pageX + 770, pageY + 250, 18, '#111111', '700');
    acDoText(ctx, data.displayDate || '', pageX + 835, pageY + 250, 18, '#111111', '400');
    acDoLine(ctx, pageX + 830, pageY + 260, pageX + 980, pageY + 260, '#666666', 1);

    acDoRect(ctx, tableX, tableY, col[5] - col[0], rowH * (minRows + 1), '#333333', 1.2);
    for (i = 1; i < col.length - 1; i++) {
        acDoLine(ctx, col[i], tableY, col[i], tableY + rowH * (minRows + 1), '#333333', 1);
    }
    for (i = 1; i <= minRows + 1; i++) {
        acDoLine(ctx, tableX, tableY + rowH * i, col[5], tableY + rowH * i, '#333333', 1);
    }

    acDoText(ctx, '\u6570\u91cf', tableX + 55, tableY + 20, 15, '#111111', '700', 'center');
    acDoText(ctx, 'Quantity', tableX + 55, tableY + 39, 13, '#111111', '700', 'center');
    acDoText(ctx, '\u516c\u65a4', col[1] + 55, tableY + 20, 15, '#111111', '700', 'center');
    acDoText(ctx, 'Kg', col[1] + 55, tableY + 39, 13, '#111111', '700', 'center');
    acDoText(ctx, '\u8d27\u7269\u540d\u79f0', col[2] + 235, tableY + 20, 15, '#111111', '700', 'center');
    acDoText(ctx, 'Description', col[2] + 235, tableY + 39, 13, '#111111', '700', 'center');
    acDoText(ctx, '\u7bb1 / \u7bee', col[3] + 75, tableY + 20, 15, '#111111', '700', 'center');
    acDoText(ctx, 'Box / Basket', col[3] + 75, tableY + 39, 13, '#111111', '700', 'center');
    acDoText(ctx, '\u603b\u516c\u65a4', col[4] + 55, tableY + 20, 15, '#111111', '700', 'center');
    acDoText(ctx, 'Total Kg', col[4] + 55, tableY + 39, 13, '#111111', '700', 'center');

    for (i = 0; i < minRows; i++) {
        y = tableY + rowH * (i + 1);
        item = lines[i] || {};

        acDoText(ctx, item.qty || '', tableX + 55, y + 30, 18, '#111111', '400', 'center');
        acDoText(ctx, item.kg || '', col[1] + 55, y + 30, 18, '#111111', '400', 'center');
        acDoWrap(ctx, item.description || '', col[2] + 10, y + 22, 450, 18, 18, '#111111', '400', 2);
        acDoDrawCheckbox(ctx, col[3] + 25, y + 16, !!item.isCtn);
        acDoText(ctx, 'Ctn', col[3] + 43, y + 29, 14, '#111111', '400');
        acDoDrawCheckbox(ctx, col[3] + 86, y + 16, !!item.isBsk);
        acDoText(ctx, 'Bsk', col[3] + 104, y + 29, 14, '#111111', '400');
        acDoText(ctx, item.totalKg || '', col[4] + 55, y + 30, 18, '#111111', '400', 'center');
    }

    y = tableY + rowH * (minRows + 1) + 35;
    acDoText(ctx, 'We Do The EXCELLENT Way', tableX, y + 25, 23, '#111111', '700');

    if (data.remark) {
        acDoText(ctx, 'Remark: ' + data.remark, tableX, y + 65, 16, '#111111', '700');
    }

    var totalX = pageX + 705;
    var totalY = y;
    var totalRows = [
        ['\u603b\u7bb1', 'Total Ctn', data.totalCtn || ''],
        ['\u603b\u7bee', 'Total Bsk', data.totalBsk || ''],
        ['\u603b\u516c\u65a4', 'Total Kg', data.totalKg || '']
    ];

    for (i = 0; i < totalRows.length; i++) {
        acDoText(ctx, totalRows[i][0], totalX, totalY + 18 + i * 48, 18, '#111111', '700', 'right');
        acDoText(ctx, totalRows[i][1], totalX, totalY + 38 + i * 48, 16, '#111111', '400', 'right');
        acDoFillRect(ctx, totalX + 20, totalY + 4 + i * 48, 100, 37, 'rgba(255,255,255,0.25)');
        acDoRect(ctx, totalX + 20, totalY + 4 + i * 48, 100, 37, '#333333', 1);
        acDoText(ctx, totalRows[i][2], totalX + 70, totalY + 30 + i * 48, 18, '#111111', '700', 'center');
    }

    acDoLine(ctx, tableX, pageY + pageH - 140, tableX + 260, pageY + pageH - 140, '#555555', 1);
    acDoLine(ctx, pageX + pageW - 420, pageY + pageH - 140, pageX + pageW - 160, pageY + pageH - 140, '#555555', 1);
    acDoText(ctx, '\u7ecf\u624b\u4eba Issued by', tableX, pageY + pageH - 110, 17, '#111111', '400');
    acDoText(ctx, '\u6536\u8d27\u4eba Received by', pageX + pageW - 420, pageY + pageH - 110, 17, '#111111', '400');

    return canvas;
}

function acDoDrawProofCanvas() {
    var out = acDoCanvas(1240, 1754);
    var canvas = out.canvas;
    var ctx = out.ctx;
    var data = acDoPdfData || {};
    var pageX = 106;
    var pageY = 58;
    var pageW = 1028;
    var pageH = 1638;

    acDoFillRect(ctx, 0, 0, canvas.width, canvas.height, '#ffffff');
    acDoFillRect(ctx, pageX, pageY, pageW, pageH, '#ffffff');
    acDoRect(ctx, pageX, pageY, pageW, pageH, '#d1d5db', 1);

    acDoText(ctx, 'Proof of Delivery', pageX + 58, pageY + 105, 36, '#111111', '700');
    acDoText(ctx, data.companyName || '', pageX + 58, pageY + 142, 18, '#111111', '400');
    acDoText(ctx, 'DO No: ' + (data.docNo || ''), pageX + pageW - 60, pageY + 100, 18, '#111111', '700', 'right');
    acDoText(ctx, 'Customer: ' + (data.customerName || ''), pageX + pageW - 60, pageY + 132, 18, '#111111', '400', 'right');
    acDoText(ctx, 'Date: ' + (data.displayDate || ''), pageX + pageW - 60, pageY + 164, 18, '#111111', '400', 'right');
    acDoLine(ctx, pageX + 58, pageY + 190, pageX + pageW - 58, pageY + 190, '#333333', 3);

    acDoRect(ctx, pageX + 58, pageY + 240, pageW - 116, 1210, '#333333', 1.5);
    acDoText(ctx, 'No proof of delivery image uploaded yet.', pageX + pageW / 2, pageY + 850, 24, '#555555', '700', 'center');

    acDoLine(ctx, pageX + 58, pageY + pageH - 120, pageX + 410, pageY + pageH - 120, '#555555', 1);
    acDoLine(ctx, pageX + pageW - 410, pageY + pageH - 120, pageX + pageW - 58, pageY + pageH - 120, '#555555', 1);
    acDoText(ctx, 'Driver / Issued by', pageX + 58, pageY + pageH - 90, 17, '#111111', '400');
    acDoText(ctx, 'Customer / Received by', pageX + pageW - 410, pageY + pageH - 90, 17, '#111111', '400');

    return canvas;
}

function acDoBuildPdfBlob() {
S�|����k��������S�|
?����
    return acDoLoadJsPdf()
        .then(function(jsPDF) {
            return Promise.all([
                Promise.resolve(jsPDF),
                acDoLoadCanvasImage(acDoPdfData.companyLogoUrl)
            ]);
        })
        .then(function(result) {
            var jsPDF = result[0];
            var logoImage = result[1];
            var pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a5' });
            var pageOne = acDoDrawReceiptCanvas(logoImage);

            pdf.addImage(pageOne.toDataURL('image/jpeg', 0.95), 'JPEG', 0, 0, 148, 210);
            pdf.addPage();
            pdf.addImage(acDoDrawProofCanvas().toDataURL('image/jpeg', 0.95), 'JPEG', 0, 0, 148, 210);

            return pdf.output('blob');
        });
}

function acDoDownloadBlob(blob) {
    var url = URL.createObjectURL(blob);
    var link = document.createElement('a');

    link.href = url;
    link.download = acDoPdfFileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);

    setTimeout(function() {
        URL.revokeObjectURL(url);
    }, 1000);
}

function acDoSharePdf(button) {
    var originalText = button ? button.textContent : '';
    var images = document.querySelectorAll('.ac-do-proof-paper img');
    var waits = [];

    if (!navigator.share) {
        alert('This browser does not support the native share interface. Please use Print / Save PDF, then share the saved PDF in WhatsApp.');
        return;
    }

    images.forEach(function(img) {
        waits.push(acDoWaitImage(img));
    });

    if (button) {
        button.disabled = true;
        button.textContent = 'Preparing PDF...';
    }

    Promise.all(waits)
        .then(acDoBuildPdfBlob)
        .then(function(blob) {
            var file = new File([blob], acDoPdfFileName, { type: 'application/pdf' });
            var shareData = {
                title: acDoPdfFileName.replace(/\.pdf$/i, ''),
                text: 'Delivery Order PDF',
                files: [file]
            };

            if (!navigator.canShare || !navigator.canShare({ files: [file] })) {
                acDoDownloadBlob(blob);
                alert('PDF downloaded. This browser cannot share PDF files directly, so please attach the downloaded PDF in WhatsApp.');
                return null;
            }

            return navigator.share(shareData);
        })
        .catch(function(error) {
            if (error && error.name === 'AbortError') {
                return;
            }

            alert('Unable to prepare the PDF for sharing. Please use Print / Save PDF, then share the saved PDF in WhatsApp.');
        })
        .finally(function() {
            if (button) {
                button.disabled = false;
                button.textContent = originalText || 'Share PDF';
            }
        });
}

function acDoPrintWithImages() {
    var images = document.querySelectorAll('.ac-do-proof-paper img');
    var waits = [];

    images.forEach(function(img) {
        waits.push(acDoWaitImage(img));
    });

    Promise.all(waits).then(function() {
        setTimeout(function() {
            window.print();
        }, 300);
    });
}
</script>S�|��Kl��������+��b
??�qt(new Error('PDF library could not be loaded.'));
      document.head.appendChild(script);
    });

    return brJsPdfPromise;
  }

  function loadCanvasImage(url){
    if (!url) return Promise.resolve(null);
    return new Promise(resolve => {
      const img = new Image();
      img.crossOrigin = 'anonymous';
      img.onload = () => resolve(img);
      img.onerror = () => resolve(null);
      img.src = url;
    });
  }

  function drawCanvasText(ctx, text, x, y, size=18, color='#111', weight='400', align='left'){
    ctx.fillStyle = color;
    ctx.font = `${weight} ${size}px Arial, sans-serif`;
    ctx.textAlign = align;
    ctx.textBaseline = 'top';
    ctx.fillText(String(text || ''), x, y);
  }

  function drawWrappedCanvasText(ctx, text, x, y, maxWidth, lineHeight, size=18, color='#111', weight='400'){
    const words = String(text || '').split(/\s+/).filter(Boolean);
    let line = '';

    words.forEach(word => {
      const testLine = line ? `${line} ${word}` : word;
      if (ctx.measureText(testLine).width > maxWidth && line) {
        drawCanvasText(ctx, line, x, y, size, color, weight);
        line = word;
        y += lineHeight;
      } else {
        line = testLine;
      }
    });

    if (line) drawCanvasText(ctx, line, x, y, size, color, weight);
    return y + lineHeight;
  }

  function drawCanvasImageContained(ctx, image, x, y, maxW, maxH){
    if (!image) return;
    const ratio = Math.min(maxW / image.width, maxH / image.height);
    const imgW = image.width * ratio;
    const imgH = image.height * ratio;
    ctx.drawImage(image, x + (maxW - imgW) / 2, y + (maxH - imgH) / 2, imgW, imgH);
  }

  function makeReceiptCanvas(receipt, proofImage=null, logoImage=null){
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = 1240;
    canvas.height = 1754;

    ctx.fillStyle = '#ffffff';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.strokeStyle = '#111111';
    ctx.lineWidth = 2;
    ctx.strokeRect(82, 70, 1076, 1614);

    if (logoImage) {
      drawCanvasImageContained(ctx, logoImage, 138, 112, 360, 128);
    } else {
      drawCanvasText(ctx, 'BASKET RETURN', 140, 130, 30, '#111', '900');
    }

    drawCanvasText(ctx, 'BASKET RETURN', 1100, 130, 24, '#111', '900', 'right');
    drawCanvasText(ctx, receipt.ref || ('BR-' + receipt.id), 1100, 172, 22, '#111', '900', 'right');

    ctx.strokeStyle = '#111111';
    ctx.lineWidth = 4;
    ctx.beginPath();
    ctx.moveTo(140, 278);
    ctx.lineTo(1100, 278);
    ctx.stroke();

    let y = 350;
    drawCanvasText(ctx, 'Customer', 160, y, 22, '#555', '700');
    drawWrappedCanvasText(ctx, receipt.customerName || 'Customer', 160, y + 34, 410, 30, 24, '#111', '800');
    drawCanvasText(ctx, 'Driver', 660, y, 22, '#555', '700');
    drawWrappedCanvasText(ctx, receipt.driverName || '', 660, y + 34, 410, 30, 24, '#111', '800');

    y += 105;
    ctx.strokeStyle = '#e5e7eb';
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(160, y);
    ctx.lineTo(1080, y);
    ctx.stroke();

    drawCanvasText(ctx, `${receipt.qty} BASKETS`, 620, y + 54, 74, '#111', '900', 'center');
    y += 210;

    ctx.strokeStyle = '#cbd5e1';
    ctx.setLineDash([12, 10]);
    ctx.strokeRect(160, y, 920, 560);
    ctx.setLineDash([]);

    if (proofImage) {
      drawCanvasText(ctx, 'Image Proof', 620, y + 28, 22, '#334155', '800', 'center');
      drawCanvasImageContained(ctx, proofImage, 210, y + 82, 820, 420);
    } else {
      drawCanvasText(ctx, 'No image proof uploaded', 620, y + 255, 28, '#64748b', '800', 'center');
    }

    return canvas;
  }

  function buildBasketReceiptPdf(receipt){
    return Promise.all([loadJsPdf(), loadCanvasImage(receipt.proofUrl), loadCanvasImage(RECEIPT_LOGO_URL)])
      .then(([jsPDF, proofImage, logoImage]) => {
        const warnings = [];
        if (receipt.proofUrl && !proofImage) warnings.push('Proof image could not be included in the PDF.');
        if (RECEIPT_LOGO_URL && !logoImage) warnings.push('Logo could not be included in the PDF.');
        if (warnings.length && window.Swal && typeof Swal.fire === 'function') {
          Swal.fire({ icon:'warning', title:'PDF image warning', text:warnings.join(' ') });
        }
        const pdf = new jsPDF({orientation:'portrait', unit:'mm', format:'a5'});
        const canvas = makeReceiptCanvas(receipt, proofImage, logoImage);
        pdf.addImage(canvas.toDataURL('image/jpeg', 0.95), 'JPEG', 0, 0, 148, 210);
        return pdf.output('blob');
      });
  }

  function downloadBlob(blob, fileName){
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  }

  function preloadReceiptImage(url){
    if (!url) return Promise.resolve();

    return new Promise(resolve => {
      const img = new Image();
      const done = () => resolve();
      img.onload = done;
      img.onerror = done;
      img.src = url;

      if (img.complete) resolve();
      setTimeout(done, 2500);
    });
  }

  function waitForElementImages(el){
    const images = Array.from(el.querySelectorAll('img'));
    if (!images.length) return Promise.resolve();

    return Promise.all(images.map(img => new Promise(resolve => {
      if (img.complete && img.naturalWidth > 0) {
        resolve();
        return;
      }

      const done = () => resolve();
      img.addEventListener('load', done, { once:true });
      img.addEventListener('error', done, { once:true });
      setTimeout(done, 2500);
    }))).then(() => undefined);
  }

  function printCurrentBasketReceipt(){
    const receipt = currentReceiptForShare;
    if (!receipt) {
      showError('Receipt not found', 'Please reopen the basket receipt and try again.');
      return;
    }

    buildBasketReceiptPdf(receipt)
      .then(blob => {
        const fileName = receiptFileName(receipt);
        const url = URL.createObjectURL(blob);
        const opened = window.open(url, '_blank', 'noopener');

        if (!opened) {
          downloadBlob(blob, fileName);
          if (window.Swal && typeof Swal.fire === 'function') {
            Swal.fire({ icon:'info', title:'PDF downloaded', text:'Open the downloaded PDF to print or share.' });
          }
        }

        setTimeout(() => URL.revokeObjectURL(url), 60000);
      })
      .catch(() => {
        showError('Unable to prepare PDF', 'Please try again.');
      });
  }

  function shareCurrentBasketReceipt(button){
    const receipt = currentReceiptForShare;
    if (!receipt) {
      showError('Receipt not found', 'Please reopen the basket receipt and try again.');
      return;
    }

    const originalText = button ? button.textContent : '';
    if (button) {
      button.disabled = true;
      button.textContent = 'Preparing...';
    }

    buildBasketReceiptPdf(receipt)
      .then(blob => {
        const fileName = receiptFileName(receipt);
        const file = new File([blob], fileName, {type:'application/pdf'});

        if (!navigator.share || !navigator.canShare || !navigator.canShare({files:[file]})) {
          downloadBlob(blob, fileName);
          if (window.Swal && typeof Swal.fire === 'function') {
            Swal.fire({ icon:'info', title:'PDF downloaded', text:'Attach the downloaded PDF in WhatsApp.' });
          }
          return null;
        }

        return navigator.share({
          title: fileName.replace(/\.pdf$/i, ''),
          text: 'Basket Return PDF',
          files: [file]
        });
      })
      .catch(error => {
        if (error && error.name === 'AbortError') return;
        showError('Unable to prepare PDF', 'Please print or save PDF, then share it in WhatsApp.');
      })
      .finally(() => {
        if (button) {
          button.disabled = false;
          button.textContent = originalText || 'Share PDF';
        }
      });
  }

  function markSelectedDebtorRow(debtorCode){
    wrap.querySelectorAll('[data-summary-row="1"]').forEach(row => {
      row.classList.toggle('bs-row-selected', (row.dataset.debtorCode || '') === debtorCode);
    });
  }

  async function loadSummary(){
    if (!REST_SUMMARY_URL) {
      showError('Missing configuration', 'Summary endpoint missing.');
      return;
    }

    const rangeError = dateRangeError();
    if (rangeError) {
      showError('Invalid date range', rangeError);
      return;
    }

    const requestSeq = ++loadSummarySeq;
    setSummaryLoading(true, 'Loading basket summary...');

    try {
      let rows = [];
      if (selectedDebtors.length > 1) {
        const selectedResults = await Promise.all(selectedDebtors.map(debtor => apiGet(buildSummaryUrl(debtor))));
        rows = selectedResults.flatMap(data => Array.isArray(data && data.rows) ? data.rows : []);
      } else {
        const data = await apiGet(buildSummaryUrl());
        rows = Array.isArray(data && data.rows) ? data.rows : [];
      }

      if (requestSeq !== loadSummarySeq) return;

      rows = dedupeSummaryRowsByDebtor(filterRowsBySelectedCustomers(rows.map(normalizeSummaryRow)));
      Object.keys(ledgerCache).forEach(k => delete ledgerCache[k]);
      renderSummary(rows);
      setSummaryLoading(false);

      const receiptMount = $('ac_bs_receipt_mount');
      if (receiptMount) receiptMount.innerHTML = '';

      currentLedgerRows = [];
      currentLedgerCustomer = { code: '', name: '' };
      $('ac_bs_ledger_title').textContent = 'Basket Movement History';
      $('ac_bs_ledger_table').innerHTML = '<tr><td colspan="8" class="bs-empty-cell">Select a customer to view basket movement history.</td></tr>';
    } catch(err) {
      if (requestSeq !== loadSummarySeq) return;
      showError('Failed to load summary', err && err.message ? err.message : 'Please try again.');
      setSummaryLoading(false);
      $('ac_bs_rows_table').innerHTML = '<tr><td colspan="8" class="bs-empty-cell">Failed to load summary.</td></tr>';
    }
  }

  function showLedgerLoading(debtorCode, debtorName){
    currentLedgerRows = [];
    currentLedgerCustomer = { code: debtorCode || '', name: debtorName || '' };
    $('ac_bs_ledger_title').textContent = 'Basket Movement History: ' + (debtorName || debtorCode);
    $('ac_bs_ledger_table').innerHTML = `
      <tr>
        <td colspan="8" class="bs-ledger-loading-cell">
          <div class="bs-ledger-loading">
            <div class="bs-ledger-spinner"></div>
            <div>Loading basket movement...</div>
            <div style="font-size:12px;color:#64748b;">${esc(debtorName || debtorCode || 'Customer')}</div>
          </div>
        </td>
      </tr>`;
  }

  function renderLedger(debtorCode, debtorName, rows){
    currentLedgerRows = Array.isArray(rows) ? rows.map(normalizeLedgerRow).sort(compareLedgerByLastActivity) : [];
    currentLedgerCustomer = { code: debtorCode || '', name: debtorName || '' };
    $('ac_bs_ledger_title').textContent = 'Basket Movement History: ' + (debtorName || debtorCode);

    const table = $('ac_bs_ledger_table');
    if (!currentLedgerRows.length) {
      table.innerHTML = '<tr><td colspan="8" class="bs-empty-cell">No basket movement found for this customer.</td></tr>';
      return;
    }

    table.innerHTML = currentLedgerRows.map((r, i) => {
      const txnType = getRowTxnType(r);
      const label = txnType === 'RETURN' ? 'Returned' : 'Sent Out';
      const typeClass = txnType === 'RETURN' ? 'bs-type-return' : 'bs-type-send';
      const receiptBtn = txnType === 'RETURN'
        ? `<button class="bs-view-btn bs-receipt-btn" type="button" data-basket-receipt-idx="${i}">View Basket Receipt</button>`
        : '<span style="color:#94a3b8;">-</span>';

      return `<tr>
        <td>${i+1}</td>
        <td>${esc(r.txnDate || r.txn_date || '-')}</td>
        <td><span class="${typeClass}">${esc(label)}</span></td>
        <td>${fmtQty(r.qty || 0)}</td>
        <td>${esc(r.sourceType || r.source_type || '-')}</td>
        <td>${esc(r.sourceRef || r.source_ref || '-')}</td>
        <td>${esc(r.remark || r.note || '-')}</td>
        <td>${receiptBtn}</td>
      </tr>`;
    }).join('');
  }

  async function loadLedger(debtorCode, debtorName){
    if (!REST_LEDGER_URL) {
      showError('Missing configuration', 'Ledger endpoint missing.');
      return;
    }

    const rangeError = dateRangeError();
    if (rangeError) {
      showError('Invalid date range', rangeError);
      return;
    }

    const cacheKey = debtorCode + '|' + ($('ac_bs_date_from').value || '') + '|' + ($('ac_bs_date_to').value || '');
    showLedgerLoading(debtorCode, debtorName);
    markSelectedDebtorRow(debtorCode);
    openLedgerModal();

    if (ledgerCache[cacheKey]) {
      renderLedger(debtorCode, debtorName, ledgerCache[cacheKey]);
      return;
    }

    try {
      const data = await apiGet(buildLedgerUrl(debtorCode));
      const rows = Array.isArray(data && data.rows) ? data.rows.map(normalizeLedgerRow) : [];
      ledgerCache[cacheKey] = rows;
      renderLedger(debtorCode, debtorName, rows);
    } catch(err) {
      showError('Failed to load movement history', err && err.message ? err.message : 'Please try again.');
      $('ac_bs_ledger_table').innerHTML = '<tr><td colspan="8" class="bs-empty-cell">Failed to load movement history.</td></tr>';
    }
  }

  function updateCustomerClearButton(){
    const hasValue = selectedDebtors.length > 0;
    $('ac_bs_customer_clear')?.classList.toggle('show', hasValue);
  }

  function clearCustomerSelection(){
    selectedDebtors.splice(0, selectedDebtors.length);
    updateCustomerSelectionUi();
    closeSelectedCustomerManager();
    loadSummary();
  }

  async function searchCustomersLive(q){
    if (!AJAX_URL || !DEBTOR_NONCE) return [];

    const url = AJAX_URL + '?action=ac_cs_debtor_search&nonce=' + encodeURIComponent(DEBTOR_NONCE) + '&q=' + encodeURIComponent(q);
    const res = await fetch(url, { credentials:'same-origin', cache:'no-store' });
    const data = await res.json();
    if (!data.success) throw new Error((data.data && data.data.error) ? data.data.error : 'Search failed');

    const items = data.data?.items || [];
    return items.map(it => {
      const name = it.name || it.debtorName || '';
      const code = it.code || it.debtorCode || '';
      return { label: name || code, meta: (SHOW_DEBTOR_CODE && code) ? code : '', raw: { name, code } };
    });
  }

  function renderPickerNote(msg){
    $('ac_bs_picker_results').innerHTML = '<div class="bs-picker-note">' + esc(msg) + '</div>';
  }

  function renderPickerItems(items){
    if (!items.length) {
      renderPickerNote('No result found');
      return;
    }

    $('ac_bs_picker_results').innerHTML = items.map((it, idx) => `<button type="button" class="bs-picker-item" data-picker-idx="${idx}">
      <span class="bs-picker-item-main">${esc(it.label || '')}</span>
      ${it.meta ? '<span class="bs-picker-item-sub">' + esc(it.meta) + '</span>' : ''}
    </button>`).join('');
  }

  function runPickerSearch(q){
    const query = (q || '').trim();
    clearTimeout(pickerTimer);

    if (query.length < 1) {
      pickerState.items = [];
      renderPickerNote('Type to search');
      return;
    }

    pickerTimer = setTimeout(async function(){
      renderPickerNote('Searching...');
      try {
        pickerState.items = await pickerState.fetchFn(query) || [];
        renderPickerItems(pickerState.items);
      } catch(e) {
        pickerState.items = [];
        renderPickerNote('Failed to load');
      }
    }, 220);
  }

  function openPicker(opts){
    pickerState.items = [];
    pickerState.fetchFn = opts.fetchFn;
    pickerState.onPick = opts.onPick;
    $('ac_bs_picker_title').textContent = opts.title || 'Search';
    $('ac_bs_picker_sear+��b�K	m����������v
?
w����eliveryForm') {
            const file = e.target.querySelector('input[type="file"]');

            if (!file || !file.files || !file.files.length) {
                if (!window.Swal) {
                    const ok = confirm('No proof photo selected. Continue marking this delivery as delivered?');
                    if (!ok) {
                        e.preventDefault();
                    }
                    return;
                }

                e.preventDefault();

                const result = await Swal.fire({
                    icon: 'question',
                    title: 'No proof photo',
                    text: 'Continue marking this delivery as delivered without proof?',
                    showCancelButton: true,
                    confirmButtonText: 'Yes, mark delivered',
                    cancelButtonText: 'Cancel',
                    confirmButtonColor: '#0B4A2D',
                    cancelButtonColor: '#64748b'
                });

                if (result.isConfirmed) {
                    e.target.dataset.bdoResubmitting = '1';
                    e.target.submit();
                }
            }
        }
    });

    setStats(initialStats);
    renderDoLines();
    renderActive();
    updateClearButtons();
    const initialTab = root.dataset.initialTab || 'home';
    if (['home','create','deliveries','active','return'].includes(initialTab)) openPanel(initialTab);
})();
</script>
HTML;

$alert_html = '';
if ($message === 'delivered') {
    $alert_html = '<div class="bdo-alert ok">Delivery marked as DELIVERED. You can open the DO receipt from the delivery panel.</div>';
} elseif ($message === 'br_saved') {
    $alert_html = '<div class="bdo-alert ok">Basket return saved. Receipt is shown on this page.</div>';
}

if ($error !== '') {
    $alert_html = '<div class="bdo-alert err">' . esc_html($error) . '</div>';
}

$replacements = [
    '__REST_NONCE__'         => esc_attr($rest_nonce),
    '__AJAX_URL__'           => esc_url($ajax_url),
    '__DEBTOR_NONCE__'       => esc_attr($debtor_nonce),
    '__ITEM_NONCE__'         => esc_attr($item_nonce),
    '__REST_JOB_POST__'      => esc_url($REST_JOB_POST),
    '__REST_JOB_BASE__'      => esc_url($REST_JOB_BASE),
    '__REST_RECEIPT_TOKEN__' => esc_url($REST_RECEIPT_TOKEN),
    '__RECEIPT_BASE__'       => esc_url($receipt_page_base),
    '__DEFAULT_LOCATION__'   => esc_attr($default_location),
    '__TODAY__'              => esc_attr($today),
    '__TODAY_LABEL__'        => esc_html($today_label),
    '__DRIVER_NAME__'        => esc_html($driver_name),
    '__FORM_NONCE__'         => esc_attr($form_nonce),
    '__LOGOUT_URL__'         => esc_url(wp_logout_url(wp_login_url())),
    '__INITIAL_ACTIVE_JSON__'=> $initial_active_json ?: '{}',
    '__INITIAL_ACTIVE_JOBS_JSON__'=> $active_jobs_json ?: '[]',
    '__INITIAL_TODAY_JOBS_JSON__'=> $today_jobs_json ?: '[]',
    '__INITIAL_DELIVERED_JOBS_JSON__'=> $delivered_jobs_json ?: '[]',
    '__INITIAL_RETURN_ROWS_JSON__'=> $return_rows_json ?: '[]',
    '__STATS_JSON__'         => $stats_json ?: '{}',
    '__INLINE_BR_RECEIPT__'  => $inline_br_receipt_html,
    '__ALERT_HTML__'         => $alert_html,
    '__INITIAL_TAB__'        => esc_attr($initial_tab),
    '__INITIAL_FILTER__'     => esc_attr($message === 'delivered' ? 'delivered' : 'active'),
];

echo strtr($html, $replacements);��vh�G�n��������+"X�
??�pown', onKeydown);
            root.appendChild(overlay);
            setTimeout(() => overlay.querySelector(opts.showCancel ? '[data-dialog-cancel]' : '[data-dialog-confirm]')?.focus(), 30);
        });
    }
    function modal(icon,title,html){
        if(window.Swal) { Swal.fire({icon,title,html,confirmButtonText:'OK',confirmButtonColor:'#166534'}); return; }
        localDialog({icon,title,html,confirmText:'OK',showCancel:false});
    }
    function lineSummary(){
        return state.lines.reduce((sum, line) => {
            const type = String(line.packType || '').toUpperCase();
            const qty = parseQty(line.qty || 0);
            const totalKg = Number(line.total || calcTotalKg(line.qty || 0, line.kg || 0)) || 0;
            if (type === 'CARTON') {
                sum.carton += qty;
            } else {
                sum.basket += qty;
            }
            sum.kg += totalKg;
            return sum;
        }, {basket:0, carton:0, kg:0});
    }
    async function confirmQueueUpdate(){
        const selectedDriverId = parseInt($('acd_resp_do_driver')?.value || '0',10) || 0;
        const originalDriverId = parseInt(root.dataset.originalDriverId || '0',10) || 0;
        const selectedDriver = ($('acd_resp_do_driver_name')?.value || '').trim().toUpperCase() || 'NO DRIVER';
        const originalDriver = String(root.dataset.originalDriverLabel || 'NO DRIVER').trim().toUpperCase() || 'NO DRIVER';
        const driverChanged = selectedDriverId > 0 && originalDriverId > 0 && selectedDriverId !== originalDriverId;
        const totals = lineSummary();
        const html = `
            <div style="text-align:left">
                <p><strong>Delivery Order:</strong> ${esc(root.dataset.docNo || '')}</p>
                <p><strong>Customer:</strong> ${esc(root.dataset.customerName || '')}</p>
                <p><strong>Driver:</strong> ${esc(selectedDriver)}</p>
                ${driverChanged ? `<p style="padding:.7rem .8rem;border:1px solid #fbbf24;background:#fef3c7;color:#92400e;border-radius:.55rem"><strong>Driver reassignment:</strong><br>Driver will change from ${esc(originalDriver)} to ${esc(selectedDriver)}.<br>This means ${esc(selectedDriver)} will take this Delivery Order after AutoCount update and reprint.</p>` : ''}
                <p><strong>Items:</strong> ${esc(state.lines.length)} line(s)</p>
                <p><strong>Summary:</strong> Basket ${esc(fmtQty(totals.basket))} | Carton ${esc(fmtQty(totals.carton))} | Total KG ${esc(fmtKg(totals.kg))}</p>
                <p>This will queue an AutoCount update for the existing DO. Staff should reprint the corrected DO after the update is ready.</p>
            </div>`;

        if (window.Swal) {
            const result = await Swal.fire({
                icon: driverChanged ? 'warning' : 'question',
                title: 'Confirm AutoCount update',
                html,
                confirmButtonText: 'Confirm Queue Update',
                confirmButtonColor: '#166534',
                showCancelButton: true,
                cancelButtonText: 'Cancel / Review Again',
                reverseButtons: true,
                focusCancel: driverChanged
            });
            return !!result.isConfirmed;
        }

        return localDialog({
            icon: driverChanged ? 'warning' : 'question',
            title: 'Confirm AutoCount update',
            html,
            confirmText: 'Confirm Queue Update',
            cancelText: 'Cancel / Review Again',
            showCancel: true
        });
    }

    function updateEntryTotal(){
        const itemCode = ($('acd_resp_do_item').value || '').trim();
        const itemName = ($('acd_resp_do_item_display').value || '').trim() || itemCode;
        const packType = ($('acd_resp_do_pack_type').value || '').trim();
        const qtyRaw = ($('acd_resp_do_qty').value || '').trim();
        const kgRaw = ($('acd_resp_do_kg').value || '').trim();
        const qty = parseQty(qtyRaw || '0');
        const kg = parseKg(kgRaw || '0');
        const total = calcTotalKg(qty, kg);
        const pv = $('acd_resp_do_line_preview');
        if (!itemCode || qtyRaw === '' || kgRaw === '') { pv.style.display = 'none'; pv.innerHTML = ''; return; }
        pv.style.display = 'block';
        pv.innerHTML = `<div><b>${esc(itemName)}</b></div><div>Type: ${esc(packType)} | Qty: ${fmtQty(qty)} | KG: ${fmtKg(kg)} | Total KG: ${fmtKg(total)}</div>`;
    }
    function setPackType(type){
        const nextType = String(type || '').toUpperCase() === 'CARTON' ? 'CARTON' : 'BASKET';
        $('acd_resp_do_pack_type').value = nextType;
        document.querySelectorAll('#acd_resp_do_pack_type_toggle .acd-resp-type-btn').forEach(btn => btn.classList.toggle('active', (btn.dataset.packType || '').toUpperCase() === nextType));
        updateEntryTotal();
    }
    function updateClearButtons(){
        $('acdRespDriverClear')?.classList.toggle('show', !!($('acd_resp_do_driver_name')?.value.trim()));
        $('acdRespItemClear')?.classList.toggle('show', !!($('acd_resp_do_item_name')?.value.trim()));
    }
    function syncHiddenLines(){
        const box = $('acd_resp_do_hidden_lines');
        box.innerHTML = state.lines.map((line, idx) => `
            <input type="hidden" name="wst_doe_lines[${idx}][item_name]" value="${esc(line.itemName || line.itemCode || '')}">
            <input type="hidden" name="wst_doe_lines[${idx}][item_code]" value="${esc(line.itemCode || '')}">
            <input type="hidden" name="wst_doe_lines[${idx}][pack_type]" value="${esc(line.packType || 'BASKET')}">
            <input type="hidden" name="wst_doe_lines[${idx}][unit_qty]" value="${esc(line.qty || 0)}">
            <input type="hidden" name="wst_doe_lines[${idx}][kg]" value="${esc(line.kg || 0)}">`).join('');
    }
    function updateUI(){
        $('acd_resp_do_lines_count_badge').textContent = state.lines.length;
        const container = $('acd_resp_do_lines');
        if (!state.lines.length) { container.innerHTML = '<div class="acd-resp-empty">No items added</div>'; syncHiddenLines(); return; }
        const mobileHtml = state.lines.map((l, idx) => `
            <div class="acd-resp-mobile-line-item" data-idx="${idx}">
                <div class="acd-resp-mobile-line-top"><div class="acd-resp-mobile-line-name">${esc(l.itemName || l.itemCode)}</div><div><button type="button" class="acd-resp-mobile-delete-btn" data-edit-idx="${idx}">Edit</button> <button type="button" class="acd-resp-mobile-delete-btn" data-idx="${idx}">Delete</button></div></div>
                <div class="acd-resp-mobile-line-meta"><div class="acd-resp-mobile-chip"><span class="acd-resp-mobile-chip-label">Type</span><span class="acd-resp-mobile-chip-value"><span class="acd-resp-type-pill">${esc(l.packType)}</span></span></div><div class="acd-resp-mobile-chip"><span class="acd-resp-mobile-chip-label">Qty</span><span class="acd-resp-mobile-chip-value">${fmtQty(l.qty)}</span></div><div class="acd-resp-mobile-chip"><span class="acd-resp-mobile-chip-label">KG</span><span class="acd-resp-mobile-chip-value">${fmtKg(l.kg)}</span></div><div class="acd-resp-mobile-chip"><span class="acd-resp-mobile-chip-label">Total KG</span><span class="acd-resp-mobile-chip-value">${fmtKg(l.total)}</span></div></div>
            </div>`).join('');
        const desktopHtml = state.lines.map((l, idx) => `<div class="acd-resp-line" data-idx="${idx}"><div><strong>${esc(l.itemName || l.itemCode)}</strong></div><div><span class="acd-resp-type-pill">${esc(l.packType)}</span></div><div class="acd-resp-number-cell">${fmtQty(l.qty)}</div><div class="acd-resp-number-cell">${fmtKg(l.kg)}</div><div class="acd-resp-number-cell">${fmtKg(l.total)}</div><div><button type="button" class="acd-resp-delete-btn" data-edit-idx="${idx}" title="Edit">&#9998;</button><button type="button" class="acd-resp-delete-btn" data-idx="${idx}" title="Delete">&#128465;</button></div></div>`).join('');
        container.innerHTML = desktopHtml + mobileHtml;
        syncHiddenLines();
    }
    async function searchItemsLive(q){
        const fd = new FormData(); fd.append('action','ac_itemcode_suggest'); fd.append('nonce',ITEM_NONCE); fd.append('term',q);
        const res = await fetch(AJAX_URL,{method:'POST',body:fd,credentials:'same-origin'}); const data = await res.json();
        if (data?.success && data.data?.items) return data.data.items.map(it => ({code:it.code || '', name:(it.desc || it.name || '').trim()}));
        return [];
    }
    function searchDriversLive(q){
        const query = String(q || '').trim().toLowerCase();
        const rows = !query ? DRIVER_ITEMS : DRIVER_ITEMS.filter(driver => [driver.label,driver.name,driver.login].join(' ').toLowerCase().includes(query));
        return Promise.resolve(rows);
    }
    function renderPickerNote(msg){ $('acd_resp_do_picker_results').innerHTML = `<div class="acd-resp-picker-note">${esc(msg)}</div>`; }
    function renderPickerItems(items){
        if (!items.length) { renderPickerNote('No result found'); return; }
        $('acd_resp_do_picker_results').innerHTML = items.map((it,idx) => `<button type="button" class="acd-resp-picker-item" data-picker-idx="${idx}"><span class="acd-resp-picker-item-main">${esc(it.label || '')}</span>${it.meta ? `<span class="acd-resp-picker-item-sub">${esc(it.meta)}</span>` : ''}</button>`).join('');
    }
    function openPicker(opts){
        pickerState.defaultItems = opts.initialItems || []; pickerState.items = pickerState.defaultItems; pickerState.fetchFn = opts.fetchFn; pickerState.onPick = opts.onPick;
        $('acd_resp_do_picker_title').textContent = opts.title || 'Search'; $('acd_resp_do_picker_search').placeholder = opts.placeholder || 'Type to search...'; $('acd_resp_do_picker_search').value = ''; $('acd_resp_do_picker_modal').classList.add('active');
        pickerState.items.length ? renderPickerItems(pickerState.items) : renderPickerNote('Type to search');
        setTimeout(() => $('acd_resp_do_picker_search').focus(), 80);
    }
    function closePicker(){ $('acd_resp_do_picker_modal').classList.remove('active'); $('acd_resp_do_picker_search').value = ''; $('acd_resp_do_picker_results').innerHTML = ''; pickerState.items = []; pickerState.defaultItems = []; pickerState.fetchFn = null; pickerState.onPick = null; }
    async function runPickerSearch(q){
        const query = (q || '').trim(); clearTimeout(pickerTimer);
        if (query.length < 1) { pickerState.items = pickerState.defaultItems || []; pickerState.items.length ? renderPickerItems(pickerState.items) : renderPickerNote('Type to search'); return; }
        pickerTimer = setTimeout(async () => { renderPickerNote('Searching...'); try { pickerState.items = await pickerState.fetchFn(query) || []; renderPickerItems(pickerState.items); } catch(e) { pickerState.items = []; renderPickerNote('Failed to load'); } }, 220);
    }
    function setDeliveryDriver(picked){ const id = parseInt(picked?.id || 0,10) || 0; const label = String(picked?.login || picked?.label || picked?.name || '').toUpperCase(); $('acd_resp_do_driver_name').value = label; $('acd_resp_do_driver').value = id ? String(id) : ''; $('acd_resp_do_driver_login').value = picked?.login || ''; updateClearButtons(); }
    function clearDriverSelection(){ $('acd_resp_do_driver_name').value=''; $('acd_resp_do_driver').value=''; $('acd_resp_do_driver_login').value=''; updateClearButtons(); }
    function clearItemSelection(){ $('acd_resp_do_item_name').value=''; $('acd_resp_do_item').value=''; $('acd_resp_do_item_display').value=''; updateEntryTotal(); updateClearButtons(); }
    function clearLineEntry(){ state.editingIndex = -1; $('acd_resp_do_addline').textContent = 'Add Item'; setPackType('BASKET'); $('acd_resp_do_qty').value=''; $('acd_resp_do_kg').value=''; clearItemSelection(); }
    function loadLineForEdit(idx){
        const line = state.lines[idx];
        if (!line) return;
        state.editingIndex = idx;
        $('acd_resp_do_item_name').value = line.itemName || line.itemCode || '';
        $('acd_resp_do_item').value = line.itemCode || '';
        $('acd_resp_do_item_display').value = line.itemName || line.itemCode || '';
        $('acd_resp_do_qty').value = fmtQty(line.qty || 0);
        $('acd_resp_do_kg').value = fmtKg(line.kg || 0);
        setPackType(line.packType || 'BASKET');
        $('acd_resp_do_addline').textContent = 'Update Item';
        updateClearButtons();
        updateEntryTotal();
        window.scrollTo({top: root.getBoundingClientRect().top + window.scrollY, behavior: 'smooth'});
    }
    function openDriverPicker(){ const options = DRIVER_ITEMS.map(driver => ({label:String(driver.login || '').toUpperCase(),meta:'',raw:driver})); openPicker({title:'Select Driver',placeholder:'Search driver...',initialItems:options,fetchFn:async q => (await searchDriversLive(q)).map(driver => ({label:String(driver.login || '').toUpperCase(),meta:'',raw:driver})),onPick:picked => { if (!picked) return; setDeliveryDriver(picked); closePicker(); }}); }
    function openItemPicker(){ openPicker({title:'Select Item',placeholder:'Search item...',fetchFn:async q => (await searchItemsLive(q)).map(it => ({label:it.name || it.code,meta:'',raw:{code:it.code,name:it.name || it.code}})),onPick:picked => { if (!picked) return; $('acd_resp_do_item_name').value = picked.name || picked.code || ''; $('acd_resp_do_item').value = picked.code || ''; $('acd_resp_do_item_display').value = picked.name || picked.code || ''; updateEntryTotal(); updateClearButtons(); closePicker(); }}); }

    $('acd_resp_do_picker_close').addEventListener('click', closePicker);
    $('acd_resp_do_picker_backdrop').addEventListener('click', closePicker);
    $('acd_resp_do_picker_search').addEventListener('input', function(){ runPickerSearch(this.value); });
    $('acd_resp_do_picker_results').addEventListener('click', e => { const btn = e.target.closest('[data-picker-idx]'); if (!btn) return; const idx = parseInt(btn.dataset.pickerIdx,10); if (!isNaN(idx) && pickerState.items[idx] && pickerState.onPick) pickerState.onPick(pickerState.items[idx].raw); });
    $('acd_resp_do_driver_name').addEventListener('click', openDriverPicker);
    $('acd_resp_do_item_name').addEventListener('click', openItemPicker);
    $('acdRespDriverClear')?.addEventListener('click', e => { e.preventDefault(); clearDriverSelection(); });
    $('acdRespItemClear')?.addEventListener('click', e => { e.preventDefault(); clearItemSelection(); });
    $('acd_resp_do_qty').addEventListener('input', updateEntryTotal);
    $('acd_resp_do_kg').addEventListener('input', updateEntryTotal);
    document.querySelectorAll('#acd_resp_do_pack_type_toggle .acd-resp-type-btn').forEach(btn => btn.addEventListener('click', () => setPackType(btn.dataset.packType)));
    $('acd_resp_do_addline').addEventListener('click', () => {
        const itemCode = ($('acd_resp_do_item').value || '').trim(); const itemName = ($('acd_resp_do_item_display').value || '').trim() || itemCode; const packType = ($('acd_resp_do_pack_type').value || '').trim(); const qty = parseQty($('acd_resp_do_qty').value || '0'); const kg = parseKg($('acd_resp_do_kg').value || '0'); const customerCode = ($('acd_resp_do_customer').value || '').trim(); const assignedDriverId = parseInt($('acd_resp_do_driver')?.value || '0',10) || 0;
        if (!customerCode) { toast('error','Customer missing'); return; } if (!assignedDriverId) { toast('error','Select driver'); return; } if (!itemCode) { toast('error','Select an item'); return; } if (qty <= 0) { toast('error','Qty must be >0'); return; } if (kg <= 0) { toast('error','KG must be >0'); return; }
        const nextLine = {itemCode,itemName,packType,qty,kg,total:calcTotalKg(qty,kg)};
        if (state.editingIndex >= 0 && state.lines[state.editingIndex]) {
            state.lines[state.editingIndex] = nextLine;
            toast('success','Item updated');
        } else {
            state.lines.push(nextLine);
            toast('success','Item added');
        }
        updateUI(); clearLineEntry();
    });
    $('acd_resp_do_lines').addEventListener('click', e => {
     +"X�ӈ�jo��������	��
?!����resp-picker-item" data-picker-idx="${idx}"><span class="acd-resp-picker-item-main">${esc(it.label || '')}</span>${it.meta ? `<span class="acd-resp-picker-item-sub">${esc(it.meta)}</span>` : ''}</button>`).join('');
        }
        async function runPickerSearch(q) {
            const query = (q || '').trim();
            clearTimeout(pickerTimer);
            if (query.length < 1) { pickerState.items = []; renderPickerNote('Type to search'); return; }
            pickerTimer = setTimeout(async () => {
                renderPickerNote('Searching...');
                try { const items = await pickerState.fetchFn(query); pickerState.items = items || []; renderPickerItems(pickerState.items); }
                catch(e) { pickerState.items = []; renderPickerNote('Failed to load'); }
            }, 220);
        }
        function openBasketPicker(opts) {
            pickerState.items = []; pickerState.fetchFn = opts.fetchFn; pickerState.onPick = opts.onPick;
            const titleEl = $('acd_resp_br_picker_title');
            const searchInput = $('acd_resp_br_picker_search');
            const modal = $('acd_resp_br_picker_modal');
            if (titleEl) titleEl.textContent = opts.title || 'Search';
            if (searchInput) { searchInput.placeholder = opts.placeholder || 'Type to search...'; searchInput.value = ''; }
            if (modal) { modal.classList.add('active'); renderPickerNote('Type to search'); setTimeout(() => searchInput?.focus(), 80); }
        }
        function closeBasketPicker() {
            const modal = $('acd_resp_br_picker_modal');
            const searchInput = $('acd_resp_br_picker_search');
            const resultsDiv = $('acd_resp_br_picker_results');
            if (modal) modal.classList.remove('active');
            if (searchInput) searchInput.value = '';
            if (resultsDiv) resultsDiv.innerHTML = '';
            pickerState.items = []; pickerState.fetchFn = null; pickerState.onPick = null;
        }
        async function searchBasketDebtorsLive(q) {
            const wrapper = $('acdRespBrDebtorWrapper');
            if (!wrapper) return [];
            const url = `${wrapper.dataset.ajaxUrl}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(wrapper.dataset.nonce)}&q=${encodeURIComponent(q)}`;
            const res = await fetch(url, { credentials: 'same-origin' });
            const data = await res.json();
            if (!data.success) throw new Error(data.data?.error || 'Search failed');
            const items = data.data?.items || [];
            return items.map(it => {
                const name = it.name || it.debtorName || '';
                const code = it.code || it.debtorCode || '';
                return { label: name || code, meta: (SHOW_DEBTOR_CODE && code) ? code : '', raw: { name, code } };
            });
        }
        function initBasketPickerModal() {
            const closeBtn = $('acd_resp_br_picker_close');
            const backdrop = $('acd_resp_br_picker_backdrop');
            const searchInput = $('acd_resp_br_picker_search');
            const resultsDiv = $('acd_resp_br_picker_results');
            if (closeBtn) closeBtn.addEventListener('click', closeBasketPicker);
            if (backdrop) backdrop.addEventListener('click', closeBasketPicker);
            if (searchInput) searchInput.addEventListener('input', function() { runPickerSearch(this.value); });
            if (resultsDiv) resultsDiv.addEventListener('click', (e) => {
                const btn = e.target.closest('[data-picker-idx]');
                if (!btn) return;
                const idx = parseInt(btn.dataset.pickerIdx);
                if (!isNaN(idx) && pickerState.items[idx] && pickerState.onPick) pickerState.onPick(pickerState.items[idx].raw);
            });
        }
        function updateBasketClearButton() {
            const input = $('acdRespBrDebtorInput');
            const clearBtn = $('acdRespBrDebtorClear');
            if (clearBtn) clearBtn.classList.toggle('show', !!(input?.value.trim()));
        }
        function clearBasketCustomer(manual = false) {
            const input = $('acdRespBrDebtorInput');
            const hiddenCode = $('acd_resp_br_debtor_code');
            const hiddenName = $('acd_resp_br_debtor_name');

            if (input) input.value = '';
            if (hiddenCode) hiddenCode.value = '';
            if (hiddenName) hiddenName.value = '';

            if (manual) {
                sharedCustomerState.basketCustomerManuallyCleared = true;
            }

            updateBasketClearButton();
        }
        function openBasketDebtorPicker() {
            openBasketPicker({
                title: 'Select Customer',
                placeholder: 'Search customer...',
                fetchFn: searchBasketDebtorsLive,
                onPick: (picked) => {
                    if (!picked) return;
                    const input = $('acdRespBrDebtorInput');
                    const hiddenCode = $('acd_resp_br_debtor_code');
                    const hiddenName = $('acd_resp_br_debtor_name');

                    if (input) input.value = picked.name || picked.code || '';
                    if (hiddenCode) hiddenCode.value = picked.code || '';
                    if (hiddenName) hiddenName.value = picked.name || '';

                    sharedCustomerState.basketCustomerManuallyCleared = false;

                    updateBasketClearButton();
                    closeBasketPicker();
                }
            });
        }

        initBasketPickerModal();
        const input = $('acdRespBrDebtorInput');
        const clearBtn = $('acdRespBrDebtorClear');
        if (input) { input.setAttribute('readonly', 'readonly'); input.addEventListener('click', openBasketDebtorPicker); }
        if (clearBtn) {
            clearBtn.addEventListener('click', (e) => {
                e.preventDefault();
                clearBasketCustomer(true);
            });
        }
        updateBasketClearButton();

        $('acd_resp_br_submit').addEventListener('click', async () => {
            if (isSubmitting) return;
            const debtorCode = ($('acd_resp_br_debtor_code').value || '').trim();
            const debtorName = ($('acd_resp_br_debtor_name').value || '').trim();
            const docDate = $('acd_resp_br_date').value;
            const basketQty = whole($('acd_resp_br_qty').value);
            const proofInput = $('acd_resp_br_proof');
            const proofFile = proofInput?.files?.[0] || null;
            if (!debtorCode) { toast('error', 'Select customer'); return; }
            if (basketQty <= 0) { toast('error', 'Quantity must be >0'); return; }
            isSubmitting = true;
            const btn = $('acd_resp_br_submit');
            btn.disabled = true;
            btn.textContent = 'Saving...';
            try {
                let body;
                let headers = { 'X-WP-Nonce': REST_NONCE };

                if (proofFile) {
                    body = new FormData();
                    body.append('debtorCode', debtorCode);
                    body.append('debtorName', debtorName);
                    body.append('docDate', docDate);
                    body.append('basketQty', String(basketQty));
                    body.append('basketReturnProof', proofFile);
                } else {
                    body = JSON.stringify({ debtorCode, debtorName, docDate, basketQty });
                    headers['Content-Type'] = 'application/json';
                }

                const res = await fetch(REST_RETURN_URL, { method: 'POST', headers, body });
                if (!res.ok) throw new Error(await res.text());
                const data = await res.json();

                toast('success', 'Basket return saved', `${debtorName} | Qty ${basketQty}${data?.proofSaved ? ' | Proof saved' : ''}`);

                $('acd_resp_br_qty').value = '';
                if (proofInput) proofInput.value = '';

                const dateField = $('acd_resp_br_date');
                if (dateField) dateField.value = root.dataset.today || '';
            } catch(err) { toast('error', 'Save failed', err.message); }
            finally { isSubmitting = false; btn.disabled = false; btn.textContent = 'Save Basket Return'; }
        });
    }
})();
</script>	�ʯ�Z�p��������+"X�
?�����   const editBtn = e.target.closest('[data-edit-idx]');
        if (editBtn) {
            loadLineForEdit(parseInt(editBtn.dataset.editIdx,10));
            return;
        }
        const btn = e.target.closest('.acd-resp-delete-btn,.acd-resp-mobile-delete-btn');
        if (!btn || !btn.dataset.idx) return;
        const idx = parseInt(btn.dataset.idx,10);
        if (!isNaN(idx)) { state.lines.splice(idx,1); updateUI(); toast('info','Item removed'); }
    });
    $('acd_resp_do_form').addEventListener('submit', async e => {
        e.preventDefault();
        if (state.isSubmitting) return;
        if (!parseInt($('acd_resp_do_driver')?.value || '0',10)) { modal('error','Select driver','This update needs one driver for the whole Delivery Order.'); return; }
        if (!state.lines.length) { modal('error','Add at least one item','This update cannot be queued without item lines.'); return; }
        syncHiddenLines();
        const confirmed = await confirmQueueUpdate();
        if (!confirmed) return;
        state.isSubmitting = true;
        $('acd_resp_do_submit').disabled = true;
        $('acd_resp_do_submit').textContent = 'Queueing update...';
        HTMLFormElement.prototype.submit.call($('acd_resp_do_form'));
    });

    updateClearButtons(); setPackType('BASKET'); updateUI();
    const savedJob = Number(root.dataset.savedJob || 0);
    if (savedJob > 0) { $('acd_resp_do_success_actions').style.display = 'block'; if (window.Swal) { Swal.fire({icon:'success',title:'Update queued',html:`AutoCount update job #${savedJob} has been queued for <strong>${esc(root.dataset.docNo || '')}</strong>. Print or reprint after the status is ready.`,confirmButtonText:'Go to list/status',confirmButtonColor:'#166534',showCancelButton:true,cancelButtonText:'Stay here'}).then(result => { if (result.isConfirmed) window.location.href = root.dataset.listUrl || '/delivery-order-records/'; }); } }
})();
</script>+"X�\��q��������+��b
?����ch').placeholder = opts.placeholder || 'Type to search...';
    $('ac_bs_picker_search').value = '';
    $('ac_bs_picker_modal').classList.add('active');
    renderPickerNote('Type to search');
    setTimeout(() => $('ac_bs_picker_search').focus(), 80);
  }

  function closePicker(){
    $('ac_bs_picker_modal').classList.remove('active');
    $('ac_bs_picker_search').value = '';
    $('ac_bs_picker_results').innerHTML = '';
    pickerState.items = [];
    pickerState.fetchFn = null;
    pickerState.onPick = null;
  }

  function openCustomerPicker(){
    openPicker({
      title:'Select Customer',
      placeholder:'Search customer...',
      fetchFn: searchCustomersLive,
      onPick: function(picked){
        if (!picked) return;
        addSelectedCustomer(picked);
        closePicker();
        loadSummary();
      }
    });
  }

  function setDefaultDateRange(){
    const dateFromEl = $('ac_bs_date_from');
    const dateToEl = $('ac_bs_date_to');
    if (!dateFromEl || !dateToEl) return;

    const today = new Date();
    const oneMonthAgo = new Date(today);
    oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);

    function toYmd(d){
      return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
    }

    if (!dateToEl.value) dateToEl.value = toYmd(today);
    if (!dateFromEl.value) dateFromEl.value = toYmd(oneMonthAgo);
  }

  function openLedgerModal(){
    const modal = $('ac_bs_ledger_modal');
    if (!modal) return;
    modal.classList.add('active');
    modal.setAttribute('aria-hidden', 'false');
    document.body.classList.add('bs-ledger-open');
  }

  function closeLedgerModal(){
    const modal = $('ac_bs_ledger_modal');
    if (!modal) return;
    modal.classList.remove('active');
    modal.setAttribute('aria-hidden', 'true');
    document.body.classList.remove('bs-ledger-open');
  }

  $('ac_bs_refresh').addEventListener('click', loadSummary);
  $('ac_bs_rows_table').addEventListener('click', e => {
    const btn = e.target.closest('[data-debtor-code]');
    if (!btn) return;
    const code = btn.dataset.debtorCode || '';
    const name = btn.dataset.debtorName || '';
    if (code) loadLedger(code, name);
  });
  $('ac_bs_ledger_table').addEventListener('click', e => {
    const btn = e.target.closest('[data-basket-receipt-idx]');
    if (!btn) return;
    e.preventDefault();
    e.stopPropagation();
    openBasketReceiptByIndex(btn.dataset.basketReceiptIdx);
  });
  $('ac_bs_receipt_mount').addEventListener('click', e => {
    const printBtn = e.target.closest('[data-print-current-basket-receipt]');
    if (printBtn) {
      e.preventDefault();
      printCurrentBasketReceipt();
      return;
    }

    const btn = e.target.closest('[data-share-current-basket-receipt]');
    if (!btn) return;
    e.preventDefault();
    shareCurrentBasketReceipt(btn);
  });
  $('ac_bs_customer_input').addEventListener('click', openCustomerPicker);
  $('ac_bs_customer_clear').addEventListener('click', e => {
    e.preventDefault();
    e.stopPropagation();
    clearCustomerSelection();
  });
  $('ac_bs_selected_customers').addEventListener('click', e => {
    const toggleBtn = e.target.closest('[data-toggle-selected-customers]');
    if (toggleBtn) {
      e.preventDefault();
      e.stopPropagation();
      toggleSelectedCustomerManager();
      return;
    }

    const btn = e.target.closest('[data-remove-selected-customer]');
    if (!btn) return;
    e.preventDefault();
    e.stopPropagation();
    removeSelectedCustomer(btn.dataset.removeSelectedCustomer || '');
  });
  $('ac_bs_manage_list').addEventListener('click', e => {
    const btn = e.target.closest('[data-remove-selected-customer]');
    if (!btn) return;
    e.preventDefault();
    e.stopPropagation();
    removeSelectedCustomer(btn.dataset.removeSelectedCustomer || '');
  });
  $('ac_bs_manage_add').addEventListener('click', e => {
    e.preventDefault();
    openCustomerPicker();
  });
  $('ac_bs_manage_clear').addEventListener('click', e => {
    e.preventDefault();
    clearCustomerSelection();
  });
  $('ac_bs_manage_done').addEventListener('click', e => {
    e.preventDefault();
    closeSelectedCustomerManager();
  });
  document.addEventListener('click', function(e){
    const panel = $('ac_bs_manage_selected');
    const selectedArea = $('ac_bs_selected_customers');
    if (!panel || !panel.classList.contains('active')) return;
    if (panel.contains(e.target) || selectedArea.contains(e.target)) return;
    closeSelectedCustomerManager();
  });
  $('ac_bs_picker_close').addEventListener('click', closePicker);
  $('ac_bs_picker_backdrop').addEventListener('click', closePicker);
  $('ac_bs_picker_search').addEventListener('input', function(){ runPickerSearch(this.value); });
  $('ac_bs_picker_results').addEventListener('click', e => {
    const btn = e.target.closest('[data-picker-idx]');
    if (!btn) return;
    const idx = parseInt(btn.dataset.pickerIdx, 10);
    if (!isNaN(idx) && pickerState.items[idx] && pickerState.onPick) {
      pickerState.onPick(pickerState.items[idx].raw);
    }
  });
  $('ac_bs_ledger_close').addEventListener('click', closeLedgerModal);
  $('ac_bs_ledger_backdrop').addEventListener('click', closeLedgerModal);
  document.addEventListener('keydown', function(e){
    if (e.key === 'Escape') {
      closeSelectedCustomerManager();
      closeLedgerModal();
      const receiptMount = $('ac_bs_receipt_mount');
      if (receiptMount && receiptMount.innerHTML) {
        receiptMount.innerHTML = '';
        document.body.classList.remove('bs-br-open');
      }
    }
  });

  setDefaultDateRange();
  updateCustomerClearButton();
  loadSummary();
})();
</script>+��b��~�r��������)��*
?3��hare || !navigator.canShare({files:[file]})) {
                    downloadBlob(blob, fileName);
                    toast('info', 'PDF downloaded', 'Attach the downloaded PDF in WhatsApp.');
                    return null;
                }

                return navigator.share({
                    title: fileName.replace(/\.pdf$/i, ''),
                    text: 'Basket Return PDF',
                    files: [file]
                });
            })
            .catch(error => {
                if (error && error.name === 'AbortError') return;
                toast('error', 'Unable to prepare PDF', 'Please print or save PDF, then share it in WhatsApp.');
            })
            .finally(() => {
                if (button) {
                    button.disabled = false;
                    button.textContent = originalText || 'Share PDF';
                }
            });
    }

    function findDeliveredJob(id) {
        return deliveredJobs.find(job => String(job.id) === String(id)) || null;
    }

    function downloadDeliveryOrder(id, button) {
        const job = findDeliveredJob(id);
        if (!job) {
            toast('error', 'Delivery order not found');
            return;
        }

        const originalText = button ? button.textContent : '';
        if (button) {
            button.disabled = true;
            button.textContent = 'Preparing PDF...';
        }

        buildDeliveryOrderPdf(job)
            .then(blob => {
                downloadBlob(blob, doFileName(job));
            })
            .catch(() => {
                toast('error', 'Unable to prepare DO PDF', 'Please try again.');
            })
            .finally(() => {
                if (button) {
                    button.disabled = false;
                    button.textContent = originalText || 'Download DO';
                }
            });
    }

    function shareDeliveryOrder(id, button) {
        const job = findDeliveredJob(id);
        if (!job) {
            toast('error', 'Delivery order not found');
            return;
        }

        if (!navigator.share) {
            toast('error', 'Sharing not supported', 'Download the DO PDF, then attach it in WhatsApp.');
            return;
        }

        const originalText = button ? button.textContent : '';
        if (button) {
            button.disabled = true;
            button.textContent = 'Preparing PDF...';
        }

        buildDeliveryOrderPdf(job)
            .then(blob => {
                const fileName = doFileName(job);
                const file = new File([blob], fileName, {type:'application/pdf'});
                if (!navigator.canShare || !navigator.canShare({files:[file]})) {
                    downloadBlob(blob, fileName);
                    toast('info', 'PDF downloaded', 'Attach the downloaded DO PDF in WhatsApp.');
                    return null;
                }

                return navigator.share({
                    title: fileName.replace(/\.pdf$/i, ''),
                    text: 'Delivery Order PDF',
                    files: [file]
                });
            })
            .catch(error => {
                if (error && error.name === 'AbortError') return;
                toast('error', 'Unable to share DO PDF', 'Download the DO PDF, then share it in WhatsApp.');
            })
            .finally(() => {
                if (button) {
                    button.disabled = false;
                    button.textContent = originalText || 'Share WhatsApp';
                }
            });
    }

    document.addEventListener('click', function(e) {
        const panelBtn = e.target.closest('[data-open-panel]');
        if (panelBtn && root.contains(panelBtn)) {
            e.preventDefault();
            if (panelBtn.dataset.setFilter) deliveryFilter = panelBtn.dataset.setFilter;
            openPanel(panelBtn.dataset.openPanel);
            renderDeliveryList();
            return;
        }

        const statBtn = e.target.closest('[data-stat-filter]');
        if (statBtn && root.contains(statBtn)) {
            deliveryFilter = statBtn.dataset.statFilter || 'assigned';
            openPanel(deliveryFilter === 'returns' ? 'deliveries' : 'deliveries');
            renderDeliveryList();
            return;
        }

        const selectBtn = e.target.closest('[data-select-delivery]');
        if (selectBtn && root.contains(selectBtn)) {
            const id = Number(selectBtn.dataset.selectDelivery);
            const found = activeJobs.find(j => Number(j.id) === id);
            if (found) {
                activeJob = found;
                renderActive();
                openPanel('active');
            }
            return;
        }

        const printBtn = e.target.closest('[data-print-basket-receipt]');
        if (printBtn && root.contains(printBtn)) {
            e.preventDefault();
            printBasketReceipt(printBtn.dataset.printBasketReceipt);
            return;
        }

        const shareBtn = e.target.closest('[data-share-basket-receipt]');
        if (shareBtn && root.contains(shareBtn)) {
            e.preventDefault();
            shareBasketReceipt(shareBtn.dataset.shareBasketReceipt, shareBtn);
            return;
        }

        const downloadDoBtn = e.target.closest('[data-download-do]');
        if (downloadDoBtn && root.contains(downloadDoBtn)) {
            e.preventDefault();
            downloadDeliveryOrder(downloadDoBtn.dataset.downloadDo, downloadDoBtn);
            return;
        }

        const shareDoBtn = e.target.closest('[data-share-do]');
        if (shareDoBtn && root.contains(shareDoBtn)) {
            e.preventDefault();
            shareDeliveryOrder(shareDoBtn.dataset.shareDo, shareDoBtn);
            return;
        }
    });

    document.addEventListener('submit', async function(e) {
        if (!root.contains(e.target)) return;

        if (e.target && e.target.id === 'bdoBasketReturnForm') {
            if (!$('bdo_br_debtor_code').value.trim()) {
                e.preventDefault();
                toast('error', 'Select customer');
                return;
            }

            if (whole($('bdoBrQty').value) <= 0) {
                e.preventDefault();
                toast('error', 'Basket qty must be more than 0');
                return;
            }
        }

        if (e.target && e.target.classList.contains('bdo-shortage-form') && e.target.dataset.bdoConfirmed !== '1') {
            e.preventDefault();

            const ok = await confirmModal({
                icon: 'warning',
                title: 'Send back to staff?',
                text: 'Use this when items are not enough and staff needs to edit and reprint this DO.',
                confirmButtonText: 'Send to staff',
                confirmButtonColor: '#b45309'
            });

            if (!ok) {
                return;
            }

            e.target.dataset.bdoConfirmed = '1';
            e.target.requestSubmit();
            return;
        }

        if (e.target && e.target.id === 'bdoCompleteDeliveryForm' && e.target.dataset.bdoConfirmed !== '1') {
            const file = e.target.querySelector('input[type="file"]');
            if (!file || !file.files || !file.files.length) {
                e.preventDefault();

                const ok = await confirmModal({
                    icon: 'question',
                    title: 'No proof photo',
                    text: 'Continue marking this delivery as delivered without a photo?',
                    confirmButtonText: 'Mark delivered'
                });

                if (!ok) {
                    return;
                }

                e.target.dataset.bdoConfirmed = '1';
                e.target.requestSubmit();
                return;
            }
        }

        if (e.defaultPrevented) return;

        if (e.target && e.target.matches('form')) {
            if (submittingForm) {
                e.preventDefault();
                return;
            }

            submittingForm = true;
            const submitButton = e.target.querySelector('button[type="submit"]');
            if (submitButton) {
                submitButton.dataset.originalText = submitButton.textContent;
                submitButton.textContent = submitButton.dataset.bdoLoadingText || 'Saving...';
                submitButton.disabled = true;
            }
        }
    });

    async function searchDebtors(q) {
        const url = `${cfg.ajaxUrl}?action=ac_cs_debtor_search&nonce=${encodeURIComponent(cfg.debtorNonce)}&q=${encodeURIComponent(q)}`;
        const res = await fetch(url, {credentials:'same-origin'});
        const data = await res.json();
        if (!data.success) throw new Error(data.data?.error || 'Search failed');
        return (data.data?.items || []).map(it => {
            const name = it.name || it.debtorName || '';
            const code = it.code || it.debtorCode || '';
            return {label:name || code, raw:{name, code}};
        });
    }

    const picker = {items:[], timer:null};
    function pickerNote(msg) { $('bdoPickerResults').innerHTML = `<div class="bdo-picker-note">${esc(msg)}</div>`; }
    function renderPickerItems(items) {
        if (!items.length) return pickerNote('No result found');
        $('bdoPickerResults').innerHTML = items.map((it, idx) => `<button type="button" class="bdo-picker-item" data-idx="${idx}"><span class="bdo-picker-main">${esc(it.label)}</span></button>`).join('');
    }
    function openPicker() {
        picker.items = [];
        $('bdoPickerSearch').value = '';
        $('bdoPickerModal').classList.add('active');
        pickerNote('Type to search');
        setTimeout(() => $('bdoPickerSearch').focus(), 80);
    }
    function closePicker() {
        $('bdoPickerModal').classList.remove('active');
        $('bdoPickerSearch').value = '';
        $('bdoPickerResults').innerHTML = '';
        picker.items = [];
    }
    function setBrCustomer(c) {
        $('bdoBrCustomerInput').value = c.name || c.code || '';
        $('bdo_br_debtor_code').value = c.code || '';
        $('bdo_br_debtor_name').value = c.name || '';
        $('bdoBrCustomerClear').classList.toggle('show', !!$('bdoBrCustomerInput').value.trim());
    }
    function clearBrCustomer() {
        $('bdoBrCustomerInput').value = '';
        $('bdo_br_debtor_code').value = '';
        $('bdo_br_debtor_name').value = '';
        $('bdoBrCustomerClear').classList.remove('show');
    }

    $('bdoBrCustomerInput')?.addEventListener('click', openPicker);
    $('bdoBrCustomerClear')?.addEventListener('click', clearBrCustomer);
    $('bdoPickerClose')?.addEventListener('click', closePicker);
    $('bdoPickerBackdrop')?.addEventListener('click', closePicker);
    $('bdoPickerSearch')?.addEventListener('input', e => {
        clearTimeout(picker.timer);
        const q = String(e.target.value || '').trim();
        if (q.length < 1) {
            pickerNote('Type to search');
            return;
        }
        picker.timer = setTimeout(async () => {
            pickerNote('Searching...');
            try {
                picker.items = await searchDebtors(q);
                renderPickerItems(picker.items);
            } catch (err) {
                pickerNote('Failed to load');
            }
        }, 220);
    });
    $('bdoPickerResults')?.addEventListener('click', e => {
        const btn = e.target.closest('[data-idx]');
        if (!btn) return;
        const item = picker.items[Number(btn.dataset.idx)];
        if (item) {
            setBrCustomer(item.raw);
            closePicker();
        }
    });
    setStats(stats);
    renderActive();
    const initialTab = root.dataset.initialTab || 'home';
    if (['home','deliveries','active','return'].includes(initialTab)) openPanel(initialTab);
})();
</script>
HTML;

$replacements = [
    '__AJAX_URL__'            => esc_url($ajax_url),
    '__DEBTOR_NONCE__'        => esc_attr($debtor_nonce),
    '__ACTIVE_JOBS_JSON__'    => esc_attr(wp_json_encode($active_jobs) ?: '[]'),
    '__DELIVERED_JOBS_JSON__' => esc_attr(wp_json_encode($delivered_jobs) ?: '[]'),
    '__RETURN_ROWS_JSON__'    => esc_attr(wp_json_encode($return_rows) ?: '[]'),
    '__BASKET_RECEIPTS_JSON__' => esc_attr(wp_json_encode($basket_receipts) ?: '{}'),
    '__SELECTED_BASKET_RECEIPT_ID__' => esc_attr($selected_basket_receipt ? (string) $selected_basket_receipt['id'] : ''),
    '__STATS_JSON__'          => esc_attr(wp_json_encode($stats) ?: '{}'),
    '__INITIAL_TAB__'         => esc_attr($initial_tab),
    '__INITIAL_FILTER__'      => esc_attr($initial_filter),
    '__LOGOUT_URL__'          => esc_url(wp_logout_url(wp_login_url())),
    '__DRIVER_NAME__'         => esc_html($driver_name),
    '__GREETING__'            => esc_html($greeting),
    '__ALERT_HTML__'          => $alert_html,
    '__FORM_NONCE__'          => esc_attr($form_nonce),
];

echo strtr($html, $replacements);)��*��

Youez - 2016 - github.com/yon3zu
LinuXploit