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/inventorysearch/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : C:/Program Files/MariaDB 10.6/data/inventorysearch/wp_snippets.ibd
��������
P����@��������������������������&&����������������������
P��"Q ���������d<��d<��w���������
P������������������������������������i������������������������������������������������������������������������������������������������������������������������������������������������������i�	
���������������������������������������������������������������������������������������������������������������������������������i������������������������������������������������������������������������������������������������������������������������������������������������������i���������������������������������������������������������������������������������������������������������������������������������������������������������i������������������������������������������������������������������������������������������������������������������������������������������������������i��������������������������������������������������������������������������������������������������������������������������������
P���������������ϟE��������2infimumsupremum��!�΀
	pc�ϟ�߹���������!�E��Ѐ�/e���r��Zinfimumsupremum+global�	��front-end� )global�(��content�0global�8global�@global�Hglobal�Pglobal�	Xglobal�
`global�hglobal�pglobal�
xglobal��global��global����global�p^�c�!��%{���������	J#E��f�/yR����2infimumsupremum���� ��(��0b��8b��@��HT��	Pb��
X����`�䀀hF��p����x��
��Ȁ����������p�3�c�	J#�aj������o9E������!infimumsupremum
4��&��Make upload filenames lowercaseMakes sure that image and file uploads have lowercase filenames.

This is a sample snippet. Feel free to use it, edit it, or remove it.add_filter( 'sanitize_file_name', 'mb_strtolower' );sample, mediaglobal��
���TAs�	s��c��Disable admin barTurns off the WordPress admin bar for everyone except administrators.

This is a sample snippet. Feel free to use it, edit it, or remove it.add_action( 'wp', function () {
	if ( ! current_user_can( 'manage_options' ) ) {
		show_admin_bar( false );
	}
} );sample, admin-barfront-end��
���TAs���r
 ��Allow smiliesAllows smiley conversion in obscure places.

This is a sample snippet. Feel free to use it, edit it, or remove it.add_filter( 'widget_text', 'convert_smilies' );
add_filter( 'the_title', 'convert_smilies' );
add_filter( 'wp_title', 'convert_smilies' );
add_filter( 'get_bloginfo', 'convert_smilies' );sampleglobal��
���TAs�
��(���Current yearShortcode for inserting the current year into a post or page..

This is a sample snippet. Feel free to use it, edit it, or remove it.<?php echo date( 'Y' ); ?>sample, datescontent��
���TAs���0���MSSQL Connection (AutoCount)/* ============================================================
   SQL SERVER 2016  →  AED_SYARIKAT
   ============================================================ */
if (!function_exists('get_mssql_2016')) {

    function get_mssql_2016()
    {
        static $conn2016 = null;

        if ($conn2016 !== null) {
            return $conn2016;
        }

        // SQL Server 2016
        $server   = "192.168.100.225,19416\\MSSQL2016SERVER";
        $database = "AED_SYARIKAT";
		// $database = "AED_HENKCO";
        $username = "sa";
        $password = "user2025**";

        $connectionInfo = [
            "Database"               => $database,
            "UID"                    => $username,
            "PWD"                    => $password,
            "CharacterSet"           => "UTF-8",
            "Encrypt"                => "yes",
            "TrustServerCertificate" => "yes",
            "LoginTimeout"           => 5,
        ];

        $conn2016 = sqlsrv_connect($server, $connectionInfo);

        if ($conn2016 === false) {
            error_log('MSSQL 2016 connection failed: ' . print_r(sqlsrv_errors(), true));
            return null;
        }

        return $conn2016;
    }
}

/* ============================================================
   SQL SERVER 2019  →  AED_YTL_21122025
   ============================================================ */
if (!function_exists('get_mssql_2019')) {

    function get_mssql_2019()
    {
        static $conn2019 = null;

        if ($conn2019 !== null) {
            return $conn2019;
        }

        $server   = "192.168.100.225\\MSSQL2019SERVER";
        $database = "AED_YTL_26052026";
        $username = "sa";
        $password = "user2025**";

        $connectionInfo = [
            "Database"               => $database,
            "UID"                    => $username,
            "PWD"                    => $password,
            "CharacterSet"           => "UTF-8",
            "Encrypt"                => "yes",
            "TrustServerCertificate" => "yes",
            "LoginTimeout"           => 5,
        ];

        $conn2019 = sqlsrv_connect($server, $connectionInfo);

        if ($conn2019 === false) {
            error_log('MSSQL 2019 connection failed: ' . print_r(sqlsrv_errors(), true));
            return null;
        }

        return $conn2019;
    }
}

/* ============================================================
   SQL SERVER 2019  →  MYKCO_v150505
   ============================================================ */
if (!function_exists('get_mssql_2019_img')) {
  function get_mssql_2019_img()
  {
    static $conn2019_img = null;

    if ($conn2019_img !== null) return $conn2019_img;

    $server   = "192.168.100.225\\MSSQL2019SERVER";
    $database = "MYKCO_v150505";
    $username = "sa";
    $password = "user2025**";

    $connectionInfo = [
      "Database"               => $database,
      "UID"                    => $username,
      "PWD"                    => $password,
      "CharacterSet"           => "UTF-8",
      "Encrypt"                => "yes",
      "TrustServerCertificate" => "yes",
      "LoginTimeout"           => 5,
    ];

    $conn2019_img = sqlsrv_connect($server, $connectionInfo);

    if ($conn2019_img === false) {
      error_log('MSSQL 2019 IMG connection failed: ' . print_r(sqlsrv_errors(), true));
      return null;
    }

    return $conn2019_img;
  }
}

/* ============================================================
   SQL SERVER 2019  →  AED_BLUECON
   ============================================================ */
if (!function_exists('get_mssql_case_sale')) {

  function get_mssql_case_sale()
  {
    static $conn_case_sale = null;

    if ($conn_case_sale !== null) return $conn_case_sale;

    $server   = "192.168.100.225\\MSSQL2019SERVER";
    $database = "AED_BLUECON";
    $username = "sa";
    $password = "user2025**";

    $connectionInfo = [
      "Database"               => $database,
      "UID"                    => $username,
      "PWD"                    => $password,
      "CharacterSet"           => "UTF-8",
      "Encrypt"                => "yes",
      "TrustServerCertificate" => "yes",
      "LoginTimeout"           => 5,
    ];

    $conn_case_sale = sqlsrv_connect($server, $connectionInfo);

    if ($conn_case_sale === false) {
      error_log('MSSQL 2019 case_sale connection failed: ' . print_r(sqlsrv_errors(), true));
      return null;
    }

    return $conn_case_sale;
  }
}

/* ============================================================
   SQL SERVER 2019  →  AED_TEST_API_09012026
   Dedicated Cash Sale connection
   ============================================================ */
if (!function_exists('get_mssql_cash_sale_test_db')) {
    function get_mssql_cash_sale_test_db() {
        $server   = "192.168.100.225\\MSSQL2019SERVER";
        $database = "AED_TEST_API_09012026";
        $username = "sa";
        $password = "user2025**";

        $conn = sqlsrv_connect($server, [
            "Database"               => $database,
            "UID"                    => $username,
            "PWD"                    => $password,
            "CharacterSet"           => "UTF-8",
            "Encrypt"                => "yes",
            "TrustServerCertificate" => "yes",
            "LoginTimeout"           => 5,
        ]);

        if ($conn === false) {
            error_log('MSSQL Cash Sale Test DB connection failed: ' . print_r(sqlsrv_errors(), true));
            return null;
        }

        return $conn;
    }
}global��
���0Nˀpc�o9�J��������,�-
�-����add_shortcode('ac_hybrid_scanner', function () {
  if (!is_user_logged_in()) {
    return '<div style="padding:12px;border:1px solid #fca5a5;border-radius:12px;background:#fff1f2;color:#991b1b;">Please login.</div>';
  }

  $rest_url = esc_url_raw(rest_url('ac/v1/job'));
  $nonce    = wp_create_nonce('wp_rest');

  $uid = 'acscan_' . wp_generate_uuid4();

  ob_start(); ?>
  <div class="ac-scan-card" id="<?php echo esc_attr($uid); ?>">
    <div class="ac-scan-row">
      <div class="ac-scan-col">
        <label class="ac-label">Device Mode</label>
        <select class="ac-input acMode">
          <option value="keyboard">Swift / Hardware Scanner (Keyboard)</option>
          <option value="camera" selected>Mobile / Camera (Quagga2)</option>
        </select>
      </div>

      <div class="ac-scan-col">
        <label class="ac-label">Action</label>
        <select class="ac-input acAction">
          <option value="STOCK_RECEIVE">Stock Receive</option>
          <option value="STOCK_ISSUE">Stock Issue</option>
          <option value="STOCK_TRANSFER">Stock Transfer</option>
        </select>
      </div>

      <div class="ac-scan-col">
        <label class="ac-label">Qty</label>
        <input class="ac-input acQty" type="number" min="1" step="1" value="1" />
      </div>

      <div class="ac-scan-col">
        <label class="ac-label">Auto Submit</label>
        <select class="ac-input acAutoSubmit">
          <option value="1">On</option>
          <option value="0" selected>Off</option>
        </select>
      </div>
    </div>

    <label class="ac-label" style="margin-top:10px;">Item Code (scan with hardware scanner or type)</label>
    <input class="ac-input acItemCode" type="text" placeholder="Tap here, then scan…" autocomplete="off" inputmode="text" />

    <div class="ac-scan-actions">
      <button type="button" class="ac-btn ac-btn-primary acBtnCamera">📷 Use Camera</button>
      <button type="button" class="ac-btn ac-btn-danger acBtnStop">Stop</button>
      <button type="button" class="ac-btn ac-btn-muted acBtnClear">Clear</button>
      <button type="button" class="ac-btn ac-btn-dark acBtnSubmit">Submit</button>
      <span class="ac-status acStatus">Ready</span>
    </div>

    <div class="ac-video-wrap" style="display:none;">
      <div class="ac-video-frame">
        <div class="ac-video-target"></div>
        <div class="ac-aim-box"></div>
      </div>
      <div class="ac-hint">Tip: hold ~15–25cm, keep barcode inside box, avoid glare. (HTTPS required on iPhone)</div>
    </div>
  </div>

  <style>
    .ac-scan-card{max-width:860px;margin:16px auto;padding:14px;border:1px solid #e5e7eb;border-radius:14px;background:#fff}
    .ac-scan-row{display:flex;gap:10px;flex-wrap:wrap}
    .ac-scan-col{flex:1;min-width:200px}
    .ac-label{display:block;font-size:12px;color:#6b7280;margin:0 0 6px}
    .ac-input{width:100%;padding:10px 12px;border:1px solid #d1d5db;border-radius:10px;font-size:14px;outline:none;background:#fff}
    .ac-input:focus{border-color:#60a5fa;box-shadow:0 0 0 3px rgba(96,165,250,.25)}
    .ac-scan-actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:10px}

    .ac-btn{padding:10px 14px;border-radius:10px;border:1px solid transparent;cursor:pointer;font-weight:600}
    .ac-btn-primary{background:#2563eb;color:#fff;border-color:#2563eb}
    .ac-btn-dark{background:#111827;color:#fff;border-color:#111827}
    .ac-btn-danger{background:#dc2626;color:#fff;border-color:#dc2626}
    .ac-btn-muted{background:#f3f4f6;color:#111827;border-color:#d1d5db}
    .ac-btn:disabled{opacity:.55;cursor:not-allowed}

    .ac-status{font-size:12px;color:#6b7280}

    .ac-video-wrap{margin-top:12px}
    .ac-video-frame{position:relative;width:100%;border-radius:12px;overflow:hidden;background:#000}
    /* Quagga will inject a <video> into this */
    .ac-video-target video{width:100%;display:block}

    .ac-aim-box{
      position:absolute; left:50%; top:50%;
      width:70%; height:35%;
      transform:translate(-50%,-50%);
      border:2px solid rgba(255,255,255,.9);
      border-radius:12px;
      box-shadow:0 0 0 9999px rgba(0,0,0,.15);
      pointer-events:none;
    }
    .ac-hint{margin-top:8px;font-size:12px;color:#6b7280}
			 
.ac-video-frame{
  position:relative;
  width:100%;
  aspect-ratio: 4 / 3;   /* ← IMPORTANT */
  background:#000;
  border-radius:12px;
  overflow:hidden;
}

/* Quagga injects video + canvas here */
.ac-video-target,
.ac-video-target video,
.ac-video-target canvas{
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  object-fit:cover;     /* ← IMPORTANT */
}

  </style>

  <script>
  (function(){
    // Load Quagga2 only once
    function loadScriptOnce(src){
      return new Promise((resolve, reject) => {
        if (document.querySelector('script[data-ac-quagga="1"]')) return resolve();
        const s = document.createElement('script');
        s.src = src;
        s.async = true;
        s.dataset.acQuagga = "1";
        s.onload = resolve;
        s.onerror = () => reject(new Error('Failed to load Quagga2 library'));
        document.head.appendChild(s);
      });
    }

    const ROOT = document.getElementById(<?php echo json_encode($uid); ?>);
    const REST_URL   = <?php echo json_encode($rest_url); ?>;
    const REST_NONCE = <?php echo json_encode($nonce); ?>;

    const elMode   = ROOT.querySelector('.acMode');
    const elAction = ROOT.querySelector('.acAction');
    const elQty    = ROOT.querySelector('.acQty');
    const elAuto   = ROOT.querySelector('.acAutoSubmit');
    const elCode   = ROOT.querySelector('.acItemCode');

    const elBtnCam   = ROOT.querySelector('.acBtnCamera');
    const elBtnStop  = ROOT.querySelector('.acBtnStop');
    const elBtnClear = ROOT.querySelector('.acBtnClear');
    const elBtnSub   = ROOT.querySelector('.acBtnSubmit');

    const elStatus = ROOT.querySelector('.acStatus');
    const elVideoWrap = ROOT.querySelector('.ac-video-wrap');
    const elVideoTarget = ROOT.querySelector('.ac-video-target');

    let isCameraRunning = false;
    let lastSubmitAt = 0;

    // Prevent repeated triggers (same barcode)
    let lastScanText = '';
    let lastScanTime = 0;

    function setStatus(t){ elStatus.textContent = t; }

    function normalize(v){
      return (v || '').replace(/[\r\n]+/g,'').trim();
    }

    function canAcceptScan(text){
      const now = Date.now();
      if (text === lastScanText && (now - lastScanTime) < 1200) return false;
      lastScanText = text;
      lastScanTime = now;
      return true;
    }

    async function submitJob(){
      const now = Date.now();
      if (now - lastSubmitAt < 400) return;
      lastSubmitAt = now;

      const itemCode = normalize(elCode.value);
      const qty = parseInt(elQty.value || '1', 10);

      if (!itemCode) { setStatus('❌ Item code is empty'); elCode.focus(); return; }
      if (!Number.isFinite(qty) || qty < 1) { setStatus('❌ Qty must be >= 1'); elQty.focus(); return; }

      const payload = {
        type: elAction.value,
        payload: { itemCode, qty }
      };

      setStatus('Submitting…');

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

        const data = await res.json().catch(()=> ({}));
        if (!res.ok) {
          setStatus('❌ Submit failed: ' + (data.message || res.status));
          return;
        }

        setStatus('✅ Submitted (JobId: ' + (data.jobId || 'OK') + ')');
        elCode.value = '';
        elCode.focus();

      }catch(e){
        console.error(e);
        setStatus('❌ Network/REST error');
      }
    }

    // Hardware scanner path
    elCode.addEventListener('keydown', (e) => {
      if (e.key === 'Enter') {
        e.preventDefault();
        submitJob();
      }
    });

    function stopCameraScan(){
      try{
        if (window.Quagga) {
          window.Quagga.offDetected(onDetectedSafe);
          window.Quagga.stop();
        }
      } catch (e) { console.error(e); }

      // clean injected video
      elVideoTarget.innerHTML = '';

      isCameraRunning = false;
      elVideoWrap.style.display = 'none';
      setStatus('Ready');
      elCode.focus();
    }

    function onDetectedSafe(result){
      try{
        if (!result || !result.codeResult || !result.codeResult.code) return;
        const text = result.codeResult.code;

        // Quagga decoder is already restricted to code_128
        if (!canAcceptScan(text)) return;

        elCode.value = text;
        setStatus('✅ Found: ' + text);

        if (elAuto.value === '1') {
          submitJob();
        }
      } catch (e) {
        console.error(e);
      }
    }

    async function startCameraScan(){
      try{
        if (isCameraRunning) return;

        if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
          throw new Error('Camera not available in this browser.');
        }

        setStatus('Loading scanner…');
        await loadScriptOnce('https://unpkg.com/@ericblade/quagga2/dist/quagga.min.js');

        if (!window.Quagga) throw new Error('Quagga2 not loaded');

        elVideoWrap.style.display = 'block';
        setStatus('Starting camera…');

        isCameraRunning = true;

        // Important: Quagga2 uses getUserMedia internally
window.Quagga.init({
  inputStream: {
    name: "Live",
    type: "LiveStream",
    target: elVideoTarget,
    constraints: {
      facingMode: "environment",
      width: { ideal: 1280 },
      height:{ ideal: 720 }
    },
    area: {
      top: "25%",
      right: "10%",
      left: "10%",
      bottom: "25%"
    }
  },
  decoder: {
    readers: ["code_128_reader"]
  },
  locate: true,
  locator: {
    patchSize: "medium",
    halfSample: true
  },
  numOfWorkers: 2,      // keep low for mobile
  frequency: 8          // smoother + less CPU
}, function(err){
  if (err) {
    console.error(err);
    stopCameraScan();
    setStatus('❌ Camera init failed');
    return;
  }
  Quagga.start();
  Quagga.onDetected(onDetectedSafe);
  setStatus('Scanning Code 128…');
});

      } catch (e) {
        console.error(e);
        stopCameraScan();
        setStatus('❌ ' + (e.message || 'Camera start failed'));
      }
    }

    function applyMode(){
      if (elMode.value === 'keyboard') {
        stopCameraScan();
        setStatus('Ready (scan using Swift / hardware scanner)…');
        elBtnCam.disabled = true;
        elBtnCam.style.opacity = '0.55';
        elCode.focus();
      } else {
        setStatus('Tap "Use Camera" to start scanning…');
        elBtnCam.disabled = false;
        elBtnCam.style.opacity = '1';
      }
    }

    elMode.addEventListener('change', applyMode);

    elBtnCam.addEventListener('click', () => {
      elMode.value = 'camera';
      applyMode();
      startCameraScan();
    });

    elBtnStop.addEventListener('click', stopCameraScan);

    elBtnClear.addEventListener('click', () => {
      elCode.value = '';
      lastScanText = '';
      lastScanTime = 0;
      setStatus('Cleared');
      elCode.focus();
    });

    elBtnSub.addEventListener('click', submitJob);

    // Defaults requested: Camera mode + AutoSubmit OFF
    setTimeout(() => {
      applyMode();
      elCode.focus();
    }, 150);
  })();
  </script>
  <?php
  return ob_get_clean();
});
,�-��y	�_FE��;��	+��!infimumsupremumf����AJAX handler (item search)add_action('wp_ajax_ac_itemcode_suggest', 'ac_itemcode_suggest');
add_action('wp_ajax_nopriv_ac_itemcode_suggest', 'ac_itemcode_suggest');

function ac_itemcode_suggest() {
  $nonce = $_GET['nonce'] ?? '';
  if (!wp_verify_nonce($nonce, 'ac_itemcode_suggest')) wp_send_json([]);

  $term = trim((string)($_GET['term'] ?? ''));
  if (mb_strlen($term) < 2) wp_send_json([]);

  $conn = get_mssql_2019();
  if (!$conn) wp_send_json([]);

  $termNorm = strtolower(preg_replace('/[^a-zA-Z0-9]/', '', $term));
  $like = '%' . $termNorm . '%';

  $sql = "
    SELECT TOP 20
      i.ItemCode AS code,
      i.Description AS [desc]
    FROM dbo.Item i
    WHERE LOWER(
      REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(i.ItemCode,' ',''),'-',''),'/',''),'(',''),')',''),'*',''),'.','')
    ) LIKE ?
    ORDER BY i.ItemCode
  ";

  $stmt = sqlsrv_query($conn, $sql, [$like]);
  if (!$stmt) wp_send_json([]);

  $out = [];
  while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
    $out[] = [
      'code' => (string)($r['code'] ?? ''),
      'desc' => (string)($r['desc'] ?? ''),
    ];
  }

  wp_send_json($out);
}
global��
����,���	p��AC AJAX Images Handler// ---------------------------
// AC AJAX: Get Images
// ---------------------------

add_action('wp_ajax_ac_get_images', 'ac_ajax_get_images');
add_action('wp_ajax_nopriv_ac_get_images', 'ac_ajax_get_images');

if (!function_exists('ac_ajax_get_images')) {
  function ac_ajax_get_images() {
    $nonce = isset($_GET['nonce']) ? (string)$_GET['nonce'] : '';
    if (!wp_verify_nonce($nonce, 'ac_get_images')) {
      wp_send_json_error(['message' => 'Bad nonce'], 403);
    }

    $item = isset($_GET['item']) ? trim((string)$_GET['item']) : '';
    if ($item === '') {
      wp_send_json_success([]);
    }

    if (!function_exists('get_mssql_2019')) {
      wp_send_json_error(['message' => 'Missing get_mssql_2019()'], 500);
    }

    $conn = get_mssql_2019();
    if (!$conn) {
      wp_send_json_error(['message' => 'DB connection error'], 500);
    }

    $imgs = ac_fetch_images_base64($conn, $item, 16);
    wp_send_json_success($imgs);
  }
}

if (!function_exists('ac_fetch_images_base64')) {
  function ac_fetch_images_base64($conn, $itemCode, $limit = 16) {
    $itemCode = trim((string)$itemCode);
    if ($itemCode === '') return [];

    $sql = "
      SELECT TOP ($limit) ImageData
      FROM dbo.Web_StockImages
      WHERE LTRIM(RTRIM(ItemCode)) = ?
        AND ImageData IS NOT NULL
      ORDER BY (SELECT NULL)
    ";

    $stmt = sqlsrv_query($conn, $sql, [$itemCode]);
    if (!$stmt) {
      error_log('ac_fetch_images_base64 failed: ' . print_r(sqlsrv_errors(), true));
      return [];
    }

    $out = [];
    while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
      $bin = $r['ImageData'] ?? null;
      if (is_resource($bin)) $bin = stream_get_contents($bin);
      if (!$bin) continue;

      $mime = 'image/jpeg';
      $head = substr($bin, 0, 12);

      if ($head !== false) {
        if (strncmp($head, "\x89PNG\r\n\x1a\n", 8) === 0) $mime = 'image/png';
        elseif (strncmp($head, "GIF87a", 6) === 0 || strncmp($head, "GIF89a", 6) === 0) $mime = 'image/gif';
        elseif (strncmp($head, "RIFF", 4) === 0 && substr($head, 8, 4) === "WEBP") $mime = 'image/webp';
        elseif (strncmp($head, "\xFF\xD8\xFF", 3) === 0) $mime = 'image/jpeg';
      }

      $out[] = "data:$mime;base64," . base64_encode($bin);
      if (count($out) >= $limit) break;
    }

    return $out;
  }
}
global��
����,ŀ�� ��Log Outadd_action('template_redirect', function () {

  if (!is_page('logout')) {
    return;
  }

  // Admin: don't logout, just redirect away
  if (current_user_can('administrator')) {
    wp_safe_redirect('https://website.ipohserver.com/InventorySearch/index.php/');
    exit;
  }

  // Non-admin: logout properly
  wp_logout();
  wp_safe_redirect('https://website.ipohserver.com/InventorySearch/index.php/');
  exit;
});
global��
���Ĝ���(b�	�Barcode Scanner�&-global��
����Ԁ܈0	1�
�Search Debtors by NAME/**
 * FULL REPLACEMENT (AJAX)
 * Debtor dropdown from CS only (distinct debtors who exist in CS and not cancelled)
 *
 * Action: ac_cs_debtor_search
 * Nonce:  ac_cs_debtor_search
 *
 * Requires:
 * - get_mssql_case_sale()
 *
 * Returns:
 * success: true
 * data: { items: [ {code,name,text}, ... ] }
 */

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

add_action('wp_ajax_ac_cs_debtor_search', 'ac_cs_debtor_search');
add_action('wp_ajax_nopriv_ac_cs_debtor_search', 'ac_cs_debtor_search');

function ac_cs_debtor_search() {
  // Nonce check
  $nonce = isset($_GET['nonce']) ? sanitize_text_field(wp_unslash($_GET['nonce'])) : '';
  if (!wp_verify_nonce($nonce, 'ac_cs_debtor_search')) {
    wp_send_json_error(['error' => 'Invalid nonce.'], 403);
  }

  // Input
  $q = isset($_GET['q']) ? trim((string)wp_unslash($_GET['q'])) : '';
  $q = preg_replace('/[\x00-\x1F\x7F]/u', '', $q);
  $q = mb_substr($q, 0, 80);

  if ($q === '') {
    wp_send_json_success(['items' => []]);
  }

  if (!function_exists('get_mssql_case_sale')) {
    wp_send_json_error(['error' => 'get_mssql_case_sale() not found.'], 500);
  }

  $conn = get_mssql_case_sale();
  if (!$conn) {
    wp_send_json_error(['error' => 'MSSQL connection failed.'], 500);
  }

  $like = '%' . $q . '%';

  $sql = "
    SELECT TOP 25
      DebtorCode AS code,
      DebtorName AS name,
      MAX(DocDate) AS last_doc_date
    FROM dbo.CS WITH (NOLOCK)
    WHERE ISNULL(Cancelled,'F') <> 'T'
      AND (
        ISNULL(DebtorName,'') LIKE ?
        OR ISNULL(DebtorCode,'') LIKE ?
      )
    GROUP BY DebtorCode, DebtorName
    ORDER BY last_doc_date DESC, DebtorName ASC
  ";

  $stmt = sqlsrv_query($conn, $sql, [$like, $like]);
  if ($stmt === false) {
    wp_send_json_error([
      'error' => 'Search query failed on CS.',
      'sqlsrv' => sqlsrv_errors()
    ], 500);
  }

  $items = [];
  while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
    $code = trim((string)($r['code'] ?? ''));
    $name = trim((string)($r['name'] ?? ''));

    if ($code === '' && $name === '') continue;

    $items[] = [
      'code' => $code,
      'name' => ($name !== '' ? $name : $code),
      'text' => ($name !== '' ? $name : $code),
    ];
  }
  sqlsrv_free_stmt($stmt);

  wp_send_json_success(['items' => $items]);
}global��
���8H��(�8���AC Item Search (Typeahead)// ============================================================
// AutoCount Item Suggest (LIVE SQL) — CODE + NAME + PRICE + BARCODE
// action: ac_itemcode_suggest
// nonce : ac_itemcode_suggest
// Requires: get_mssql()
// Tables: dbo.Item, dbo.ItemUOM
// ============================================================

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

add_action('wp_ajax_ac_itemcode_suggest', 'ac_itemcode_suggest_ajax');
add_action('wp_ajax_nopriv_ac_itemcode_suggest', 'ac_itemcode_suggest_ajax');

function ac_itemcode_suggest_ajax() {
    $nonce = isset($_POST['nonce']) ? (string)$_POST['nonce'] : '';
    if (!wp_verify_nonce($nonce, 'ac_itemcode_suggest')) {
        wp_send_json(['ok' => false, 'message' => 'Bad nonce'], 403);
    }

    if (!function_exists('get_mssql')) {
        wp_send_json(['ok' => false, 'message' => 'Missing get_mssql()'], 500);
    }

    $term = isset($_POST['term']) ? trim((string)$_POST['term']) : '';
    $term = preg_replace('/\s+/', ' ', $term);

    if (mb_strlen($term) < 2) {
        wp_send_json(['ok' => true, 'items' => []], 200);
    }

    $conn = get_mssql();
    if (!$conn) {
        wp_send_json(['ok' => false, 'message' => 'MSSQL connection failed'], 500);
    }

    // allow "#HNBR" to match "HNBR..."
    $termCode = ltrim($term, '#');

    // Escape LIKE wildcards for SQL Server: %, _, [
    $esc = function($s){
        return str_replace(['[', '%', '_'], ['[[]', '[%]', '[_]'], $s);
    };
    $safeCode = $esc($termCode);
    $safeName = $esc($term);

    $codeLike    = $safeCode . '%';
    $nameLike    = '%' . $safeName . '%';
    $barcodeLike = '%' . $safeName . '%';   // use $safeCode instead if barcodes are strictly numeric

    /**
     * Price source: dbo.ItemUOM.Price ONLY
     * Row pick:
     * 1) UOM = Item.SalesUOM
     * 2) UOM = Item.BaseUOM
     * 3) any row
     *
     * Added:
     * - Search barcode from dbo.ItemUOM.BarCode (any UOM row)
     */
    $sql = "
      SELECT TOP (12)
        i.ItemCode,
        i.Description,
        i.Desc2,
        ISNULL(pick.UOM, i.SalesUOM) AS UOM,
        CAST(ISNULL(pick.Price, 0) AS decimal(18,2)) AS UnitPrice
      FROM dbo.Item i
      OUTER APPLY (
        SELECT TOP (1)
          iu.UOM,
          iu.Price
        FROM dbo.ItemUOM iu
        WHERE iu.ItemCode = i.ItemCode
        ORDER BY
          CASE
            WHEN iu.UOM = i.SalesUOM THEN 0
            WHEN iu.UOM = i.BaseUOM  THEN 1
            ELSE 2
          END,
          iu.AutoKey DESC
      ) pick
      WHERE
        (i.ItemCode LIKE ?)
        OR (i.Description LIKE ?)
        OR (i.Desc2 LIKE ?)
        OR EXISTS (
            SELECT 1
            FROM dbo.ItemUOM iu2
            WHERE iu2.ItemCode = i.ItemCode
              AND iu2.BarCode LIKE ?
        )
      ORDER BY
        CASE
          WHEN EXISTS (
              SELECT 1
              FROM dbo.ItemUOM iu3
              WHERE iu3.ItemCode = i.ItemCode
                AND iu3.BarCode LIKE ?
          ) THEN 0
          WHEN i.ItemCode LIKE ? THEN 1
          WHEN i.Description LIKE ? THEN 2
          WHEN i.Desc2 LIKE ? THEN 3
          ELSE 4
        END,
        i.ItemCode ASC
    ";

    $params = [
        $codeLike,        // i.ItemCode LIKE ?
        $nameLike,        // i.Description LIKE ?
        $nameLike,        // i.Desc2 LIKE ?
        $barcodeLike,     // EXISTS barcode LIKE ?
        $barcodeLike,     // ORDER BY barcode EXISTS LIKE ?
        $codeLike,        // ORDER BY ItemCode LIKE ?
        $nameLike,        // ORDER BY Description LIKE ?
        $nameLike,        // ORDER BY Desc2 LIKE ?
    ];

    $stmt = @sqlsrv_query($conn, $sql, $params);
    if ($stmt === false) {
        wp_send_json(['ok' => false, 'message' => 'SQL error', 'detail' => sqlsrv_errors()], 500);
    }

    $items = [];
    while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
        $items[] = [
            'code'      => isset($r['ItemCode']) ? (string)$r['ItemCode'] : '',
            'desc'      => isset($r['Description']) ? (string)$r['Description'] : '',
            'desc2'     => isset($r['Desc2']) ? (string)$r['Desc2'] : '',
            'uom'       => isset($r['UOM']) ? (string)$r['UOM'] : '',
            'unitPrice' => isset($r['UnitPrice']) ? (string)$r['UnitPrice'] : '0.00',
        ];
    }
    sqlsrv_free_stmt($stmt);

    wp_send_json(['ok' => true, 'items' => $items], 200);
}global��
����,����@���AC Debtor Search (By Name)if (!defined('ABSPATH')) exit;

add_action('wp_ajax_ac_cs_debtor_search', 'ac_cs_debtor_search');
add_action('wp_ajax_nopriv_ac_cs_debtor_search', 'ac_cs_debtor_search');

function ac_cs_debtor_search() {
    // Verify nonce
    $nonce = isset($_GET['nonce']) ? sanitize_text_field(wp_unslash($_GET['nonce'])) : '';
    if (!wp_verify_nonce($nonce, 'ac_cs_debtor_search')) {
        wp_send_json_error(['error' => 'Invalid nonce.'], 403);
    }

    // Get and sanitize search term
    $q = isset($_GET['q']) ? trim((string) wp_unslash($_GET['q'])) : '';
    $q = preg_replace('/[\x00-\x1F\x7F]/u', '', $q);
    $q = mb_substr($q, 0, 80);

    if ($q === '') {
        wp_send_json_success(['items' => []]);
    }

    // Ensure MSSQL function exists
    if (!function_exists('get_mssql')) {
        wp_send_json_error(['error' => 'get_mssql() not found.'], 500);
    }

    // Connect to MSSQL
    $conn = get_mssql();
    if (!$conn) {
        wp_send_json_error(['error' => 'MSSQL connection failed.'], 500);
    }

    $like = '%' . $q . '%';

    $sql = "
    ;WITH cs AS (
      SELECT
        CAST(DebtorCode AS nvarchar(50))  AS code,
        CAST(DebtorName AS nvarchar(200)) AS name,
        MAX(DocDate) AS last_doc_date,
        CAST(MAX(CAST(SalesAgent AS nvarchar(50))) AS nvarchar(50)) AS salesAgent,
        1 AS from_cs
      FROM dbo.CS WITH (NOLOCK)
      WHERE ISNULL(Cancelled,'F') <> 'T'
        AND (
          ISNULL(DebtorName,'') LIKE ?
          OR ISNULL(DebtorCode,'') LIKE ?
        )
        AND ISNULL(DebtorCode,'') <> ''
        AND ISNULL(DebtorName,'') <> ''
      GROUP BY DebtorCode, DebtorName
    ),
    d AS (
      SELECT
        CAST(AccNo AS nvarchar(50))        AS code,
        CAST(CompanyName AS nvarchar(200)) AS name,
        CAST(NULL AS datetime) AS last_doc_date,
        CAST(MAX(CAST(SalesAgent AS nvarchar(50))) AS nvarchar(50)) AS salesAgent,
        0 AS from_cs
      FROM dbo.Debtor WITH (NOLOCK)
      WHERE ISNULL(DebtorType,'') = 'CS'
        AND ISNULL(IsActive,'F') = 'T'
        AND (
          ISNULL(CompanyName,'') LIKE ?
          OR ISNULL(AccNo,'') LIKE ?
        )
        AND ISNULL(AccNo,'') <> ''
        AND ISNULL(CompanyName,'') <> ''
      GROUP BY AccNo, CompanyName
    ),
    u AS (
      SELECT * FROM cs
      UNION ALL
      SELECT * FROM d
    ),
    final AS (
      -- Dedupe only when code+name match
      SELECT
        code,
        name,
        MAX(last_doc_date) AS last_doc_date,
        COALESCE(
          MAX(CASE WHEN from_cs = 1 THEN salesAgent END),
          MAX(salesAgent),
          ''
        ) AS salesAgent,
        MAX(from_cs) AS has_cs
      FROM u
      GROUP BY code, name
    )
    SELECT TOP 25
      code, name, salesAgent, last_doc_date
    FROM final
    ORDER BY
      has_cs DESC,
      last_doc_date DESC,
      name ASC,
      code ASC
    ";

    // Execute query with parameters
    $stmt = sqlsrv_query($conn, $sql, [$like, $like, $like, $like]);

    if ($stmt === false) {
        $errs = sqlsrv_errors();
        sqlsrv_close($conn);
        wp_send_json_error([
            'error'  => 'Search query failed (CS + Debtor).',
            'sqlsrv' => $errs
        ], 500);
    }

    // Build results array
    $items = [];
    while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
        $code       = trim((string) ($r['code'] ?? ''));
        $name       = trim((string) ($r['name'] ?? ''));
        $salesAgent = trim((string) ($r['salesAgent'] ?? ''));

        if ($code === '' && $name === '') continue;

        $display = ($name !== '' ? $name : $code);

        $items[] = [
            'code'       => $code,
            'name'       => $display,
            'debtorType' => 'CS',
            'salesAgent' => $salesAgent,
            'text'       => ($code !== '' ? ($display . ' — ' . $code) : $display),
        ];
    }

    sqlsrv_free_stmt($stmt);
    sqlsrv_close($conn);

    wp_send_json_success(['items' => $items]);
}global��
����,��pc�_F�Q�	����
P��E��9��-,�!infimumsupremum݌
6�
�AC Cash Sale Fetch (DocNo)if (!defined('ABSPATH')) exit;

add_action('wp_ajax_ac_cs_get_by_docno', 'ac_cs_get_by_docno');
add_action('wp_ajax_nopriv_ac_cs_get_by_docno', 'ac_cs_get_by_docno');

function ac_cs_get_by_docno() {
  $nonce = isset($_GET['nonce']) ? sanitize_text_field(wp_unslash($_GET['nonce'])) : '';
  if (!wp_verify_nonce($nonce, 'ac_cs_get_by_docno')) {
    wp_send_json_error(['error' => 'Invalid nonce.'], 403);
  }

  if (!function_exists('get_mssql')) {
    wp_send_json_error(['error' => 'Missing get_mssql()'], 500);
  }

  $docNo = isset($_GET['docno']) ? trim((string)wp_unslash($_GET['docno'])) : '';
  if ($docNo === '') {
    wp_send_json_error(['error' => 'docno is required'], 400);
  }

  $conn = get_mssql();
  if (!$conn) {
    wp_send_json_error(['error' => 'MSSQL connection failed'], 500);
  }

  // ---- HEADER (safe string‑only cancelled filter) ----
  $sqlH = "
    SELECT TOP 1
      CS.DocKey,
      CS.DocNo,
      CS.DocDate,
      CS.DebtorCode,
      ISNULL(D.CompanyName, '') AS DebtorName
    FROM dbo.CS CS
    LEFT JOIN dbo.Debtor D ON D.AccNo = CS.DebtorCode
    WHERE CS.DocNo = ?
      AND ISNULL(CS.Cancelled, '') IN ('', '0', 'F', 'N')
  ";

  $stH = sqlsrv_query($conn, $sqlH, [$docNo]);
  if (!$stH) {
    wp_send_json_error([
      'error'  => 'SQL error (header)',
      'detail' => sqlsrv_errors(),
    ], 500);
  }

  $h = sqlsrv_fetch_array($stH, SQLSRV_FETCH_ASSOC);
  sqlsrv_free_stmt($stH);

  if (!$h) {
    wp_send_json_error(['error' => 'Cash Sale not found (or cancelled).'], 404);
  }

  // ---- LINES (with SubTotal) ----
  $sqlL = "
    SELECT
      d.Seq,
      d.ItemCode,
      ISNULL(d.Description, '') AS LineDesc,
      ISNULL(d.UOM, '') AS UOM,
      CAST(ISNULL(d.Qty, 0) AS float) AS Qty,
      CAST(ISNULL(d.UnitPrice, 0) AS float) AS UnitPrice,
      CAST(ISNULL(d.SubTotal, 0) AS float) AS SubTotal
    FROM dbo.CSDTL d
    WHERE d.DocKey = ?
    ORDER BY d.Seq ASC
  ";

  $stL = sqlsrv_query($conn, $sqlL, [$h['DocKey']]);
  if (!$stL) {
    wp_send_json_error([
      'error'  => 'SQL error (lines)',
      'detail' => sqlsrv_errors(),
    ], 500);
  }

  $lines = [];
  while ($r = sqlsrv_fetch_array($stL, SQLSRV_FETCH_ASSOC)) {
    // Use LineDesc if available, otherwise fallback to ItemCode
    $itemName = trim((string)$r['LineDesc']);
    if ($itemName === '') $itemName = (string)$r['ItemCode'];

    $lines[] = [
      'seq'       => (int)$r['Seq'],
      'itemCode'  => (string)$r['ItemCode'],
      'itemName'  => $itemName,
      'uom'       => (string)$r['UOM'],
      'qty'       => (float)$r['Qty'],
      'unitPrice' => (float)$r['UnitPrice'],
      'subTotal'  => (float)$r['SubTotal'],
    ];
  }
  sqlsrv_free_stmt($stL);

  // Format date to YYYY-MM-DD
  $docDate = '';
  if (!empty($h['DocDate'])) {
    if ($h['DocDate'] instanceof DateTime) {
      $docDate = $h['DocDate']->format('Y-m-d');
    } else {
      $ts = strtotime((string)$h['DocDate']);
      if ($ts) $docDate = date('Y-m-d', $ts);
    }
  }

  wp_send_json_success([
    'header' => [
      'docKey'      => (string)$h['DocKey'],
      'docNo'       => (string)$h['DocNo'],
      'docDate'     => $docDate,
      'debtorCode'  => (string)$h['DebtorCode'],
      'debtorName'  => (string)$h['DebtorName'],
    ],
    'lines' => $lines,
  ]);
}global��
����,€��"Y��AC Cash Sale Fetch (Price History)if (!defined('ABSPATH')) exit;

add_action('wp_ajax_ac_item_price_history', 'ac_item_price_history_ajax');
add_action('wp_ajax_nopriv_ac_item_price_history', 'ac_item_price_history_ajax');

function ac_item_price_history_ajax() {
    $nonce = isset($_GET['nonce']) ? sanitize_text_field(wp_unslash($_GET['nonce'])) : '';
    if (!wp_verify_nonce($nonce, 'ac_item_price_history')) {
        wp_send_json_error(['error' => 'Invalid nonce.'], 403);
    }

    if (!function_exists('get_mssql')) {
        wp_send_json_error(['error' => 'Missing get_mssql()'], 500);
    }

    $itemCode = isset($_GET['itemCode']) ? trim((string) wp_unslash($_GET['itemCode'])) : '';
    $itemCode = preg_replace('/[\x00-\x1F\x7F]/u', '', $itemCode);
    $itemCode = mb_substr($itemCode, 0, 100);

    $customerCode = isset($_GET['customerCode']) ? trim((string) wp_unslash($_GET['customerCode'])) : '';
    $customerCode = preg_replace('/[\x00-\x1F\x7F]/u', '', $customerCode);
    $customerCode = mb_substr($customerCode, 0, 100);

    $customerName = isset($_GET['customerName']) ? trim((string) wp_unslash($_GET['customerName'])) : '';
    $customerName = preg_replace('/[\x00-\x1F\x7F]/u', '', $customerName);
    $customerName = mb_substr($customerName, 0, 255);

    $extraName = isset($_GET['extraName']) ? trim((string) wp_unslash($_GET['extraName'])) : '';
    $extraName = preg_replace('/[\x00-\x1F\x7F]/u', '', $extraName);
    $extraName = mb_substr($extraName, 0, 255);

    if ($itemCode === '') {
        wp_send_json_error(['error' => 'Missing itemCode.'], 400);
    }

    $conn = get_mssql();
    if (!$conn) {
        wp_send_json_error(['error' => 'Database connection failed.'], 500);
    }

    $where = [];
    $params = [];

    $where[] = "d.ItemCode = ?";
    $params[] = $itemCode;

    $where[] = "ISNULL(h.Cancelled, 'F') = 'F'";

    // Filter by selected debtor code if present
    if ($customerCode !== '') {
        $where[] = "LTRIM(RTRIM(ISNULL(h.DebtorCode, ''))) = ?";
        $params[] = $customerCode;
    }

    // Filter by final current customer name if present
    // This covers base + extra name, e.g. CASH SALES ABANG SAYAN
    if ($customerName !== '') {
        $where[] = "LTRIM(RTRIM(ISNULL(h.DebtorName, ''))) = ?";
        $params[] = $customerName;
    }

    $sql = "
        SELECT TOP 20
            d.ItemCode AS ItemCode,
            d.UOM AS UOM,
            'CS' AS DocType,
            CONVERT(varchar(10), h.DocDate, 103) AS DocDate,
            h.DocNo AS DocNo,
            h.DebtorName AS CompanyName,
            d.Description AS ItemDescription,
            CAST(d.Qty AS decimal(18,2)) AS Qty,
            CAST(d.UnitPrice AS decimal(18,2)) AS UnitPrice,
            CAST(d.SubTotal AS decimal(18,2)) AS SubTotal
        FROM dbo.CSDTL d
        INNER JOIN dbo.CS h
            ON d.DocKey = h.DocKey
        WHERE " . implode("\n          AND ", $where) . "
        ORDER BY h.DocDate DESC, h.DocNo DESC
    ";

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

    if ($stmt === false) {
        $errs = sqlsrv_errors(SQLSRV_ERR_ERRORS);
        $msg = 'SQL query failed.';
        if (!empty($errs[0]['message'])) {
            $msg .= ' ' . $errs[0]['message'];
        }
        wp_send_json_error(['error' => $msg], 500);
    }

    $items = [];
    while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
        $items[] = [
            'itemCode'    => isset($row['ItemCode']) ? (string)$row['ItemCode'] : '',
            'uom'         => isset($row['UOM']) ? (string)$row['UOM'] : '',
            't'           => isset($row['DocType']) ? (string)$row['DocType'] : '',
            'date'        => isset($row['DocDate']) ? (string)$row['DocDate'] : '',
            'docNo'       => isset($row['DocNo']) ? (string)$row['DocNo'] : '',
            'companyName' => isset($row['CompanyName']) ? (string)$row['CompanyName'] : '',
            'description' => isset($row['ItemDescription']) ? (string)$row['ItemDescription'] : '',
            'qty'         => isset($row['Qty']) ? (float)$row['Qty'] : 0,
            'unitPrice'   => isset($row['UnitPrice']) ? (float)$row['UnitPrice'] : 0,
            'subTotal'    => isset($row['SubTotal']) ? (float)$row['SubTotal'] : 0,
        ];
    }

    sqlsrv_free_stmt($stmt);

    wp_send_json_success([
        'items' => $items
    ]);
}global��
����,����+ ��AC Cash Sale Fetch (Price Purchase History)if (!defined('ABSPATH')) exit;

