customerModel = new Customer(); $this->customerTypeModel = new SettingCustomerType(); $this->membershipTierModel = new SettingMembershipTier(); $this->customerAddressModel = new CustomerAddress(); $this->customerWalletModel = new CustomerWallet(); $this->customerPointModel = new CustomerPoint(); $this->customerVoucher = new CustomerVoucherList(); $this->promoCode = new PromoCode(); $this->promoSetting = new PromoSetting(); $this->carts = new Carts(); $this->cart_items = new CartItems(); $this->cart_item_options = new CartItemOptions(); $this->orders = new Orders(); $this->order_items = new OrderItems(); $this->order_item_options = new OrderItemOptions(); $this->order_payments = new OrderPayments(); $this->order_taxes = new OrderTaxes(); $this->cart_service = service('cartService'); $this->calculate_service = service('calculateService'); $this->promo_service = service('promoService'); $this->voucher_service = service('voucherService'); $this->outlet = new Outlet(); $this->variations = new MenuItemVariations(); $this->order_deliveries = new OrderDeliveries(); $this->promo_redemptions_record = new PromoRedemptionRecord(); $this->voucher_redemptions = new VoucherRedemptions(); $this->zeoniqModel = new LogZeoniq(); } public function logzeoniq($action, $gross_amount, $net_total, $points_earned, $wallet_used, $voucher_used) { $logZeoniqData = [ 'action' => $action, 'gross_amount' => $gross_amount, 'net_total' => $net_total, 'points_earned' => $points_earned, 'wallet_used' => $wallet_used, 'voucher_used' => $voucher_used, 'created_at' => date('Y-m-d H:i:s') ]; return $this->zeoniqModel->insert($logZeoniqData); } public function getmember() { $search_text = $this->request->getGet('SearchText'); if (empty($search_text)) { return $this->failValidationErrors('Search Text is required.'); } // ✅ Normalize phone: remove '+' and any non-digit characters $phone = preg_replace('/\D/', '', $search_text); // ✅ Search for ONE customer using LIKE on phone $customer = $this->customerModel ->where('status', 'active') ->like('phone', $phone, 'both') // both = wraps with % % ->orderBy('id', 'ASC') ->first(); if (empty($customer)) { return $this->respond([ "Code" => "400", "Message" => "Customer not found.", "ExceptionMessage" => "Customer{$search_text} not found", "Params" => $search_text ], 400); } // ✅ Address lookup $array_address = $this->customerAddressModel ->where('customer_id', $customer['id']) ->first(); // ✅ Latest point balance from CustomerPoint model $membership = $this->membershipTierModel ->where('category_id', $customer['customer_tier_id']) ->first(); $array_member[] = [ "MemberNo" => $customer['id'], "CardNo" => $customer['customer_referral_code'], "MemberShipCode" => $customer['customer_tier_id'], "MembershipLevelDesc" => $membership['name'], "MemberName" => $customer['name'], "ExpiryDate" => "", "DateOfBirth" => ($customer['birthday'] != '0000-00-00' ? $customer['birthday'] : ''), "Address1" => $array_address['address'] ?? '', "Address2" => "", "Address3" => "", "AddCity" => "", "AddPostal" => "", "CountryCode" => "", "StateCode" => "", "Email" => $customer['email'], "MobileNo" => $customer['phone'], "PhoneNo" => $customer['phone'], "ICNo" => "", "PassportNo" => "", "RefNo" => "", "PointBalance" => $customer['customer_point'], "PointValue" => (1/0.01), "WalletBalance" => $customer['customer_wallet'] ]; $requestData = [ 'SearchText' => $search_text, ]; $json_member = json_encode($array_member); return $this->respond([ "Code" => "200", "Message" => "Successful", "Member" => $array_member ], 200); } public function submitsales() { $data = $this->request->getJSON(true); // Validation data $rules = [ 'MemberNo' => 'required', 'BillNo' => 'required', 'GrossAmt' => 'required', 'DiscAmt' => 'required', 'TaxAmt' => 'required', 'AdjAmt' => 'required', 'NetTotal' => 'required', 'Payments' => 'required', 'Vouchers' => 'required', ]; if (!$this->validate($rules)) { return $this->failValidationErrors($this->validator->getErrors()); } $array_point = []; $this->db = db_connect(); $this->db->transBegin(); try { $memberNo = $data['MemberNo']; $billNo = $data['BillNo']; $grossAmt = $data['GrossAmt']; $discAmt = $data['DiscAmt']; $taxAmt = $data['TaxAmt']; $adjAmt = $data['AdjAmt']; $netTotal = $data['NetTotal']; $remarks = $data['Remarks']; $payments = $data['Payments']; $vouchers = $data['Vouchers']; // Validate Member $customer = $this->customerModel->where('id', $memberNo)->first(); if (!$customer) { return $this->respond([ "Code" => "400", "Message" => "Member {$memberNo} not found", "Params" => $memberNo ], 400); } // Get customer's current points and wallet balances $customer_point_balance = $customer['customer_point']; $customer_wallet_balance = $customer['customer_wallet']; $total_wallet = $rewards_point = 0; // Get wallet usage if (!empty($payments)) { foreach ($payments as $value) { $total_wallet += $value['WalletAmount'] ?? 0; } } // Rewards (points earned from purchase) $rewards_point = ($netTotal > 0) ? floor($netTotal) : 0; // Get voucher used $voucher = null; if (!empty($vouchers)) { foreach ($vouchers as $code) { $voucher = $this->customerVoucher ->where('voucher_code', $code) ->where('customer_id', $memberNo) ->where('voucher_status', 'active') ->first(); if ($voucher) { break; } } // If vouchers were passed in, but none found in DB → stop if (!$voucher) { return $this->respond([ "Code" => "400", "Message" => "Voucher not found or used", "Params" => $vouchers ], 400); } } // Save and update voucher redemption if used if ($voucher) { $this->customerVoucher ->where('voucher_code', $voucher['voucher_code']) ->where('customer_id', $memberNo) ->where('voucher_status !=', 'used') ->set(['voucher_status' => 'used', 'voucher_order_id' => $billNo]) ->update(); } // Add earned points if ($rewards_point > 0) { $customer_point = $customer_point_balance + $rewards_point; $this->customerModel->update($memberNo, ['customer_point' => $customer_point]); $this->customerPointModel->insert([ 'customer_id' => $memberNo, 'related_id' => $billNo, 'related_type' => 'order', 'action' => 'in', 'current' => $customer_point_balance, 'in' => $rewards_point, 'out' => 0, 'balance' => $customer_point, 'remark' => "{$rewards_point} points earned from order {$billNo}", ]); } // Deduct wallet if used if ($total_wallet > 0) { $customer_wallet_balance -= $total_wallet; $this->customerModel->update($memberNo, ['customer_wallet' => $customer_wallet_balance]); $this->order_payments->update($billNo, [ 'status' => 'completed', 'paid_at' => date('Y-m-d H:i:s') ]); $this->customerWalletModel->insert([ 'customer_id' => $memberNo, 'related_id' => $billNo, 'related_type' => 'order', 'action' => 'out', 'current' => $customer['customer_wallet'], 'in' => 0, 'out' => $total_wallet, 'balance' => $customer_wallet_balance, 'remark' => "{$total_wallet} deducted from wallet for order {$billNo}", ]); } $point_balance = $customer_point_balance + $rewards_point; // Record the zeoniq API call $salesTranId = $this->logzeoniq('integration-submitsales', $grossAmt, $netTotal, $rewards_point, $total_wallet ?? 0, $voucher['voucher_code'] ?? ''); $array_point = [ "SalesTranId" => $salesTranId, "PointEarned" => number_format($rewards_point, 1), "PointRedeemed" => number_format($rewards_point, 1), "PointBalance" => number_format($point_balance, 1), "WalletBalance" => number_format($customer_wallet_balance, 1) ]; $error = $this->db->error(); if ($error['code'] != 0) { log_message('error', 'DB ERROR: ' . print_r($error, true)); throw new \Exception("DB Error: " . $error['message']); } // Commit transaction if ($this->db->transStatus() === false) { $this->db->transRollback(); throw new \Exception("Transaction failed."); } $this->db->transCommit(); return $this->respond([ "Code" => "200", "Message" => "submitsales Successful", "Data" => $array_point ], 200); } catch (\Throwable $e) { $this->db->transRollback(); return $this->respond([ "Code" => "400", "Message" => $e->getMessage(), "Params" => $data['MemberNo'] ?? null ], 400); } } public function reversesales() { $data = $this->request->getJSON(true); // Validate data $rules = [ 'SalesTranId' => 'required', 'MemberNo' => 'required', 'BillNo' => 'required', 'Vouchers' => 'required', ]; if (!$this->validate($rules)) { return $this->failValidationErrors($this->validator->getErrors()); } $this->db = db_connect(); $this->db->transBegin(); try { $salesTranId = $data['SalesTranId'] ?? null; $memberNo = $data['MemberNo'] ?? null; $billNo = $data['BillNo'] ?? null; $vouchers = $data['Vouchers'] ?? null; // Validate Member $customer = $this->customerModel->where('id', $memberNo)->first(); if (!$customer) { return $this->respond([ "Code" => "400", "Message" => "Member {$memberNo} not found", "Params" => $memberNo ], 400); } // Get customer's current points and wallet balances $customer_point_balance = $customer['customer_point']; $customer_wallet_balance = $customer['customer_wallet']; $zeoniqLog = $this->zeoniqModel ->where("id", $salesTranId) ->first(); if (!$zeoniqLog) { return $this->respond([ "Code" => "400", "Message" => "Order Record not found", "Params" => $salesTranId ], 400); } $points_earned = $zeoniqLog['points_earned']; $wallet_used = $zeoniqLog['wallet_used']; // Get voucher used $voucher = null; if (!empty($vouchers)) { foreach ($vouchers as $code) { $voucher = $this->customerVoucher ->where('voucher_code', $code) ->where('customer_id', $memberNo) ->where('voucher_status', 'used') ->first(); if ($voucher) { break; } } // If vouchers were passed in, but none found in DB → stop if (!$voucher) { return $this->respond([ "Code" => "400", "Message" => "Voucher not found", "Params" => $vouchers ], 400); } } // Revert voucher redemption if used if ($voucher) { $this->customerVoucher ->where('voucher_code', $voucher['voucher_code']) ->where('customer_id', $memberNo) ->where('voucher_status =', 'used') ->set(['voucher_status' => 'active', 'voucher_order_id' => 0]) ->update(); } // Deduct points if($points_earned > 0) { $customer_point = $customer_point_balance - $points_earned; $this->customerModel->update($memberNo, ['customer_point' => $customer_point]); $this->customerPointModel->insert([ 'customer_id' => $memberNo, 'related_id' => $billNo, 'related_type' => 'order', 'action' => 'out', 'current' => $customer_point_balance, 'in' => 0, 'out' => $points_earned, 'balance' => $customer_point, 'remark' => "{$points_earned} points losed from cancellation of order {$billNo}", ]); } // Add back customer's wallet if used if($wallet_used > 0) { $out = $wallet_used; $customer_wallet = $customer_wallet_balance + $out; $this->customerModel->update($memberNo, ['customer_wallet' => $customer_wallet]); $this->order_payments->update($billNo, [ 'status' => 'cancelled', 'deleted_at' => date('Y-m-d H:i:s') ]); $this->customerWalletModel->insert([ 'customer_id' => $memberNo, 'related_id' => $billNo, 'related_type' => 'order', 'action' => 'in', 'current' => $customer_wallet_balance, 'in' => $out, 'out' => 0, 'balance' => $customer_wallet, 'remark' => "{$out} added to the wallet for cancellation of order {$billNo}", ]); } // Record the zeoniq API call $this->logzeoniq('integration-reversesales', $zeoniqLog['gross_amount'], $zeoniqLog['net_total'], -$points_earned, -($wallet_used ?? 0), $voucher['voucher_code'] ?? ''); $error = $this->db->error(); if ($error['code'] != 0) { log_message('error', 'DB ERROR: ' . print_r($error, true)); throw new \Exception("DB Error: " . $error['message']); } // ✅ Commit transaction if ($this->db->transStatus() === false) { $this->db->transRollback(); throw new \Exception("Transaction failed."); } $this->db->transCommit(); return $this->respond([ "Code" => "200", "Message" => "reversesales Successful", ], 200); } catch (\Throwable $e) { $this->db->transRollback(); return $this->respond([ "Code" => "400", "Message" => $e->getMessage(), "Params" => $data['MemberNo'] ?? null ], 400); } } public function getvoucher() { $member_no = $this->request->getGet('MemberNo'); $voucher_no = $this->request->getGet('VoucherNo'); if (empty($member_no)) { return $this->failValidationErrors('Member No is required.'); } if (empty($voucher_no)) { return $this->failValidationErrors('Voucher No is required.'); } // ✅ Search for customer $customer = $this->customerModel ->where('status', 'active') ->where('id', $member_no) ->first(); if (empty($customer)) { return $this->respond([ "Code" => "400", "Message" => "Customer not found.", "ExceptionMessage" => "Customer {$member_no} not found", "Params" => $member_no ], 400); } // ✅ Search for voucher $voucher = $this->customerVoucher ->where('voucher_code', $voucher_no) ->where('customer_id', $member_no) ->first(); if (empty($voucher)) { return $this->respond([ "Code" => "400", "Message" => "Voucher not found.", "ExceptionMessage" => "Voucher {$voucher_no} not found", "Params" => $voucher_no ], 400); } // ✅ Search for promo code $promo_code = $this->promoCode ->where('promo_setting_id', $voucher['promo_setting_id']) ->first(); // ✅ Search for promo setting $promo_setting = $this->promoSetting ->where('id', $voucher['promo_setting_id']) ->first(); // ✅ Decode JSON from promo_setting column $settings = json_decode($promo_setting['promo_setting'], true); // ✅ Get Promo Amount (default to 0 if not found) $promo_amount = $settings['Promo']['amount'] ?? 0; // ✅ Format to 2 decimal places $promo_amount = number_format((float) $promo_amount, 2, '.', ''); $array_voucher = [ "VoucherNo" => $voucher['voucher_code'], "ExpiryDate" => $voucher['voucher_expiry_date'], "VoucherTypeCode" => $promo_code['code'], "VoucherTypeDesc" => $promo_setting['promotion_type'], "StatusFlag" => $voucher['voucher_status'], "VchValue" => $promo_amount, ]; $json_voucher = json_encode($array_voucher); $requestData = [ 'MemberNo' => $member_no, 'VoucherNo' => $voucher_no ]; return $this->respond([ "Code" => "200", "Message" => "Successful", "Voucher" => $array_voucher ], 200); } public function getvoucherlist() { $member_no = $this->request->getGet('MemberNo'); if (empty($member_no)) { return $this->failValidationErrors('Member No is required.'); } // ✅ Search for customer $customer = $this->customerModel ->where('status', 'active') ->where('id', $member_no) ->first(); if (empty($customer)) { return $this->respond([ "Code" => "400", "Message" => "Customer not found.", "ExceptionMessage" => "Customer {$member_no} not found", "Params" => $member_no ], 400); } // ✅ Fetch all vouchers for this customer $vouchers = $this->customerVoucher ->where('customer_id', $member_no) ->findAll(); if (empty($vouchers)) { return $this->respond([ "Code" => "400", "Message" => "No vouchers found.", "ExceptionMessage" => "No vouchers for Member {$member_no}", "Params" => $member_no ], 400); } $array_vouchers = []; foreach ($vouchers as $voucher) { // ✅ Skip if promo_setting_id is 0 or null if (empty($voucher['promo_setting_id']) || $voucher['promo_setting_id'] == 0) { continue; } // ✅ Get promo code $promo_code = $this->promoCode ->where('promo_setting_id', $voucher['promo_setting_id']) ->first(); // ✅ Get promo setting $promo_setting = $this->promoSetting ->where('id', $voucher['promo_setting_id']) ->first(); if (empty($promo_setting)) { continue; // skip if no promo_setting found } // ✅ Decode JSON settings $settings = json_decode($promo_setting['promo_setting'], true); // ✅ Get promo amount, format to 2 decimals $promo_amount = $settings['Promo']['amount'] ?? 0; $promo_amount = number_format((float) $promo_amount, 2, '.', ''); $array_vouchers[] = [ "VoucherNo" => $voucher['voucher_code'], "ExpiryDate" => $voucher['voucher_expiry_date'], "VoucherTypeCode" => $promo_code['code'] ?? '', "VoucherTypeDesc" => $promo_setting['promotion_type'] ?? '', "StatusFlag" => $voucher['voucher_status'], "VchValue" => $promo_amount, ]; } if (empty($array_vouchers)) { return $this->respond([ "Code" => "400", "Message" => "No valid vouchers found.", "ExceptionMessage" => "All vouchers skipped for Member {$member_no}", "Params" => $member_no ], 400); } $json_vouchers = json_encode($array_vouchers); $requestData = [ 'MemberNo' => $member_no ]; return $this->respond([ "Code" => "200", "Message" => "Successful", "Vouchers" => $array_vouchers ], 200); } }