AMS_Backend/app/Controllers/ZeoniqController.php
2025-11-06 13:41:06 +08:00

725 lines
25 KiB
PHP

<?php
namespace App\Controllers;
use App\Models\Carts;
use App\Models\State;
use App\Models\Orders;
use App\Models\Outlet;
use App\Models\Country;
use App\Models\Customer;
use App\Models\CartItems;
use App\Models\PromoCode;
use App\Models\OrderItems;
use App\Models\OrderTaxes;
use App\Models\PromoSetting;
use App\Models\CustomerPoint;
use App\Models\OrderPayments;
use App\Models\CustomerWallet;
use App\Models\CartItemOptions;
use App\Models\CustomerAddress;
use App\Models\OrderDeliveries;
use App\Models\OrderItemOptions;
use App\Models\MenuItemVariations;
use App\Models\VoucherRedemptions;
use App\Models\CustomerVoucherList;
use App\Models\LogZeoniq;
use App\Models\SettingCustomerType;
use App\Models\PromoRedemptionRecord;
use App\Models\SettingMembershipTier;
use CodeIgniter\RESTful\ResourceController;
class ZeoniqController extends ResourceController
{
protected $format = 'json'; // Always return JSON
private $customerModel;
private $customerTypeModel;
private $membershipTierModel;
private $customerAddressModel;
private $customerWalletModel;
private $customerPointModel;
private $customerVoucher;
private $promoCode;
private $promoSetting;
private $carts;
private $cart_items;
private $cart_item_options;
private $orders;
private $order_items;
private $order_item_options;
private $order_payments;
private $order_taxes;
private $customer;
private $customer_wallet;
private $db;
private $cart_service;
private $calculate_service;
private $promo_service;
private $outlet;
private $voucher_service;
private $variations;
private $order_deliveries;
private $promo_redemptions_record;
private $voucher_redemptions;
private $zeoniqModel;
public function __construct()
{
$this->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);
}
}