add_action('wp_ajax_ac_item_purchase_price_history', 'ac_item_purchase_price_history_ajax');
add_action('wp_ajax_nopriv_ac_item_purchase_price_history', 'ac_item_purchase_price_history_ajax');

function ac_item_purchase_price_history_ajax() {
    $nonce = isset($_GET['nonce']) ? sanitize_text_field(wp_unslash($_GET['nonce'])) : '';
    if (!wp_verify_nonce($nonce, 'ac_item_purchase_price_history')) {
        wp_send_json_error(['error' => 'Invalid nonce.'], 403);
    }

    if (!function_exists('get_mssql')) {
        wp_send_json_error(['error' => 'Missing get_mssql()'], 500);
    }

    $itemCode = isset($_GET['itemCode']) ? trim((string) wp_unslash($_GET['itemCode'])) : '';
    $itemCode = preg_replace('/[\x00-\x1F\x7F]/u', '', $itemCode);
    $itemCode = mb_substr($itemCode, 0, 100);

    if ($itemCode === '') {
        wp_send_json_error(['error' => 'Missing itemCode.'], 400);
    }

    $conn = get_mssql();
    if (!$conn) {
        wp_send_json_error(['error' => 'Database connection failed.'], 500);
    }

    $sql = "
        SELECT TOP 20
            d.AccNo AS AccNo,
            h.CreditorName AS CompanyName,
            d.ItemCode AS ItemCode,
            d.UOM AS UOM,
            'PI' AS DocType,
            CONVERT(varchar(10), h.DocDate, 103) AS DocDate,
            h.DocNo AS DocNo,
            d.Description AS ItemDescription,
            d.Location AS Location,
            CAST(d.Qty AS decimal(18,2)) AS Qty,
            CAST(d.UnitPrice AS decimal(18,2)) AS UnitPrice,
            ISNULL(d.Discount, '') AS Discount,
            CAST(d.SubTotal AS decimal(18,2)) AS SubTotal
        FROM dbo.PIDTL d
        INNER JOIN dbo.PI h
            ON d.DocKey = h.DocKey
        WHERE d.ItemCode = ?
          AND ISNULL(h.Cancelled, 'F') = 'F'
        ORDER BY h.DocDate DESC, h.DocNo DESC
    ";

    $stmt = sqlsrv_query($conn, $sql, [$itemCode]);

    if ($stmt === false) {
        $errs = sqlsrv_errors(SQLSRV_ERR_ERRORS);
        $msg = 'SQL query failed.';
        if (!empty($errs[0]['message'])) {
            $msg .= ' ' . $errs[0]['message'];
        }
        wp_send_json_error(['error' => $msg], 500);
    }

    $items = [];
    while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
        $items[] = [
            'accNo'       => isset($row['AccNo']) ? (string)$row['AccNo'] : '',
            'companyName' => isset($row['CompanyName']) ? (string)$row['CompanyName'] : '',
            'itemCode'    => isset($row['ItemCode']) ? (string)$row['ItemCode'] : '',
            'uom'         => isset($row['UOM']) ? (string)$row['UOM'] : '',
            't'           => isset($row['DocType']) ? (string)$row['DocType'] : '',
            'date'        => isset($row['DocDate']) ? (string)$row['DocDate'] : '',
            'docNo'       => isset($row['DocNo']) ? (string)$row['DocNo'] : '',
            'description' => isset($row['ItemDescription']) ? (string)$row['ItemDescription'] : '',
            'location'    => isset($row['Location']) ? (string)$row['Location'] : '',
            'qty'         => isset($row['Qty']) ? (float)$row['Qty'] : 0,
            'unitPrice'   => isset($row['UnitPrice']) ? (float)$row['UnitPrice'] : 0,
            'discount'    => isset($row['Discount']) ? (string)$row['Discount'] : '',
            'subTotal'    => isset($row['SubTotal']) ? (float)$row['SubTotal'] : 0,
        ];
    }

    sqlsrv_free_stmt($stmt);

    wp_send_json_success([
        'items' => $items
    ]);
}global��
����,���(^��Master AJAX�
&d�global��
������0���Master AJAX get_mssql()add_action('wp_ajax_wst_cashsale_debtor_search_testdb', 'wst_cashsale_debtor_search_testdb');
add_action('wp_ajax_nopriv_wst_cashsale_debtor_search_testdb', 'wst_cashsale_debtor_search_testdb');

function wst_cashsale_debtor_search_testdb() {
    $nonce = $_GET['nonce'] ?? $_POST['nonce'] ?? '';

    if (!wp_verify_nonce($nonce, 'wst_cashsale_debtor_search_testdb')) {
        wp_send_json_error([
            'error' => 'Bad nonce',
        ], 403);
    }

    $term = $_GET['q'] ?? $_POST['q'] ?? '';
    $term = trim(sanitize_text_field(wp_unslash($term)));

    if ($term === '') {
        wp_send_json_success([
            'items' => [],
        ]);
    }

    // ✅ Fixed function existence check – matches the actual connection function
    if (!function_exists('get_mssql_cash_sale_test_db')) {
        wp_send_json_error([
            'error' => 'get_mssql_cash_sale_test_db() not found',
        ], 500);
    }

    $conn = get_mssql_cash_sale_test_db();

    if (!$conn) {
        wp_send_json_error([
            'error' => 'MSSQL connection failed',
        ], 500);
    }

    // Get current database name
    $dbCheck = sqlsrv_query($conn, "SELECT DB_NAME() AS CurrentDB");
    $dbRow = $dbCheck ? sqlsrv_fetch_array($dbCheck, SQLSRV_FETCH_ASSOC) : null;
    $currentDb = $dbRow['CurrentDB'] ?? 'UNKNOWN';

    $termUpper  = strtoupper($term);
    $like       = '%' . $termUpper . '%';
    $startsWith = $termUpper . '%';

    $sql = "
        SELECT TOP (20)
            DebtorCode,
            DebtorName
        FROM (
            SELECT DISTINCT
                LTRIM(RTRIM(DebtorCode)) AS DebtorCode,
                LTRIM(RTRIM(DebtorName)) AS DebtorName
            FROM dbo.CS
            WHERE ISNULL(Cancelled, 'F') <> 'T'
              AND DebtorName IS NOT NULL
              AND LTRIM(RTRIM(DebtorName)) <> ''
              AND (
                    DebtorName COLLATE Latin1_General_CI_AI LIKE ?
                    OR DebtorCode COLLATE Latin1_General_CI_AI LIKE ?
              )
        ) x
        ORDER BY
            CASE
                WHEN DebtorName COLLATE Latin1_General_CI_AI LIKE ? THEN 0
                WHEN DebtorCode COLLATE Latin1_General_CI_AI LIKE ? THEN 1
                ELSE 2
            END,
            DebtorName ASC
    ";

    $params = [
        $like,
        $like,
        $startsWith,
        $startsWith,
    ];

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

    if ($stmt === false) {
        wp_send_json_error([
            'error' => 'SQL error',
            'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
        ], 500);
    }

    $items = [];

    while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
        $items[] = [
            'code' => isset($r['DebtorCode']) ? (string)$r['DebtorCode'] : '',
            'name' => isset($r['DebtorName']) ? (string)$r['DebtorName'] : '',
        ];
    }

    sqlsrv_free_stmt($stmt);

    wp_send_json_success([
        'db' => $currentDb,
        'items' => $items,
    ]);
}global��
����Lۀpc
P�⫙j"
��������
P�d
�?�/**
 * AutoCount Cash Sale AJAX Handlers
 * Combined AJAX file:
 * - Item Suggest
 * - Debtor Search
 * - Sales Price History
 * - Purchase Price History
 * - Cash Sale Get By DocNo
 * - Item Images
 */

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

/* ============================================================
 * Shared Helpers
 * ============================================================ */

if (!function_exists('ac_cs_ajax_input')) {
    function ac_cs_ajax_input($key, $default = '') {
        if (isset($_POST[$key])) {
            return wp_unslash($_POST[$key]);
        }

        if (isset($_GET[$key])) {
            return wp_unslash($_GET[$key]);
        }

        return $default;
    }
}

if (!function_exists('ac_cs_ajax_clean_text')) {
    function ac_cs_ajax_clean_text($value, $max = 255) {
        $value = trim((string)$value);
        $value = preg_replace('/[\x00-\x1F\x7F]/u', '', $value);

        if (function_exists('mb_substr')) {
            $value = mb_substr($value, 0, $max);
        } else {
            $value = substr($value, 0, $max);
        }

        return $value;
    }
}

if (!function_exists('ac_cs_ajax_mssql_conn')) {
    function ac_cs_ajax_mssql_conn($prefer_2019 = false) {
        // Force Yong Tai Lie database: AED_YTL_21122025
		if (function_exists('get_mssql_cash_sale_test_db')) {
			return get_mssql_cash_sale_test_db();
		}

        return null;
    }
}

if (!function_exists('ac_cs_ajax_escape_like_sqlsrv')) {
    function ac_cs_ajax_escape_like_sqlsrv($s) {
        return str_replace(
            ['[', '%', '_'],
            ['[[]', '[%]', '[_]'],
            (string)$s
        );
    }
}

/* ============================================================
 * 1) AutoCount Item Suggest
 * action: ac_itemcode_suggest
 * nonce : ac_itemcode_suggest
 * Supports POST and GET
 * ============================================================ */

add_action('wp_ajax_ac_itemcode_suggest', 'ac_cs_ajax_itemcode_suggest');
add_action('wp_ajax_nopriv_ac_itemcode_suggest', 'ac_cs_ajax_itemcode_suggest');

if (!function_exists('ac_cs_ajax_itemcode_suggest')) {
    function ac_cs_ajax_itemcode_suggest() {
        $nonce = (string) ac_cs_ajax_input('nonce', '');

        if (!wp_verify_nonce($nonce, 'ac_itemcode_suggest')) {
            wp_send_json([
                'ok' => false,
                'message' => 'Bad nonce',
            ], 403);
        }

        $term = ac_cs_ajax_clean_text(ac_cs_ajax_input('term', ac_cs_ajax_input('q', '')), 100);
        $term = preg_replace('/\s+/', ' ', $term);

        // Minimum character check (1 char allowed)
        $minChars = 1;
        if (function_exists('mb_strlen')) {
            if (mb_strlen($term) < $minChars) {
                wp_send_json(['ok' => true, 'items' => []], 200);
            }
        } else {
            if (strlen($term) < $minChars) {
                wp_send_json(['ok' => true, 'items' => []], 200);
            }
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json([
                'ok' => false,
                'message' => 'MSSQL connection failed or get_mssql() not found',
            ], 500);
        }

        // No removal of '#', keep original term
        $termCode = $term;

        $safeCode = ac_cs_ajax_escape_like_sqlsrv($termCode);
        $safeName = ac_cs_ajax_escape_like_sqlsrv($term);

        // ✅ CHANGE: ItemCode now uses contains search (prefix + suffix)
        $codeLike    = '%' . $safeCode . '%';   // was: $safeCode . '%'
        $nameLike    = '%' . $safeName . '%';
        $barcodeLike = '%' . $safeName . '%';

        $sql = "
            SELECT TOP (12)
                i.ItemCode,
                i.Description,
                i.Desc2,
                ISNULL(pick.UOM, i.SalesUOM) AS UOM,
                CAST(ISNULL(pick.Price, 0) AS decimal(18,2)) AS UnitPrice
            FROM dbo.Item i
            OUTER APPLY (
                SELECT TOP (1)
                    iu.UOM,
                    iu.Price
                FROM dbo.ItemUOM iu
                WHERE iu.ItemCode = i.ItemCode
                ORDER BY
                    CASE
                        WHEN iu.UOM = i.SalesUOM THEN 0
                        WHEN iu.UOM = i.BaseUOM  THEN 1
                        ELSE 2
                    END,
                    iu.AutoKey DESC
            ) pick
            WHERE
                i.ItemCode LIKE ?
                OR i.Description LIKE ?
                OR i.Desc2 LIKE ?
                OR EXISTS (
                    SELECT 1
                    FROM dbo.ItemUOM iu2
                    WHERE iu2.ItemCode = i.ItemCode
                      AND iu2.BarCode LIKE ?
                )
            ORDER BY
                CASE
                    WHEN EXISTS (
                        SELECT 1
                        FROM dbo.ItemUOM iu3
                        WHERE iu3.ItemCode = i.ItemCode
                          AND iu3.BarCode LIKE ?
                    ) THEN 0
                    WHEN i.ItemCode LIKE ? THEN 1
                    WHEN i.Description LIKE ? THEN 2
                    WHEN i.Desc2 LIKE ? THEN 3
                    ELSE 4
                END,
                i.ItemCode ASC
        ";

        $params = [
            $codeLike,
            $nameLike,
            $nameLike,
            $barcodeLike,
            $barcodeLike,
            $codeLike,
            $nameLike,
            $nameLike,
        ];

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

        if ($stmt === false) {
            wp_send_json([
                'ok' => false,
                'message' => 'SQL error',
                'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $items = [];

        while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $items[] = [
                'code'      => isset($r['ItemCode']) ? (string)$r['ItemCode'] : '',
                'desc'      => isset($r['Description']) ? (string)$r['Description'] : '',
                'desc2'     => isset($r['Desc2']) ? (string)$r['Desc2'] : '',
                'uom'       => isset($r['UOM']) ? (string)$r['UOM'] : '',
                'unitPrice' => isset($r['UnitPrice']) ? (string)$r['UnitPrice'] : '0.00',
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json([
            'ok' => true,
            'items' => $items,
        ], 200);
    }
}

/* ============================================================
 * 2) Cash Sale Debtor Search
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_cs_debtor_search', 'ac_cs_ajax_debtor_search');
add_action('wp_ajax_nopriv_ac_cs_debtor_search', 'ac_cs_ajax_debtor_search');

if (!function_exists('ac_cs_ajax_debtor_search')) {
    function ac_cs_ajax_debtor_search() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_cs_debtor_search')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $q = ac_cs_ajax_clean_text(ac_cs_ajax_input('q', ac_cs_ajax_input('term', '')), 80);

        if ($q === '') {
            wp_send_json_success(['items' => []]);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'MSSQL connection failed or get_mssql() not found.'], 500);
        }

        $like = '%' . $q . '%';

        $sql = "
            ;WITH cs AS (
                SELECT
                    CAST(DebtorCode AS nvarchar(50))  AS code,
                    CAST(DebtorName AS nvarchar(200)) AS name,
                    MAX(DocDate) AS last_doc_date,
                    CAST(MAX(CAST(SalesAgent AS nvarchar(50))) AS nvarchar(50)) AS salesAgent,
                    1 AS from_cs
                FROM dbo.CS WITH (NOLOCK)
                WHERE ISNULL(Cancelled,'F') <> 'T'
                  AND (
                    ISNULL(DebtorName,'') LIKE ?
                    OR ISNULL(DebtorCode,'') LIKE ?
                  )
                  AND ISNULL(DebtorCode,'') <> ''
                  AND ISNULL(DebtorName,'') <> ''
                GROUP BY DebtorCode, DebtorName
            ),
            d AS (
                SELECT
                    CAST(AccNo AS nvarchar(50))        AS code,
                    CAST(CompanyName AS nvarchar(200)) AS name,
                    CAST(NULL AS datetime) AS last_doc_date,
                    CAST(MAX(CAST(SalesAgent AS nvarchar(50))) AS nvarchar(50)) AS salesAgent,
                    0 AS from_cs
                FROM dbo.Debtor WITH (NOLOCK)
                WHERE ISNULL(DebtorType,'') = 'CS'
                  AND ISNULL(IsActive,'F') = 'T'
                  AND (
                    ISNULL(CompanyName,'') LIKE ?
                    OR ISNULL(AccNo,'') LIKE ?
                  )
                  AND ISNULL(AccNo,'') <> ''
                  AND ISNULL(CompanyName,'') <> ''
                GROUP BY AccNo, CompanyName
            ),
            u AS (
                SELECT * FROM cs
                UNION ALL
                SELECT * FROM d
            ),
            final AS (
                SELECT
                    code,
                    name,
                    MAX(last_doc_date) AS last_doc_date,
                    COALESCE(
                        MAX(CASE WHEN from_cs = 1 THEN salesAgent END),
                        MAX(salesAgent),
                        ''
                    ) AS salesAgent,
                    MAX(from_cs) AS has_cs
                FROM u
                GROUP BY code, name
            )
            SELECT TOP 25
                code,
                name,
                salesAgent,
                last_doc_date
            FROM final
            ORDER BY
                has_cs DESC,
                last_doc_date DESC,
                name ASC,
                code ASC
        ";

        $stmt = sqlsrv_query($conn, $sql, [$like, $like, $like, $like]);

        if ($stmt === false) {
            wp_send_json_error([
                'error'  => 'Search query failed.',
                'sqlsrv' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $items = [];

        while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $code       = trim((string)($r['code'] ?? ''));
            $name       = trim((string)($r['name'] ?? ''));
            $salesAgent = trim((string)($r['salesAgent'] ?? ''));

            if ($code === '' && $name === '') continue;

            $display = $name !== '' ? $name : $code;

            $items[] = [
                'code'       => $code,
                'name'       => $display,
                'debtorType' => 'CS',
                'salesAgent' => $salesAgent,
                'text'       => $code !== '' ? ($display . ' — ' . $code) : $display,
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json_success(['items' => $items]);
    }
}

/* ============================================================
 * 3) Sales Price History
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_item_price_history', 'ac_cs_ajax_item_price_history');
add_action('wp_ajax_nopriv_ac_item_price_history', 'ac_cs_ajax_item_price_history');

if (!function_exists('ac_cs_ajax_item_price_history')) {
    function ac_cs_ajax_item_price_history() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_item_price_history')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $itemCode     = ac_cs_ajax_clean_text(ac_cs_ajax_input('itemCode', ''), 100);
        $customerCode = ac_cs_ajax_clean_text(ac_cs_ajax_input('customerCode', ''), 100);
        $customerName = ac_cs_ajax_clean_text(ac_cs_ajax_input('customerName', ''), 255);

        if ($itemCode === '') {
            wp_send_json_error(['error' => 'Missing itemCode.'], 400);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'Database connection failed.'], 500);
        }

        $where = [];
        $params = [];

        $where[] = "d.ItemCode = ?";
        $params[] = $itemCode;

        $where[] = "ISNULL(h.Cancelled, 'F') = 'F'";

        if ($customerCode !== '') {
            $where[] = "LTRIM(RTRIM(ISNULL(h.DebtorCode, ''))) = ?";
            $params[] = $customerCode;
        }

        if ($customerName !== '') {
            $where[] = "LTRIM(RTRIM(ISNULL(h.DebtorName, ''))) = ?";
            $params[] = $customerName;
        }

        $sql = "
            SELECT TOP 20
                d.ItemCode AS ItemCode,
                d.UOM AS UOM,
                'CS' AS DocType,
                CONVERT(varchar(10), h.DocDate, 103) AS DocDate,
                h.DocNo AS DocNo,
                h.DebtorName AS CompanyName,
                d.Description AS ItemDescription,
                CAST(d.Qty AS decimal(18,2)) AS Qty,
                CAST(d.UnitPrice AS decimal(18,2)) AS UnitPrice,
                CAST(d.SubTotal AS decimal(18,2)) AS SubTotal
            FROM dbo.CSDTL d
            INNER JOIN dbo.CS h
                ON d.DocKey = h.DocKey
            WHERE " . implode("\n              AND ", $where) . "
            ORDER BY h.DocDate DESC, h.DocNo DESC
        ";

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

        if ($stmt === false) {
            $errs = sqlsrv_errors(SQLSRV_ERR_ERRORS);
            $msg = 'SQL query failed.';
            if (!empty($errs[0]['message'])) {
                $msg .= ' ' . $errs[0]['message'];
            }

            wp_send_json_error(['error' => $msg], 500);
        }

        $items = [];

        while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $items[] = [
                'itemCode'    => isset($row['ItemCode']) ? (string)$row['ItemCode'] : '',
                'uom'         => isset($row['UOM']) ? (string)$row['UOM'] : '',
                't'           => isset($row['DocType']) ? (string)$row['DocType'] : '',
                'date'        => isset($row['DocDate']) ? (string)$row['DocDate'] : '',
                'docNo'       => isset($row['DocNo']) ? (string)$row['DocNo'] : '',
                'companyName' => isset($row['CompanyName']) ? (string)$row['CompanyName'] : '',
                'description' => isset($row['ItemDescription']) ? (string)$row['ItemDescription'] : '',
                'qty'         => isset($row['Qty']) ? (float)$row['Qty'] : 0,
                'unitPrice'   => isset($row['UnitPrice']) ? (float)$row['UnitPrice'] : 0,
                'subTotal'    => isset($row['SubTotal']) ? (float)$row['SubTotal'] : 0,
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json_success(['items' => $items]);
    }
}

/* ============================================================
 * 4) Purchase Price History
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_item_purchase_price_history', 'ac_cs_ajax_item_purchase_price_history');
add_action('wp_ajax_nopriv_ac_item_purchase_price_history', 'ac_cs_ajax_item_purchase_price_history');

if (!function_exists('ac_cs_ajax_item_purchase_price_history')) {
    function ac_cs_ajax_item_purchase_price_history() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_item_purchase_price_history')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $itemCode = ac_cs_ajax_clean_text(ac_cs_ajax_input('itemCode', ''), 100);

        if ($itemCode === '') {
            wp_send_json_error(['error' => 'Missing itemCode.'], 400);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'Database connection failed.'], 500);
        }

        $sql = "
            SELECT TOP 20
                d.AccNo AS AccNo,
                h.CreditorName AS CompanyName,
                d.ItemCode AS ItemCode,
                d.UOM AS UOM,
                'PI' AS DocType,
                CONVERT(varchar(10), h.DocDate, 103) AS DocDate,
       
P�di�*���������̘
�?�/**
 * AutoCount Cash Sale AJAX Handlers
 * Combined AJAX file:
 * - Item Suggest
 * - Debtor Search
 * - Sales Price History
 * - Purchase Price History
 * - Cash Sale Get By DocNo
 * - Item Images
 */

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

/* ============================================================
 * Shared Helpers
 * ============================================================ */

if (!function_exists('ac_cs_ajax_input')) {
    function ac_cs_ajax_input($key, $default = '') {
        if (isset($_POST[$key])) {
            return wp_unslash($_POST[$key]);
        }

        if (isset($_GET[$key])) {
            return wp_unslash($_GET[$key]);
        }

        return $default;
    }
}

if (!function_exists('ac_cs_ajax_clean_text')) {
    function ac_cs_ajax_clean_text($value, $max = 255) {
        $value = trim((string)$value);
        $value = preg_replace('/[\x00-\x1F\x7F]/u', '', $value);

        if (function_exists('mb_substr')) {
            $value = mb_substr($value, 0, $max);
        } else {
            $value = substr($value, 0, $max);
        }

        return $value;
    }
}

if (!function_exists('ac_cs_ajax_mssql_conn')) {
    function ac_cs_ajax_mssql_conn($prefer_2019 = false) {
        // Force Yong Tai Lie database: AED_YTL_21122025
        if (function_exists('get_mssql_2019')) {
            return get_mssql_2019();
        }

        return null;
    }
}

if (!function_exists('ac_cs_ajax_escape_like_sqlsrv')) {
    function ac_cs_ajax_escape_like_sqlsrv($s) {
        return str_replace(
            ['[', '%', '_'],
            ['[[]', '[%]', '[_]'],
            (string)$s
        );
    }
}

/* ============================================================
 * 1) AutoCount Item Suggest
 * action: ac_itemcode_suggest
 * nonce : ac_itemcode_suggest
 * Supports POST and GET
 * ============================================================ */

add_action('wp_ajax_ac_itemcode_suggest', 'ac_cs_ajax_itemcode_suggest');
add_action('wp_ajax_nopriv_ac_itemcode_suggest', 'ac_cs_ajax_itemcode_suggest');

if (!function_exists('ac_cs_ajax_itemcode_suggest')) {
    function ac_cs_ajax_itemcode_suggest() {
        $nonce = (string) ac_cs_ajax_input('nonce', '');

        if (!wp_verify_nonce($nonce, 'ac_itemcode_suggest')) {
            wp_send_json([
                'ok' => false,
                'message' => 'Bad nonce',
            ], 403);
        }

        $term = ac_cs_ajax_clean_text(ac_cs_ajax_input('term', ac_cs_ajax_input('q', '')), 100);
        $term = preg_replace('/\s+/', ' ', $term);

        // Minimum character check (1 char allowed)
        $minChars = 1;
        if (function_exists('mb_strlen')) {
            if (mb_strlen($term) < $minChars) {
                wp_send_json(['ok' => true, 'items' => []], 200);
            }
        } else {
            if (strlen($term) < $minChars) {
                wp_send_json(['ok' => true, 'items' => []], 200);
            }
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json([
                'ok' => false,
                'message' => 'MSSQL connection failed or get_mssql() not found',
            ], 500);
        }

        // No removal of '#', keep original term
        $termCode = $term;

        $safeCode = ac_cs_ajax_escape_like_sqlsrv($termCode);
        $safeName = ac_cs_ajax_escape_like_sqlsrv($term);

        // ✅ CHANGE: ItemCode now uses contains search (prefix + suffix)
        $codeLike    = '%' . $safeCode . '%';   // was: $safeCode . '%'
        $nameLike    = '%' . $safeName . '%';
        $barcodeLike = '%' . $safeName . '%';

        $sql = "
            SELECT TOP (12)
                i.ItemCode,
                i.Description,
                i.Desc2,
                ISNULL(pick.UOM, i.SalesUOM) AS UOM,
                CAST(ISNULL(pick.Price, 0) AS decimal(18,2)) AS UnitPrice
            FROM dbo.Item i
            OUTER APPLY (
                SELECT TOP (1)
                    iu.UOM,
                    iu.Price
                FROM dbo.ItemUOM iu
                WHERE iu.ItemCode = i.ItemCode
                ORDER BY
                    CASE
                        WHEN iu.UOM = i.SalesUOM THEN 0
                        WHEN iu.UOM = i.BaseUOM  THEN 1
                        ELSE 2
                    END,
                    iu.AutoKey DESC
            ) pick
            WHERE
                i.ItemCode LIKE ?
                OR i.Description LIKE ?
                OR i.Desc2 LIKE ?
                OR EXISTS (
                    SELECT 1
                    FROM dbo.ItemUOM iu2
                    WHERE iu2.ItemCode = i.ItemCode
                      AND iu2.BarCode LIKE ?
                )
            ORDER BY
                CASE
                    WHEN EXISTS (
                        SELECT 1
                        FROM dbo.ItemUOM iu3
                        WHERE iu3.ItemCode = i.ItemCode
                          AND iu3.BarCode LIKE ?
                    ) THEN 0
                    WHEN i.ItemCode LIKE ? THEN 1
                    WHEN i.Description LIKE ? THEN 2
                    WHEN i.Desc2 LIKE ? THEN 3
                    ELSE 4
                END,
                i.ItemCode ASC
        ";

        $params = [
            $codeLike,
            $nameLike,
            $nameLike,
            $barcodeLike,
            $barcodeLike,
            $codeLike,
            $nameLike,
            $nameLike,
        ];

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

        if ($stmt === false) {
            wp_send_json([
                'ok' => false,
                'message' => 'SQL error',
                'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $items = [];

        while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $items[] = [
                'code'      => isset($r['ItemCode']) ? (string)$r['ItemCode'] : '',
                'desc'      => isset($r['Description']) ? (string)$r['Description'] : '',
                'desc2'     => isset($r['Desc2']) ? (string)$r['Desc2'] : '',
                'uom'       => isset($r['UOM']) ? (string)$r['UOM'] : '',
                'unitPrice' => isset($r['UnitPrice']) ? (string)$r['UnitPrice'] : '0.00',
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json([
            'ok' => true,
            'items' => $items,
        ], 200);
    }
}

/* ============================================================
 * 2) Cash Sale Debtor Search
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_cs_debtor_search', 'ac_cs_ajax_debtor_search');
add_action('wp_ajax_nopriv_ac_cs_debtor_search', 'ac_cs_ajax_debtor_search');

if (!function_exists('ac_cs_ajax_debtor_search')) {
    function ac_cs_ajax_debtor_search() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_cs_debtor_search')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $q = ac_cs_ajax_clean_text(ac_cs_ajax_input('q', ac_cs_ajax_input('term', '')), 80);

        if ($q === '') {
            wp_send_json_success(['items' => []]);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'MSSQL connection failed or get_mssql() not found.'], 500);
        }

        $like = '%' . $q . '%';

        $sql = "
            ;WITH cs AS (
                SELECT
                    CAST(DebtorCode AS nvarchar(50))  AS code,
                    CAST(DebtorName AS nvarchar(200)) AS name,
                    MAX(DocDate) AS last_doc_date,
                    CAST(MAX(CAST(SalesAgent AS nvarchar(50))) AS nvarchar(50)) AS salesAgent,
                    1 AS from_cs
                FROM dbo.CS WITH (NOLOCK)
                WHERE ISNULL(Cancelled,'F') <> 'T'
                  AND (
                    ISNULL(DebtorName,'') LIKE ?
                    OR ISNULL(DebtorCode,'') LIKE ?
                  )
                  AND ISNULL(DebtorCode,'') <> ''
                  AND ISNULL(DebtorName,'') <> ''
                GROUP BY DebtorCode, DebtorName
            ),
            d AS (
                SELECT
                    CAST(AccNo AS nvarchar(50))        AS code,
                    CAST(CompanyName AS nvarchar(200)) AS name,
                    CAST(NULL AS datetime) AS last_doc_date,
                    CAST(MAX(CAST(SalesAgent AS nvarchar(50))) AS nvarchar(50)) AS salesAgent,
                    0 AS from_cs
                FROM dbo.Debtor WITH (NOLOCK)
                WHERE ISNULL(DebtorType,'') = 'CS'
                  AND ISNULL(IsActive,'F') = 'T'
                  AND (
                    ISNULL(CompanyName,'') LIKE ?
                    OR ISNULL(AccNo,'') LIKE ?
                  )
                  AND ISNULL(AccNo,'') <> ''
                  AND ISNULL(CompanyName,'') <> ''
                GROUP BY AccNo, CompanyName
            ),
            u AS (
                SELECT * FROM cs
                UNION ALL
                SELECT * FROM d
            ),
            final AS (
                SELECT
                    code,
                    name,
                    MAX(last_doc_date) AS last_doc_date,
                    COALESCE(
                        MAX(CASE WHEN from_cs = 1 THEN salesAgent END),
                        MAX(salesAgent),
                        ''
                    ) AS salesAgent,
                    MAX(from_cs) AS has_cs
                FROM u
                GROUP BY code, name
            )
            SELECT TOP 25
                code,
                name,
                salesAgent,
                last_doc_date
            FROM final
            ORDER BY
                has_cs DESC,
                last_doc_date DESC,
                name ASC,
                code ASC
        ";

        $stmt = sqlsrv_query($conn, $sql, [$like, $like, $like, $like]);

        if ($stmt === false) {
            wp_send_json_error([
                'error'  => 'Search query failed.',
                'sqlsrv' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $items = [];

        while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $code       = trim((string)($r['code'] ?? ''));
            $name       = trim((string)($r['name'] ?? ''));
            $salesAgent = trim((string)($r['salesAgent'] ?? ''));

            if ($code === '' && $name === '') continue;

            $display = $name !== '' ? $name : $code;

            $items[] = [
                'code'       => $code,
                'name'       => $display,
                'debtorType' => 'CS',
                'salesAgent' => $salesAgent,
                'text'       => $code !== '' ? ($display . ' — ' . $code) : $display,
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json_success(['items' => $items]);
    }
}

/* ============================================================
 * 3) Sales Price History
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_item_price_history', 'ac_cs_ajax_item_price_history');
add_action('wp_ajax_nopriv_ac_item_price_history', 'ac_cs_ajax_item_price_history');

if (!function_exists('ac_cs_ajax_item_price_history')) {
    function ac_cs_ajax_item_price_history() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_item_price_history')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $itemCode     = ac_cs_ajax_clean_text(ac_cs_ajax_input('itemCode', ''), 100);
        $customerCode = ac_cs_ajax_clean_text(ac_cs_ajax_input('customerCode', ''), 100);
        $customerName = ac_cs_ajax_clean_text(ac_cs_ajax_input('customerName', ''), 255);

        if ($itemCode === '') {
            wp_send_json_error(['error' => 'Missing itemCode.'], 400);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'Database connection failed.'], 500);
        }

        $where = [];
        $params = [];

        $where[] = "d.ItemCode = ?";
        $params[] = $itemCode;

        $where[] = "ISNULL(h.Cancelled, 'F') = 'F'";

        if ($customerCode !== '') {
            $where[] = "LTRIM(RTRIM(ISNULL(h.DebtorCode, ''))) = ?";
            $params[] = $customerCode;
        }

        if ($customerName !== '') {
            $where[] = "LTRIM(RTRIM(ISNULL(h.DebtorName, ''))) = ?";
            $params[] = $customerName;
        }

        $sql = "
            SELECT TOP 20
                d.ItemCode AS ItemCode,
                d.UOM AS UOM,
                'CS' AS DocType,
                CONVERT(varchar(10), h.DocDate, 103) AS DocDate,
                h.DocNo AS DocNo,
                h.DebtorName AS CompanyName,
                d.Description AS ItemDescription,
                CAST(d.Qty AS decimal(18,2)) AS Qty,
                CAST(d.UnitPrice AS decimal(18,2)) AS UnitPrice,
                CAST(d.SubTotal AS decimal(18,2)) AS SubTotal
            FROM dbo.CSDTL d
            INNER JOIN dbo.CS h
                ON d.DocKey = h.DocKey
            WHERE " . implode("\n              AND ", $where) . "
            ORDER BY h.DocDate DESC, h.DocNo DESC
        ";

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

        if ($stmt === false) {
            $errs = sqlsrv_errors(SQLSRV_ERR_ERRORS);
            $msg = 'SQL query failed.';
            if (!empty($errs[0]['message'])) {
                $msg .= ' ' . $errs[0]['message'];
            }

            wp_send_json_error(['error' => $msg], 500);
        }

        $items = [];

        while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $items[] = [
                'itemCode'    => isset($row['ItemCode']) ? (string)$row['ItemCode'] : '',
                'uom'         => isset($row['UOM']) ? (string)$row['UOM'] : '',
                't'           => isset($row['DocType']) ? (string)$row['DocType'] : '',
                'date'        => isset($row['DocDate']) ? (string)$row['DocDate'] : '',
                'docNo'       => isset($row['DocNo']) ? (string)$row['DocNo'] : '',
                'companyName' => isset($row['CompanyName']) ? (string)$row['CompanyName'] : '',
                'description' => isset($row['ItemDescription']) ? (string)$row['ItemDescription'] : '',
                'qty'         => isset($row['Qty']) ? (float)$row['Qty'] : 0,
                'unitPrice'   => isset($row['UnitPrice']) ? (float)$row['UnitPrice'] : 0,
                'subTotal'    => isset($row['SubTotal']) ? (float)$row['SubTotal'] : 0,
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json_success(['items' => $items]);
    }
}

/* ============================================================
 * 4) Purchase Price History
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_item_purchase_price_history', 'ac_cs_ajax_item_purchase_price_history');
add_action('wp_ajax_nopriv_ac_item_purchase_price_history', 'ac_cs_ajax_item_purchase_price_history');

if (!function_exists('ac_cs_ajax_item_purchase_price_history')) {
    function ac_cs_ajax_item_purchase_price_history() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_item_purchase_price_history')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $itemCode = ac_cs_ajax_clean_text(ac_cs_ajax_input('itemCode', ''), 100);

        if ($itemCode === '') {
            wp_send_json_error(['error' => 'Missing itemCode.'], 400);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'Database connection failed.'], 500);
        }

        $sql = "
            SELECT TOP 20
                d.AccNo AS AccNo,
                h.CreditorName AS CompanyName,
                d.ItemCode AS ItemCode,
                d.UOM AS UOM,
                'PI' AS DocType,
                CONVERT(varchar(10), h.DocDate, 103) AS DocDate,
            �̘'rp���������
P�d
�$����         h.DocNo AS DocNo,
                d.Description AS ItemDescription,
                d.Location AS Location,
                CAST(d.Qty AS decimal(18,2)) AS Qty,
                CAST(d.UnitPrice AS decimal(18,2)) AS UnitPrice,
                ISNULL(d.Discount, '') AS Discount,
                CAST(d.SubTotal AS decimal(18,2)) AS SubTotal
            FROM dbo.PIDTL d
            INNER JOIN dbo.PI h
                ON d.DocKey = h.DocKey
            WHERE d.ItemCode = ?
              AND ISNULL(h.Cancelled, 'F') = 'F'
            ORDER BY h.DocDate DESC, h.DocNo DESC
        ";

        $stmt = sqlsrv_query($conn, $sql, [$itemCode]);

        if ($stmt === false) {
            $errs = sqlsrv_errors(SQLSRV_ERR_ERRORS);
            $msg = 'SQL query failed.';
            if (!empty($errs[0]['message'])) {
                $msg .= ' ' . $errs[0]['message'];
            }

            wp_send_json_error(['error' => $msg], 500);
        }

        $items = [];

        while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $items[] = [
                'accNo'       => isset($row['AccNo']) ? (string)$row['AccNo'] : '',
                'companyName' => isset($row['CompanyName']) ? (string)$row['CompanyName'] : '',
                'itemCode'    => isset($row['ItemCode']) ? (string)$row['ItemCode'] : '',
                'uom'         => isset($row['UOM']) ? (string)$row['UOM'] : '',
                't'           => isset($row['DocType']) ? (string)$row['DocType'] : '',
                'date'        => isset($row['DocDate']) ? (string)$row['DocDate'] : '',
                'docNo'       => isset($row['DocNo']) ? (string)$row['DocNo'] : '',
                'description' => isset($row['ItemDescription']) ? (string)$row['ItemDescription'] : '',
                'location'    => isset($row['Location']) ? (string)$row['Location'] : '',
                'qty'         => isset($row['Qty']) ? (float)$row['Qty'] : 0,
                'unitPrice'   => isset($row['UnitPrice']) ? (float)$row['UnitPrice'] : 0,
                'discount'    => isset($row['Discount']) ? (string)$row['Discount'] : '',
                'subTotal'    => isset($row['SubTotal']) ? (float)$row['SubTotal'] : 0,
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json_success(['items' => $items]);
    }
}

/* ============================================================
 * 5) Cash Sale Get By DocNo
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_cs_get_by_docno', 'ac_cs_ajax_get_by_docno');
add_action('wp_ajax_nopriv_ac_cs_get_by_docno', 'ac_cs_ajax_get_by_docno');

if (!function_exists('ac_cs_ajax_get_by_docno')) {
    function ac_cs_ajax_get_by_docno() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_cs_get_by_docno')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $docNo = ac_cs_ajax_clean_text(ac_cs_ajax_input('docno', ac_cs_ajax_input('docNo', '')), 100);

        if ($docNo === '') {
            wp_send_json_error(['error' => 'docno is required'], 400);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'MSSQL connection failed'], 500);
        }

        $sqlH = "
            SELECT TOP 1
                CS.DocKey,
                CS.DocNo,
                CS.DocDate,
                CS.DebtorCode,
                ISNULL(D.CompanyName, '') AS DebtorName
            FROM dbo.CS CS
            LEFT JOIN dbo.Debtor D
                ON D.AccNo = CS.DebtorCode
            WHERE CS.DocNo = ?
              AND ISNULL(CS.Cancelled, '') IN ('', '0', 'F', 'N')
        ";

        $stH = sqlsrv_query($conn, $sqlH, [$docNo]);

        if (!$stH) {
            wp_send_json_error([
                'error'  => 'SQL error (header)',
                'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $h = sqlsrv_fetch_array($stH, SQLSRV_FETCH_ASSOC);
        sqlsrv_free_stmt($stH);

        if (!$h) {
            wp_send_json_error(['error' => 'Cash Sale not found or cancelled.'], 404);
        }

        $sqlL = "
            SELECT
                d.Seq,
                d.ItemCode,
                ISNULL(d.Description, '') AS LineDesc,
                ISNULL(d.UOM, '') AS UOM,
                CAST(ISNULL(d.Qty, 0) AS float) AS Qty,
                CAST(ISNULL(d.UnitPrice, 0) AS float) AS UnitPrice,
                CAST(ISNULL(d.SubTotal, 0) AS float) AS SubTotal
            FROM dbo.CSDTL d
            WHERE d.DocKey = ?
            ORDER BY d.Seq ASC
        ";

        $stL = sqlsrv_query($conn, $sqlL, [$h['DocKey']]);

        if (!$stL) {
            wp_send_json_error([
                'error'  => 'SQL error (lines)',
                'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $lines = [];

        while ($r = sqlsrv_fetch_array($stL, SQLSRV_FETCH_ASSOC)) {
            $itemName = trim((string)($r['LineDesc'] ?? ''));
            if ($itemName === '') {
                $itemName = (string)($r['ItemCode'] ?? '');
            }

            $lines[] = [
                'seq'       => (int)($r['Seq'] ?? 0),
                'itemCode'  => (string)($r['ItemCode'] ?? ''),
                'itemName'  => $itemName,
                'uom'       => (string)($r['UOM'] ?? ''),
                'qty'       => (float)($r['Qty'] ?? 0),
                'unitPrice' => (float)($r['UnitPrice'] ?? 0),
                'subTotal'  => (float)($r['SubTotal'] ?? 0),
            ];
        }

        sqlsrv_free_stmt($stL);

        $docDate = '';

        if (!empty($h['DocDate'])) {
            if ($h['DocDate'] instanceof DateTime) {
                $docDate = $h['DocDate']->format('Y-m-d');
            } else {
                $ts = strtotime((string)$h['DocDate']);
                if ($ts) {
                    $docDate = date('Y-m-d', $ts);
                }
            }
        }

        wp_send_json_success([
            'header' => [
                'docKey'     => (string)($h['DocKey'] ?? ''),
                'docNo'      => (string)($h['DocNo'] ?? ''),
                'docDate'    => $docDate,
                'debtorCode' => (string)($h['DebtorCode'] ?? ''),
                'debtorName' => (string)($h['DebtorName'] ?? ''),
            ],
            'lines' => $lines,
        ]);
    }
}

/* ============================================================
 * 6) Get Item Images
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_get_images', 'ac_cs_ajax_get_images');
add_action('wp_ajax_nopriv_ac_get_images', 'ac_cs_ajax_get_images');

if (!function_exists('ac_cs_ajax_get_images')) {
    function ac_cs_ajax_get_images() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_get_images')) {
            wp_send_json_error(['message' => 'Bad nonce'], 403);
        }

        $item = ac_cs_ajax_clean_text(ac_cs_ajax_input('item', ac_cs_ajax_input('itemCode', '')), 100);

        if ($item === '') {
            wp_send_json_success([]);
        }

        $conn = ac_cs_ajax_mssql_conn(true);
        if (!$conn) {
            wp_send_json_error(['message' => 'DB connection error or missing MSSQL function'], 500);
        }

        $imgs = ac_cs_ajax_fetch_images_base64($conn, $item, 16);

        wp_send_json_success($imgs);
    }
}

if (!function_exists('ac_cs_ajax_fetch_images_base64')) {
    function ac_cs_ajax_fetch_images_base64($conn, $itemCode, $limit = 16) {
        $itemCode = trim((string)$itemCode);
        $limit = max(1, min(50, (int)$limit));

        if ($itemCode === '') return [];

        $sql = "
            SELECT TOP ($limit)
                ImageData
            FROM dbo.Web_StockImages
            WHERE LTRIM(RTRIM(ItemCode)) = ?
              AND ImageData IS NOT NULL
            ORDER BY (SELECT NULL)
        ";

        $stmt = sqlsrv_query($conn, $sql, [$itemCode]);

        if (!$stmt) {
            error_log('ac_cs_ajax_fetch_images_base64 failed: ' . print_r(sqlsrv_errors(SQLSRV_ERR_ERRORS), true));
            return [];
        }

        $out = [];

        while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $bin = $r['ImageData'] ?? null;

            if (is_resource($bin)) {
                $bin = stream_get_contents($bin);
            }

            if (!$bin) continue;

            $mime = 'image/jpeg';
            $head = substr($bin, 0, 12);

            if ($head !== false) {
                if (strncmp($head, "\x89PNG\r\n\x1a\n", 8) === 0) {
                    $mime = 'image/png';
                } elseif (strncmp($head, "GIF87a", 6) === 0 || strncmp($head, "GIF89a", 6) === 0) {
                    $mime = 'image/gif';
                } elseif (strncmp($head, "RIFF", 4) === 0 && substr($head, 8, 4) === "WEBP") {
                    $mime = 'image/webp';
                } elseif (strncmp($head, "\xFF\xD8\xFF", 3) === 0) {
                    $mime = 'image/jpeg';
                }
            }

            $out[] = "data:$mime;base64," . base64_encode($bin);

            if (count($out) >= $limit) break;
        }

        sqlsrv_free_stmt($stmt);

        return $out;
    }
}
P�d��Z[
���������+
�%����         d.ItemCode AS ItemCode,
                d.UOM AS UOM,
                'PI' AS DocType,
                CONVERT(varchar(10), h.DocDate, 103) AS DocDate,
                h.DocNo AS DocNo,
                d.Description AS ItemDescription,
                d.Location AS Location,
                CAST(d.Qty AS decimal(18,2)) AS Qty,
                CAST(d.UnitPrice AS decimal(18,2)) AS UnitPrice,
                ISNULL(d.Discount, '') AS Discount,
                CAST(d.SubTotal AS decimal(18,2)) AS SubTotal
            FROM dbo.PIDTL d
            INNER JOIN dbo.PI h
                ON d.DocKey = h.DocKey
            WHERE d.ItemCode = ?
              AND ISNULL(h.Cancelled, 'F') = 'F'
            ORDER BY h.DocDate DESC, h.DocNo DESC
        ";

        $stmt = sqlsrv_query($conn, $sql, [$itemCode]);

        if ($stmt === false) {
            $errs = sqlsrv_errors(SQLSRV_ERR_ERRORS);
            $msg = 'SQL query failed.';
            if (!empty($errs[0]['message'])) {
                $msg .= ' ' . $errs[0]['message'];
            }

            wp_send_json_error(['error' => $msg], 500);
        }

        $items = [];

        while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $items[] = [
                'accNo'       => isset($row['AccNo']) ? (string)$row['AccNo'] : '',
                'companyName' => isset($row['CompanyName']) ? (string)$row['CompanyName'] : '',
                'itemCode'    => isset($row['ItemCode']) ? (string)$row['ItemCode'] : '',
                'uom'         => isset($row['UOM']) ? (string)$row['UOM'] : '',
                't'           => isset($row['DocType']) ? (string)$row['DocType'] : '',
                'date'        => isset($row['DocDate']) ? (string)$row['DocDate'] : '',
                'docNo'       => isset($row['DocNo']) ? (string)$row['DocNo'] : '',
                'description' => isset($row['ItemDescription']) ? (string)$row['ItemDescription'] : '',
                'location'    => isset($row['Location']) ? (string)$row['Location'] : '',
                'qty'         => isset($row['Qty']) ? (float)$row['Qty'] : 0,
                'unitPrice'   => isset($row['UnitPrice']) ? (float)$row['UnitPrice'] : 0,
                'discount'    => isset($row['Discount']) ? (string)$row['Discount'] : '',
                'subTotal'    => isset($row['SubTotal']) ? (float)$row['SubTotal'] : 0,
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json_success(['items' => $items]);
    }
}

/* ============================================================
 * 5) Cash Sale Get By DocNo
 * action: ac_cs_get_by_docno
 * nonce : ac_cs_get_by_docno
 * ============================================================ */

add_action('wp_ajax_ac_cs_get_by_docno', 'ac_cs_ajax_get_by_docno');
add_action('wp_ajax_nopriv_ac_cs_get_by_docno', 'ac_cs_ajax_get_by_docno');

if (!function_exists('ac_cs_ajax_get_by_docno')) {
    function ac_cs_ajax_get_by_docno() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_cs_get_by_docno')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $docNo = ac_cs_ajax_clean_text(ac_cs_ajax_input('docno', ac_cs_ajax_input('docNo', '')), 100);

        if ($docNo === '') {
            wp_send_json_error(['error' => 'docno is required'], 400);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'MSSQL connection failed'], 500);
        }

        $sqlH = "
            SELECT TOP 1
                CS.DocKey,
                CS.DocNo,
                CS.DocDate,
                CS.DebtorCode,
                ISNULL(D.CompanyName, '') AS DebtorName
            FROM dbo.CS CS
            LEFT JOIN dbo.Debtor D
                ON D.AccNo = CS.DebtorCode
            WHERE CS.DocNo = ?
              AND ISNULL(CS.Cancelled, '') IN ('', '0', 'F', 'N')
        ";

        $stH = sqlsrv_query($conn, $sqlH, [$docNo]);

        if (!$stH) {
            wp_send_json_error([
                'error'  => 'SQL error (header)',
                'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $h = sqlsrv_fetch_array($stH, SQLSRV_FETCH_ASSOC);
        sqlsrv_free_stmt($stH);

        if (!$h) {
            wp_send_json_error(['error' => 'Cash Sale not found or cancelled.'], 404);
        }

        $sqlL = "
            SELECT
                d.Seq,
                d.ItemCode,
                ISNULL(d.Description, '') AS LineDesc,
                ISNULL(d.UOM, '') AS UOM,
                CAST(ISNULL(d.Qty, 0) AS float) AS Qty,
                CAST(ISNULL(d.UnitPrice, 0) AS float) AS UnitPrice,
                CAST(ISNULL(d.SubTotal, 0) AS float) AS SubTotal
            FROM dbo.CSDTL d
            WHERE d.DocKey = ?
            ORDER BY d.Seq ASC
        ";

        $stL = sqlsrv_query($conn, $sqlL, [$h['DocKey']]);

        if (!$stL) {
            wp_send_json_error([
                'error'  => 'SQL error (lines)',
                'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $lines = [];

        while ($r = sqlsrv_fetch_array($stL, SQLSRV_FETCH_ASSOC)) {
            $itemName = trim((string)($r['LineDesc'] ?? ''));
            if ($itemName === '') {
                $itemName = (string)($r['ItemCode'] ?? '');
            }

            $lines[] = [
                'seq'       => (int)($r['Seq'] ?? 0),
                'itemCode'  => (string)($r['ItemCode'] ?? ''),
                'itemName'  => $itemName,
                'uom'       => (string)($r['UOM'] ?? ''),
                'qty'       => (float)($r['Qty'] ?? 0),
                'unitPrice' => (float)($r['UnitPrice'] ?? 0),
                'subTotal'  => (float)($r['SubTotal'] ?? 0),
            ];
        }

        sqlsrv_free_stmt($stL);

        $docDate = '';

        if (!empty($h['DocDate'])) {
            if ($h['DocDate'] instanceof DateTime) {
                $docDate = $h['DocDate']->format('Y-m-d');
            } else {
                $ts = strtotime((string)$h['DocDate']);
                if ($ts) {
                    $docDate = date('Y-m-d', $ts);
                }
            }
        }

        wp_send_json_success([
            'header' => [
                'docKey'     => (string)($h['DocKey'] ?? ''),
                'docNo'      => (string)($h['DocNo'] ?? ''),
                'docDate'    => $docDate,
                'debtorCode' => (string)($h['DebtorCode'] ?? ''),
                'debtorName' => (string)($h['DebtorName'] ?? ''),
            ],
            'lines' => $lines,
        ]);
    }
}

/* ============================================================
 * 6) Get Item Images
 * action: ac_get_images
 * nonce : ac_get_images
 * ============================================================ */

add_action('wp_ajax_ac_get_images', 'ac_cs_ajax_get_images');
add_action('wp_ajax_nopriv_ac_get_images', 'ac_cs_ajax_get_images');

if (!function_exists('ac_cs_ajax_get_images')) {
    function ac_cs_ajax_get_images() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_get_images')) {
            wp_send_json_error(['message' => 'Bad nonce'], 403);
        }

        $item = ac_cs_ajax_clean_text(ac_cs_ajax_input('item', ac_cs_ajax_input('itemCode', '')), 100);

        if ($item === '') {
            wp_send_json_success([]);
        }

        $conn = ac_cs_ajax_mssql_conn(true);
        if (!$conn) {
            wp_send_json_error(['message' => 'DB connection error or missing MSSQL function'], 500);
        }

        $imgs = ac_cs_ajax_fetch_images_base64($conn, $item, 16);

        wp_send_json_success($imgs);
    }
}

if (!function_exists('ac_cs_ajax_fetch_images_base64')) {
    function ac_cs_ajax_fetch_images_base64($conn, $itemCode, $limit = 16) {
        $itemCode = trim((string)$itemCode);
        $limit = max(1, min(50, (int)$limit));

        if ($itemCode === '') return [];

        $sql = "
            SELECT TOP ($limit)
                ImageData
            FROM dbo.Web_StockImages
            WHERE LTRIM(RTRIM(ItemCode)) = ?
              AND ImageData IS NOT NULL
            ORDER BY (SELECT NULL)
        ";

        $stmt = sqlsrv_query($conn, $sql, [$itemCode]);

        if (!$stmt) {
            error_log('ac_cs_ajax_fetch_images_base64 failed: ' . print_r(sqlsrv_errors(SQLSRV_ERR_ERRORS), true));
            return [];
        }

        $out = [];

        while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $bin = $r['ImageData'] ?? null;

            if (is_resource($bin)) {
                $bin = stream_get_contents($bin);
            }

            if (!$bin) continue;

            $mime = 'image/jpeg';
            $head = substr($bin, 0, 12);

            if ($head !== false) {
                if (strncmp($head, "\x89PNG\r\n\x1a\n", 8) === 0) {
                    $mime = 'image/png';
                } elseif (strncmp($head, "GIF87a", 6) === 0 || strncmp($head, "GIF89a", 6) === 0) {
                    $mime = 'image/gif';
                } elseif (strncmp($head, "RIFF", 4) === 0 && substr($head, 8, 4) === "WEBP") {
                    $mime = 'image/webp';
                } elseif (strncmp($head, "\xFF\xD8\xFF", 3) === 0) {
                    $mime = 'image/jpeg';
                }
            }

            $out[] = "data:$mime;base64," . base64_encode($bin);

            if (count($out) >= $limit) break;
        }

        sqlsrv_free_stmt($stmt);

        return $out;
    }
}�+FB����������̘
�$����    h.DocNo AS DocNo,
                d.Description AS ItemDescription,
                d.Location AS Location,
                CAST(d.Qty AS decimal(18,2)) AS Qty,
                CAST(d.UnitPrice AS decimal(18,2)) AS UnitPrice,
                ISNULL(d.Discount, '') AS Discount,
                CAST(d.SubTotal AS decimal(18,2)) AS SubTotal
            FROM dbo.PIDTL d
            INNER JOIN dbo.PI h
                ON d.DocKey = h.DocKey
            WHERE d.ItemCode = ?
              AND ISNULL(h.Cancelled, 'F') = 'F'
            ORDER BY h.DocDate DESC, h.DocNo DESC
        ";

        $stmt = sqlsrv_query($conn, $sql, [$itemCode]);

        if ($stmt === false) {
            $errs = sqlsrv_errors(SQLSRV_ERR_ERRORS);
            $msg = 'SQL query failed.';
            if (!empty($errs[0]['message'])) {
                $msg .= ' ' . $errs[0]['message'];
            }

            wp_send_json_error(['error' => $msg], 500);
        }

        $items = [];

        while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $items[] = [
                'accNo'       => isset($row['AccNo']) ? (string)$row['AccNo'] : '',
                'companyName' => isset($row['CompanyName']) ? (string)$row['CompanyName'] : '',
                'itemCode'    => isset($row['ItemCode']) ? (string)$row['ItemCode'] : '',
                'uom'         => isset($row['UOM']) ? (string)$row['UOM'] : '',
                't'           => isset($row['DocType']) ? (string)$row['DocType'] : '',
                'date'        => isset($row['DocDate']) ? (string)$row['DocDate'] : '',
                'docNo'       => isset($row['DocNo']) ? (string)$row['DocNo'] : '',
                'description' => isset($row['ItemDescription']) ? (string)$row['ItemDescription'] : '',
                'location'    => isset($row['Location']) ? (string)$row['Location'] : '',
                'qty'         => isset($row['Qty']) ? (float)$row['Qty'] : 0,
                'unitPrice'   => isset($row['UnitPrice']) ? (float)$row['UnitPrice'] : 0,
                'discount'    => isset($row['Discount']) ? (string)$row['Discount'] : '',
                'subTotal'    => isset($row['SubTotal']) ? (float)$row['SubTotal'] : 0,
            ];
        }

        sqlsrv_free_stmt($stmt);

        wp_send_json_success(['items' => $items]);
    }
}

/* ============================================================
 * 5) Cash Sale Get By DocNo
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_cs_get_by_docno', 'ac_cs_ajax_get_by_docno');
add_action('wp_ajax_nopriv_ac_cs_get_by_docno', 'ac_cs_ajax_get_by_docno');

if (!function_exists('ac_cs_ajax_get_by_docno')) {
    function ac_cs_ajax_get_by_docno() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_cs_get_by_docno')) {
            wp_send_json_error(['error' => 'Invalid nonce.'], 403);
        }

        $docNo = ac_cs_ajax_clean_text(ac_cs_ajax_input('docno', ac_cs_ajax_input('docNo', '')), 100);

        if ($docNo === '') {
            wp_send_json_error(['error' => 'docno is required'], 400);
        }

        $conn = ac_cs_ajax_mssql_conn(false);
        if (!$conn) {
            wp_send_json_error(['error' => 'MSSQL connection failed'], 500);
        }

        $sqlH = "
            SELECT TOP 1
                CS.DocKey,
                CS.DocNo,
                CS.DocDate,
                CS.DebtorCode,
                ISNULL(D.CompanyName, '') AS DebtorName
            FROM dbo.CS CS
            LEFT JOIN dbo.Debtor D
                ON D.AccNo = CS.DebtorCode
            WHERE CS.DocNo = ?
              AND ISNULL(CS.Cancelled, '') IN ('', '0', 'F', 'N')
        ";

        $stH = sqlsrv_query($conn, $sqlH, [$docNo]);

        if (!$stH) {
            wp_send_json_error([
                'error'  => 'SQL error (header)',
                'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $h = sqlsrv_fetch_array($stH, SQLSRV_FETCH_ASSOC);
        sqlsrv_free_stmt($stH);

        if (!$h) {
            wp_send_json_error(['error' => 'Cash Sale not found or cancelled.'], 404);
        }

        $sqlL = "
            SELECT
                d.Seq,
                d.ItemCode,
                ISNULL(d.Description, '') AS LineDesc,
                ISNULL(d.UOM, '') AS UOM,
                CAST(ISNULL(d.Qty, 0) AS float) AS Qty,
                CAST(ISNULL(d.UnitPrice, 0) AS float) AS UnitPrice,
                CAST(ISNULL(d.SubTotal, 0) AS float) AS SubTotal
            FROM dbo.CSDTL d
            WHERE d.DocKey = ?
            ORDER BY d.Seq ASC
        ";

        $stL = sqlsrv_query($conn, $sqlL, [$h['DocKey']]);

        if (!$stL) {
            wp_send_json_error([
                'error'  => 'SQL error (lines)',
                'detail' => sqlsrv_errors(SQLSRV_ERR_ERRORS),
            ], 500);
        }

        $lines = [];

        while ($r = sqlsrv_fetch_array($stL, SQLSRV_FETCH_ASSOC)) {
            $itemName = trim((string)($r['LineDesc'] ?? ''));
            if ($itemName === '') {
                $itemName = (string)($r['ItemCode'] ?? '');
            }

            $lines[] = [
                'seq'       => (int)($r['Seq'] ?? 0),
                'itemCode'  => (string)($r['ItemCode'] ?? ''),
                'itemName'  => $itemName,
                'uom'       => (string)($r['UOM'] ?? ''),
                'qty'       => (float)($r['Qty'] ?? 0),
                'unitPrice' => (float)($r['UnitPrice'] ?? 0),
                'subTotal'  => (float)($r['SubTotal'] ?? 0),
            ];
        }

        sqlsrv_free_stmt($stL);

        $docDate = '';

        if (!empty($h['DocDate'])) {
            if ($h['DocDate'] instanceof DateTime) {
                $docDate = $h['DocDate']->format('Y-m-d');
            } else {
                $ts = strtotime((string)$h['DocDate']);
                if ($ts) {
                    $docDate = date('Y-m-d', $ts);
                }
            }
        }

        wp_send_json_success([
            'header' => [
                'docKey'     => (string)($h['DocKey'] ?? ''),
                'docNo'      => (string)($h['DocNo'] ?? ''),
                'docDate'    => $docDate,
                'debtorCode' => (string)($h['DebtorCode'] ?? ''),
                'debtorName' => (string)($h['DebtorName'] ?? ''),
            ],
            'lines' => $lines,
        ]);
    }
}

/* ============================================================
 * 6) Get Item Images
 * (unchanged)
 * ============================================================ */

add_action('wp_ajax_ac_get_images', 'ac_cs_ajax_get_images');
add_action('wp_ajax_nopriv_ac_get_images', 'ac_cs_ajax_get_images');

if (!function_exists('ac_cs_ajax_get_images')) {
    function ac_cs_ajax_get_images() {
        $nonce = sanitize_text_field((string) ac_cs_ajax_input('nonce', ''));

        if (!wp_verify_nonce($nonce, 'ac_get_images')) {
            wp_send_json_error(['message' => 'Bad nonce'], 403);
        }

        $item = ac_cs_ajax_clean_text(ac_cs_ajax_input('item', ac_cs_ajax_input('itemCode', '')), 100);

        if ($item === '') {
            wp_send_json_success([]);
        }

        $conn = ac_cs_ajax_mssql_conn(true);
        if (!$conn) {
            wp_send_json_error(['message' => 'DB connection error or missing MSSQL function'], 500);
        }

        $imgs = ac_cs_ajax_fetch_images_base64($conn, $item, 16);

        wp_send_json_success($imgs);
    }
}

if (!function_exists('ac_cs_ajax_fetch_images_base64')) {
    function ac_cs_ajax_fetch_images_base64($conn, $itemCode, $limit = 16) {
        $itemCode = trim((string)$itemCode);
        $limit = max(1, min(50, (int)$limit));

        if ($itemCode === '') return [];

        $sql = "
            SELECT TOP ($limit)
                ImageData
            FROM dbo.Web_StockImages
            WHERE LTRIM(RTRIM(ItemCode)) = ?
              AND ImageData IS NOT NULL
            ORDER BY (SELECT NULL)
        ";

        $stmt = sqlsrv_query($conn, $sql, [$itemCode]);

        if (!$stmt) {
            error_log('ac_cs_ajax_fetch_images_base64 failed: ' . print_r(sqlsrv_errors(SQLSRV_ERR_ERRORS), true));
            return [];
        }

        $out = [];

        while ($r = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
            $bin = $r['ImageData'] ?? null;

            if (is_resource($bin)) {
                $bin = stream_get_contents($bin);
            }

            if (!$bin) continue;

            $mime = 'image/jpeg';
            $head = substr($bin, 0, 12);

            if ($head !== false) {
                if (strncmp($head, "\x89PNG\r\n\x1a\n", 8) === 0) {
                    $mime = 'image/png';
                } elseif (strncmp($head, "GIF87a", 6) === 0 || strncmp($head, "GIF89a", 6) === 0) {
                    $mime = 'image/gif';
                } elseif (strncmp($head, "RIFF", 4) === 0 && substr($head, 8, 4) === "WEBP") {
                    $mime = 'image/webp';
                } elseif (strncmp($head, "\xFF\xD8\xFF", 3) === 0) {
                    $mime = 'image/jpeg';
                }
            }

            $out[] = "data:$mime;base64," . base64_encode($bin);

            if (count($out) >= $limit) break;
        }

        sqlsrv_free_stmt($stmt);

        return $out;
    }
}�̘�

Youez - 2016 - github.com/yon3zu
LinuXploit