1387 lines
47 KiB
PHP
1387 lines
47 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Backend;
|
|
|
|
use App\Models\Customer;
|
|
use App\Models\StudentCard;
|
|
|
|
use App\Models\CheckinStreak;
|
|
use App\Models\CustomerPoint;
|
|
use App\Models\CustomerWallet;
|
|
use App\Models\CustomerAddress;
|
|
use App\Models\LogCustomerCheckin;
|
|
use App\Models\SettingCheckinRule;
|
|
use App\Models\SettingCustomerType;
|
|
use App\Models\SettingMembershipTier;
|
|
|
|
use CodeIgniter\HTTP\ResponseInterface;
|
|
use CodeIgniter\RESTful\ResourceController;
|
|
|
|
helper('image');
|
|
class CustomerController extends ResourceController
|
|
{
|
|
private $customerModel;
|
|
private $customerTypeModel;
|
|
private $membershipTierModel;
|
|
private $customerAddressModel;
|
|
private $customerWalletModel;
|
|
private $customerPointModel;
|
|
private $checkinStreakModel;
|
|
private $logCheckinModel;
|
|
private $checkinRuleModel;
|
|
private $studentCardModel;
|
|
|
|
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->checkinStreakModel = new CheckinStreak();
|
|
$this->logCheckinModel = new LogCustomerCheckin();
|
|
$this->checkinRuleModel = new SettingCheckinRule();
|
|
$this->studentCardModel = new StudentCard();
|
|
|
|
}
|
|
|
|
public function create()
|
|
{
|
|
helper(['form']);
|
|
|
|
$validationRules = [
|
|
'customer_type' => 'required',
|
|
'birthday' => 'required|valid_date[Y-m-d]',
|
|
'phone' => 'required|is_unique[customers.phone]',
|
|
'email' => 'required|valid_email|is_unique[customers.email]',
|
|
'password_hash' => 'required|min_length[8]',
|
|
'name' => 'required',
|
|
];
|
|
|
|
if (!$this->validate($validationRules)) {
|
|
return $this->failValidationErrors($this->validator->getErrors());
|
|
}
|
|
|
|
$customerTypeName = $this->request->getVar('customer_type');
|
|
$customerType = $this->customerTypeModel->where('name', $customerTypeName)->first();
|
|
|
|
if (!$customerType) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer type '{$customerTypeName}' not found.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
$tierName = $this->request->getVar('customer_tier');
|
|
$tier = $this->membershipTierModel->where('name', $tierName)->first();
|
|
|
|
if (!$tier) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Membership tier '{$tierName}' not found.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
// Profile picture
|
|
$profileImage = $this->request->getFile('profile_picture');
|
|
$imagePath = null;
|
|
$compressedPath = null;
|
|
|
|
if ($profileImage && $profileImage->isValid() && !$profileImage->hasMoved()) {
|
|
$originalPath = $_SERVER['DOCUMENT_ROOT'] . '/backend/uploads/customers';
|
|
$compressedDir = $_SERVER['DOCUMENT_ROOT'] . '/backend/uploads/customers_compressed';
|
|
|
|
try {
|
|
$result = save_image_with_compression($profileImage, $originalPath, $compressedDir, 900, 80);
|
|
// You can return relative paths if you want to store them in the DB
|
|
$imagePath = str_replace($_SERVER['DOCUMENT_ROOT'].'/', '', $result['original']);
|
|
$compressedPath = str_replace($_SERVER['DOCUMENT_ROOT'].'/', '', $result['compressed']);
|
|
} catch (\Exception $e) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Profile picture upload failed: ' . $e->getMessage(),
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
|
|
$data = [
|
|
'customer_type' => $customerTypeName,
|
|
'customer_tier_id' => $tier['id'],
|
|
'phone' => $this->request->getVar('phone'),
|
|
'email' => $this->request->getVar('email'),
|
|
'name' => $this->request->getVar('name'),
|
|
'password_hash' => md5($this->request->getVar('password')), // Fix match
|
|
'birthday' => $this->request->getVar('birthday'),
|
|
'customer_referral_id' => $this->request->getVar('customer_referral_id'),
|
|
'customer_wallet' => 0,
|
|
'customer_point' => 0,
|
|
'status' => 'active',
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
if ($imagePath) {
|
|
$data['profile_picture'] = $imagePath;
|
|
}
|
|
|
|
if ($this->customerModel->insert($data)) {
|
|
$insertedId = $this->customerModel->getInsertID();
|
|
|
|
// Format the ID to be used as referralCode (e.g., 00001)
|
|
$referralCode = str_pad($insertedId, 6, '0', STR_PAD_LEFT);
|
|
|
|
// Update the customer record with the referralCode
|
|
$this->customerModel->update($insertedId, ['customer_referral_code' => $referralCode]);
|
|
|
|
unset($data['password_hash']);
|
|
$data['customer_referral_code'] = $referralCode;
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer created successfully.',
|
|
'data' => [
|
|
'customer_id' => $insertedId,
|
|
'referral_code' => $referralCode,
|
|
'customer' => $data
|
|
]
|
|
];
|
|
return $this->respond($response, 200);
|
|
} else {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Failed to create customer.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
$customers = $this->customerModel
|
|
->select('
|
|
customers.id,
|
|
customers.name,
|
|
customers.email,
|
|
customers.phone,
|
|
customers.customer_type,
|
|
customers.birthday,
|
|
customers.profile_picture,
|
|
customers.status,
|
|
customers.customer_referral_id,
|
|
customers.customer_referral_code,
|
|
customers.customer_wallet,
|
|
customers.customer_point,
|
|
setting_membership_tiers.name AS customer_tier,
|
|
customers.created_at,
|
|
')
|
|
->join('setting_membership_tiers', 'setting_membership_tiers.id = customers.customer_tier_id', 'left')
|
|
->findAll();
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer list retrieved successfully.',
|
|
'data' => $customers
|
|
];
|
|
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function show($id = null)
|
|
{
|
|
if ($id === null) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// First get customer basic info
|
|
$customer = $this->customerModel
|
|
->select('
|
|
customers.id,
|
|
customers.name,
|
|
customers.email,
|
|
customers.phone,
|
|
customers.customer_type,
|
|
customers.birthday,
|
|
customers.profile_picture,
|
|
customers.status,
|
|
customers.customer_referral_id,
|
|
customers.customer_referral_code,
|
|
customers.customer_wallet,
|
|
customers.customer_point,
|
|
setting_membership_tiers.name AS customer_tier,
|
|
customers.created_at
|
|
')
|
|
->join('setting_membership_tiers', 'setting_membership_tiers.id = customers.customer_tier_id', 'left')
|
|
->where('customers.id', $id)
|
|
->first();
|
|
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer with ID {$id} not found.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
// Then get vouchers separately
|
|
$vouchers = $this->customerModel
|
|
->db // Assuming you have access to the database connection
|
|
->table('customer_voucher_list')
|
|
->select('voucher_code, voucher_expiry_date, voucher_status')
|
|
->where('customer_id', $id)
|
|
->get()
|
|
->getResultArray();
|
|
|
|
// Build full URL for profile picture if it exists
|
|
if (!empty($customer['profile_picture'])) {
|
|
$baseUrl = 'https://icom.ipsgroup.com.my';
|
|
$customer['profile_picture_url'] = $baseUrl . '/backend/' . $customer['profile_picture'];
|
|
} else {
|
|
$customer['profile_picture_url'] = null;
|
|
}
|
|
|
|
// Add vouchers to customer data
|
|
$customer['vouchers'] = $vouchers;
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer retrieved successfully.',
|
|
'data' => $customer
|
|
];
|
|
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function update($id = null)
|
|
{
|
|
helper(['form']);
|
|
|
|
$customer = $this->customerModel->find($id);
|
|
|
|
if (!$customer) {
|
|
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer with ID {$id} not found.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
$validationRules = [
|
|
'customer_type' => 'required',
|
|
'customer_tier' => 'required',
|
|
'birthday' => 'required|valid_date[Y-m-d]',
|
|
// 'phone' => "required|is_unique[customers.phone,id,{$id}]",
|
|
// 'email' => "required|valid_email|is_unique[customers.email,id,{$id}]",
|
|
'name' => 'required',
|
|
'password' => 'permit_empty|min_length[6]'
|
|
];
|
|
|
|
// Only add phone/email uniqueness check if they are being changed
|
|
if ($this->request->getVar('phone') !== $customer['phone']) {
|
|
$validationRules['phone'] = "required|is_unique[customers.phone,id,{$id}]";
|
|
} else {
|
|
$validationRules['phone'] = "required";
|
|
}
|
|
|
|
if ($this->request->getVar('email') !== $customer['email']) {
|
|
$validationRules['email'] = "required|valid_email|is_unique[customers.email,id,{$id}]";
|
|
} else {
|
|
$validationRules['email'] = "required|valid_email";
|
|
}
|
|
|
|
if (!$this->validate($validationRules)) {
|
|
|
|
$response =[
|
|
'status' => 'error',
|
|
'message' => 'Validation failed.',
|
|
'data' => $this->validator->getErrors()
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// Get raw input values
|
|
$customerTypeName = $this->request->getVar('customer_type');
|
|
$tierName = $this->request->getVar('customer_tier');
|
|
|
|
// Validate customer tier exists
|
|
$tier = $this->membershipTierModel->where('name', $tierName)->first();
|
|
|
|
if (!$tier) {
|
|
return $this->failNotFound("Membership tier '{$tierName}' not found.");
|
|
}
|
|
|
|
$data = [
|
|
'customer_type' => $customerTypeName, // Store name instead of ID
|
|
'customer_tier_id' => $tier['id'], // Store tier ID
|
|
'phone' => $this->request->getVar('phone'),
|
|
'email' => $this->request->getVar('email'),
|
|
'name' => $this->request->getVar('name'),
|
|
'birthday' => $this->request->getVar('birthday'),
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
// Optional password update
|
|
$password = $this->request->getVar('password');
|
|
if (!empty($password)) {
|
|
$data['password_hash'] = md5($password);
|
|
}
|
|
|
|
// Handle profile picture update
|
|
$profileImage = $this->request->getFile('profile_picture');
|
|
|
|
if ($profileImage && $profileImage->isValid() && !$profileImage->hasMoved()) {
|
|
$uploadPath = $_SERVER['DOCUMENT_ROOT'] . '/backend/uploads/customers/';
|
|
if (!is_dir($uploadPath)) {
|
|
mkdir($uploadPath, 0755, true);
|
|
}
|
|
|
|
$imageName = time() . '_' . uniqid() . '.' . $profileImage->getClientExtension();
|
|
|
|
try {
|
|
$profileImage->move($uploadPath, $imageName);
|
|
|
|
// Delete old image if exists
|
|
if (!empty($customer['profile_picture'])) {
|
|
$oldImagePath = $_SERVER['DOCUMENT_ROOT'] . '/backend/' . $customer['profile_picture'];
|
|
if (file_exists($oldImagePath)) {
|
|
unlink($oldImagePath);
|
|
}
|
|
}
|
|
$baseUrl = 'https://icom.ipsgroup.com.my'; // or manually: 'https://icom.ipsgroup.com.my'
|
|
|
|
if (!empty($imageName)) {
|
|
$data['profile_picture'] = "uploads/customers/{$imageName}";
|
|
$data['profile_picture_url'] = $baseUrl . "/backend/uploads/customers/{$imageName}";
|
|
}
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
log_message('error', 'Profile picture update failed: ' . $e->getMessage());
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Profile picture update failed.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
if ($this->customerModel->update($id, $data)) {
|
|
unset($data['password_hash']);
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer updated successfully.',
|
|
'data' => $data
|
|
];
|
|
return $this->respond($response,200);
|
|
} else {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Failed to update customer.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
|
|
public function delete($id = null)
|
|
{
|
|
if (!$id) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$customer = $this->customerModel->find($id);
|
|
|
|
if (!$customer) {
|
|
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer with ID {$id} not found.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
|
|
}
|
|
|
|
// Soft delete (if `useSoftDeletes = true` is enabled in your model)
|
|
if ($this->customerModel->delete($id)) {
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => "Customer with ID {$id} deleted successfully.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 200);
|
|
} else {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Failed to delete customer.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
//customer address
|
|
|
|
public function createCustomerAddress($customerId = null)
|
|
{
|
|
helper(['form']);
|
|
|
|
if (!$customerId) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// Check if customer exists, is active, and not soft-deleted
|
|
$customer = $this->customerModel
|
|
->where('id', $customerId)
|
|
->where('status', 'active')
|
|
->where('deleted_at', null)
|
|
->first();
|
|
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer with ID {$customerId} not found or not active.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
// Load the model
|
|
$customerAddressModel = new CustomerAddress();
|
|
|
|
// Set all addresses for this customer to is_default = 0
|
|
$customerAddressModel
|
|
->where('customer_id', $customerId)
|
|
->set(['is_default' => 0])
|
|
->update();
|
|
|
|
$data = [
|
|
'customer_id' => $customerId,
|
|
'name' => $this->request->getVar('name'),
|
|
'phone' => $this->request->getVar('phone'),
|
|
'address' => $this->request->getVar('address'),
|
|
'unit' => $this->request->getVar('unit'),
|
|
'note' => $this->request->getVar('note'),
|
|
'is_default' => 1, // Always true for the newly added
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
if ($customerAddressModel->insert($data)) {
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer address created successfully.',
|
|
'data' => $data
|
|
];
|
|
return $this->respond($response, 201);
|
|
|
|
} else {
|
|
$response =[
|
|
'status' => 'error',
|
|
'message' => 'Failed to create customer address.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
public function customerAddressIndex()
|
|
{
|
|
$addresses = $this->customerAddressModel->findAll();
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer addresses retrieved successfully.',
|
|
'data' => $addresses
|
|
];
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function showCustomerAddress($id = null)
|
|
{
|
|
if ($id === null) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Address ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$address = $this->customerAddressModel->find($id);
|
|
|
|
if (!$address) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer address with ID {$id} not found.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
$response = [
|
|
'status' => 'sucess',
|
|
'message' => 'Customer address retrieved successfully.',
|
|
'data' => $address
|
|
];
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function showAddressesByCustomer($customerId = null)
|
|
{
|
|
if ($customerId === null) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$addresses = $this->customerAddressModel->where('customer_id', $customerId)->findAll();
|
|
|
|
if (empty($addresses)) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "No addresses found for customer ID {$customerId}.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
$response = [
|
|
'status' => 'sucess',
|
|
'message' => 'Customer addresses retrieved successfully.',
|
|
'data' => $addresses
|
|
];
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function updateCustomerAddress($id = null)
|
|
{
|
|
helper(['form']);
|
|
|
|
if ($id === null) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Address ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$address = $this->customerAddressModel->find($id);
|
|
if (!$address) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer address with ID {$id} not found.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
// Fetch customer and validate
|
|
$customerId = $address['customer_id'];
|
|
$customer = $this->customerModel
|
|
->where('id', $customerId)
|
|
->where('status', 'active')
|
|
->where('deleted_at', null)
|
|
->first();
|
|
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer not found or inactive.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
$validationRules = [
|
|
'name' => 'required',
|
|
'phone' => 'required',
|
|
'address' => 'required',
|
|
'unit' => 'permit_empty',
|
|
'note' => 'permit_empty',
|
|
'is_default'=> 'required|in_list[0,1]'
|
|
];
|
|
|
|
if (!$this->validate($validationRules)) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Validation failed.',
|
|
'data' => $this->validator->getErrors()
|
|
];
|
|
return $this->respond($response, 422);
|
|
|
|
}
|
|
|
|
$data = [
|
|
'name' => $this->request->getVar('name'),
|
|
'phone' => $this->request->getVar('phone'),
|
|
'address' => $this->request->getVar('address'),
|
|
'unit' => $this->request->getVar('unit'),
|
|
'note' => $this->request->getVar('note'),
|
|
'is_default' => $this->request->getVar('is_default'),
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
// Handle is_default logic
|
|
if ($data['is_default'] == 1) {
|
|
// Set all other addresses of this customer to 0
|
|
$this->customerAddressModel
|
|
->where('customer_id', $customerId)
|
|
->where('id !=', $id)
|
|
->set('is_default', 0)
|
|
->update();
|
|
}
|
|
|
|
if ($this->customerAddressModel->update($id, $data)) {
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer address updated successfully.',
|
|
'data' => $data
|
|
];
|
|
return $this->respond($response, 200);
|
|
} else {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Failed to update customer address.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
public function deleteCustomerAddress($id = null)
|
|
{
|
|
if (!$id) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer address ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$address = $this->customerAddressModel->find($id);
|
|
|
|
if (!$address) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer address with ID {$id} not found.",
|
|
'data' => null
|
|
];
|
|
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
// Delete the address
|
|
if ($this->customerAddressModel->delete($id)) {
|
|
// If deleted address was default, assign default to another one
|
|
if ($address['is_default'] == 1) {
|
|
$anotherAddress = $this->customerAddressModel
|
|
->where('customer_id', $address['customer_id'])
|
|
->where('id !=', $id)
|
|
->orderBy('created_at', 'DESC')
|
|
->first();
|
|
|
|
if ($anotherAddress) {
|
|
$this->customerAddressModel->update($anotherAddress['id'], ['is_default' => 1]);
|
|
}
|
|
}
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => "Customer address with ID {$id} deleted successfully.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 200);
|
|
}
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Failed to delete customer address.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
|
|
public function setDefaultCustomerAddress($id = null) // $id from URL
|
|
{
|
|
$customerId = $this->request->getVar('customer_id');
|
|
|
|
// Validate customer ID input
|
|
if (!$customerId) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$customer = $this->customerModel
|
|
->where('id', $customerId)
|
|
->where('status', 'active')
|
|
->where('deleted_at', null)
|
|
->first();
|
|
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer not found or inactive.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
// 2. Validate address exists AND belongs to the customer
|
|
$address = $this->customerAddressModel
|
|
->where('id', $id)
|
|
->where('customer_id', $customerId) // <- this ensures correct linkage
|
|
->first();
|
|
|
|
if (!$address) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Address not found or does not belong to the customer.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
// 3. Set all customer's addresses to is_default = 0
|
|
$this->customerAddressModel
|
|
->where('customer_id', $customerId)
|
|
->set(['is_default' => 0])
|
|
->update();
|
|
|
|
// 4. Set selected address to is_default = 1
|
|
$this->customerAddressModel->update($id, ['is_default' => 1]);
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => "Address ID {$id} is now the default for customer ID {$customerId}.",
|
|
'data' => [
|
|
'address_id' => $id,
|
|
'customer_id' => $customerId
|
|
]
|
|
];
|
|
return $this->respond($response,200);
|
|
}
|
|
|
|
|
|
//Customer Wallet Part
|
|
|
|
public function createCustomerWallet()
|
|
{
|
|
$data = [
|
|
'customer_id' => $this->request->getVar('customer_id'),
|
|
'related_type' => $this->request->getVar('related_type'),
|
|
'related_id' => $this->request->getVar('related_id'),
|
|
'action' => $this->request->getVar('action'),
|
|
'in' => $this->request->getVar('in'),
|
|
'out' => $this->request->getVar('out'),
|
|
'remark' => $this->request->getVar('remark'),
|
|
];
|
|
|
|
if (empty($data['customer_id']) || empty($data['action'])) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID and action are required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// Normalize and validate action
|
|
$action = strtolower(trim($data['action']));
|
|
if (!in_array($action, ['in', 'out'])) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'The action must be either "in" or "out".',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// Ensure only one of 'in' or 'out' is provided
|
|
$in = $action === 'in' ? floatval($data['in'] ?? 0) : 0;
|
|
$out = $action === 'out' ? floatval($data['out'] ?? 0) : 0;
|
|
|
|
if (($in <= 0 && $out <= 0) || ($in > 0 && $out > 0)) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Provide a valid positive amount only for the action specified.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// Check customer existence and active status
|
|
$customer = $this->customerModel
|
|
->where('id', $data['customer_id'])
|
|
->where('status', 'active')
|
|
->where('deleted_at', null)
|
|
->first();
|
|
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer not found or inactive.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
} // Fixed: Added closing brace for the if statement
|
|
|
|
// Get current balance and calculate new balance
|
|
$currentBalance = floatval($customer['customer_wallet']);
|
|
$newBalance = $currentBalance + $in - $out;
|
|
|
|
// Prepare insert data
|
|
$walletData = [
|
|
'customer_id' => $data['customer_id'],
|
|
'related_id' => $data['related_id'] ?? null,
|
|
'related_type' => $data['related_type'] ?? null,
|
|
'action' => $action,
|
|
'current' => $currentBalance,
|
|
'in' => $in,
|
|
'out' => $out,
|
|
'balance' => $newBalance,
|
|
'remark' => $data['remark'] ?? null,
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
// Save to customer_wallets and Update customer balance
|
|
$this->customerWalletModel->insert($walletData);
|
|
$this->customerModel->update($data['customer_id'], ['customer_wallet' => $newBalance]);
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer wallet transaction created successfully.',
|
|
'data' => $walletData,
|
|
];
|
|
|
|
return $this->respond($response, 201);
|
|
}
|
|
|
|
public function getCustomerWalletHistory($customerId)
|
|
{
|
|
// Check if customer exists
|
|
$customer = $this->customerModel->find($customerId);
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer not found.',
|
|
'data' => null
|
|
];
|
|
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
$dateFrom = $this->request->getGet('date_from'); // expected format: Y-m-d
|
|
$dateTo = $this->request->getGet('date_to');
|
|
|
|
|
|
// Base query
|
|
$builder = $this->customerWalletModel
|
|
->where('customer_id', $customerId)
|
|
->orderBy('created_at', 'desc');
|
|
|
|
// Optional filters
|
|
if ($dateFrom) {
|
|
$builder->where('DATE(created_at) >=', $dateFrom);
|
|
}
|
|
if ($dateTo) {
|
|
$builder->where('DATE(created_at) <=', $dateTo);
|
|
}
|
|
|
|
// All transactions
|
|
$transactions = $builder->findAll();
|
|
|
|
// If no transactions found
|
|
if (empty($transactions)) {
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'No wallet history found for this customer.',
|
|
'data' => [
|
|
'all' => [],
|
|
'credit' => []
|
|
]
|
|
];
|
|
return $this->respond($response,200);
|
|
}
|
|
|
|
// Credit-only transactions
|
|
$creditTransactions = array_filter($transactions, function ($t) {
|
|
return strtolower($t['action']) === 'in';
|
|
});
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer wallet history retrieved successfully.',
|
|
'data' => [
|
|
'all' => $transactions,
|
|
'credit' => array_values($creditTransactions)
|
|
]
|
|
];
|
|
return $this->respond($response,200);
|
|
}
|
|
|
|
|
|
// Customer Point Part
|
|
|
|
public function createCustomerPoint()
|
|
{
|
|
$data = [
|
|
'customer_id' => $this->request->getVar('customer_id'),
|
|
'related_type' => $this->request->getVar('related_type'),
|
|
'related_id' => $this->request->getVar('related_id'),
|
|
'action' => $this->request->getVar('action'),
|
|
'in' => $this->request->getVar('in'),
|
|
'out' => $this->request->getVar('out'),
|
|
'remark' => $this->request->getVar('remark'),
|
|
];
|
|
|
|
// Check required fields
|
|
if (!isset($data['customer_id'], $data['action'])) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'customer_id and action are required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// Normalize and validate action
|
|
$action = strtolower(trim($data['action']));
|
|
if (!in_array($action, ['increase', 'decrease'])) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'The action must be either "increase" or "decrease".',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// Use 'in' and 'out' fields, based on action type
|
|
$in = $action === 'increase' ? floatval($data['in'] ?? 0) : 0;
|
|
$out = $action === 'decrease' ? floatval($data['out'] ?? 0) : 0;
|
|
|
|
// Validation to ensure only one of them is provided and positive
|
|
if (($in <= 0 && $out <= 0) || ($in > 0 && $out > 0)) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Provide a valid positive amount only for the action specified.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
// Check if customer exists and is active
|
|
$customer = $this->customerModel
|
|
->where('id', $data['customer_id'])
|
|
->where('status', 'active')
|
|
->where('deleted_at', null)
|
|
->first();
|
|
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer not found or inactive.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
// Current balance and new balance calculation
|
|
$currentPoint = floatval($customer['customer_point']);
|
|
$newPoint = $currentPoint + $in - $out;
|
|
|
|
// Prepare the data to insert
|
|
$pointData = [
|
|
'customer_id' => $data['customer_id'],
|
|
'related_id' => $data['related_id'] ?? null,
|
|
'related_type' => $data['related_type'] ?? null,
|
|
'action' => $action,
|
|
'current' => $currentPoint,
|
|
'in' => $in,
|
|
'out' => $out,
|
|
'balance' => $newPoint,
|
|
'remark' => $data['remark'] ?? null,
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
// Save to customer_points table
|
|
$this->customerPointModel->insert($pointData);
|
|
|
|
// Update customer's overall point balance
|
|
$this->customerModel->update($data['customer_id'], ['customer_point' => $newPoint]);
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer point transaction created successfully.',
|
|
'data' => $pointData
|
|
];
|
|
return $this->respond($response, 201);
|
|
}
|
|
|
|
public function getCustomerPointHistory($customerId)
|
|
{
|
|
// Check if customer exists
|
|
$customer = $this->customerModel->find($customerId);
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer not found.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
$dateFrom = $this->request->getGet('date_from'); // optional
|
|
$dateTo = $this->request->getGet('date_to'); // optional
|
|
|
|
// Build query
|
|
$builder = $this->customerPointModel
|
|
->where('customer_id', $customerId)
|
|
->orderBy('created_at', 'desc');
|
|
|
|
if ($dateFrom) {
|
|
$builder->where('DATE(created_at) >=', $dateFrom);
|
|
}
|
|
if ($dateTo) {
|
|
$builder->where('DATE(created_at) <=', $dateTo);
|
|
}
|
|
|
|
$transactions = $builder->findAll();
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => empty($transactions)
|
|
? 'No data available in the table.'
|
|
: 'Customer point history retrieved successfully.',
|
|
'data' => $transactions
|
|
];
|
|
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
|
|
public function checkin($customerId)
|
|
{
|
|
$today = date('Y-m-d');
|
|
$yesterday = date('Y-m-d', strtotime('-1 day'));
|
|
|
|
//hardcode the date for testing purpose.
|
|
// $today = '2025-07-09';
|
|
// $yesterday = '2025-07-08';
|
|
|
|
// Validate input
|
|
if (!$customerId) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 400);
|
|
}
|
|
|
|
//Check if customer exists
|
|
|
|
$customer = $this->customerModel->find($customerId);
|
|
if (!$customer) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer not found.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 404);
|
|
}
|
|
|
|
//Prevent multiple check-ins on same day
|
|
$alreadyChecked = $this->logCheckinModel
|
|
->where('customer_id', $customerId)
|
|
->where('checkin_date', $today)
|
|
->first();
|
|
|
|
if ($alreadyChecked) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Already checked in today.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 409);
|
|
}
|
|
|
|
//Get or initialize streak
|
|
$streak = $this->checkinStreakModel->where('customer_id', $customerId)->first();
|
|
|
|
if ($streak) {
|
|
if ($streak['last_checkin_date'] === $yesterday) {
|
|
$currentStreak = $streak['current_streak'] + 1;
|
|
} else {
|
|
$currentStreak = 1; // missed a day, reset streak
|
|
}
|
|
|
|
$totalCheckin = $streak['total_checkin'] + 1;
|
|
|
|
$this->checkinStreakModel->update($streak['id'], [
|
|
'current_streak' => $currentStreak,
|
|
'last_checkin_date' => $today,
|
|
'total_checkin' => $totalCheckin,
|
|
]);
|
|
} else {
|
|
// first time ever
|
|
$currentStreak = 1;
|
|
$totalCheckin = 1;
|
|
|
|
$this->checkinStreakModel->insert([
|
|
'customer_id' => $customerId,
|
|
'current_streak' => $currentStreak,
|
|
'last_checkin_date' => $today,
|
|
'total_checkin' => $totalCheckin,
|
|
]);
|
|
}
|
|
|
|
//Log the check-in
|
|
$this->logCheckinModel->insert([
|
|
'customer_id' => $customerId,
|
|
'checkin_date' => $today,
|
|
'streak_count' => $currentStreak,
|
|
]);
|
|
|
|
//Check for reward rule
|
|
$rewardGiven = false;
|
|
$rewardPoint = 0;
|
|
|
|
$rule = $this->checkinRuleModel
|
|
->where('checkin_count', $currentStreak)
|
|
->first();
|
|
|
|
if ($rule) {
|
|
$alreadyRewarded = $this->customerPointModel
|
|
->where('customer_id', $customerId)
|
|
->where('remark', "Reward for {$currentStreak}-day check-in streak on {$today}.")
|
|
->first();
|
|
|
|
if (!$alreadyRewarded) {
|
|
$latestPoint = $this->customerPointModel
|
|
->where('customer_id', $customerId)
|
|
->orderBy('id', 'DESC')
|
|
->first();
|
|
|
|
$previousBalance = $latestPoint ? $latestPoint['balance'] : 0;
|
|
$rewardPoint = (float) $rule['reward_point'];
|
|
$newBalance = $previousBalance + $rewardPoint;
|
|
|
|
$this->customerPointModel->insert([
|
|
'customer_id' => $customerId,
|
|
'related_id' => '2',
|
|
'related_type' => 'checkin_reward',
|
|
'action' => 'increase',
|
|
'current' => $previousBalance,
|
|
'in' => $rewardPoint,
|
|
'out' => 0,
|
|
'balance' => $newBalance,
|
|
'remark' => "Reward for {$currentStreak}-day check-in streak on {$today}.",
|
|
]);
|
|
|
|
$this->customerModel->update($customerId, ['customer_point' => $newBalance]);
|
|
$rewardGiven = true;
|
|
}
|
|
}
|
|
|
|
$updatedStreak = $this->checkinStreakModel
|
|
->where('customer_id', $customerId)
|
|
->first();
|
|
|
|
$latestPoint = $this->customerPointModel
|
|
->where('customer_id', $customerId)
|
|
->orderBy('id', 'DESC')
|
|
->first();
|
|
|
|
$data = [
|
|
'reward_given' => $rewardGiven,
|
|
'reward_point' => $rewardPoint,
|
|
'customer_point' => $latestPoint['balance'],
|
|
'current_streak' => $updatedStreak['current_streak'],
|
|
];
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Check-in successful!',
|
|
'data' => $data,
|
|
];
|
|
return $this->respond($response, 200);
|
|
|
|
}
|
|
|
|
private function handleStudentCardGraduation($studentCard)
|
|
{
|
|
if (!empty($studentCard['graduate_date']) && !empty($studentCard['customer_id'])) {
|
|
$today = date('Y-m-d');
|
|
if ($studentCard['graduate_date'] <= $today) {
|
|
$this->studentCardModel->delete($studentCard['id']);
|
|
$this->customerModel->update($studentCard['customer_id'], [
|
|
'customer_type' => 'Regular Customers',
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function studentCardIndex()
|
|
{
|
|
// Assuming your StudentCard model is loaded as $this->studentCardModel
|
|
$studentCards = $this->studentCardModel
|
|
->select('*') // Select all columns from student_cards
|
|
->findAll();
|
|
foreach ($studentCards as $studentCard) {
|
|
$this->handleStudentCardGraduation($studentCard);
|
|
}
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Student card list retrieved successfully.',
|
|
'data' => $studentCards
|
|
];
|
|
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function studentCardShow($student_card_id = null)
|
|
{
|
|
if ($student_card_id === null) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Student card ID is required.',
|
|
'data' => null
|
|
], 422);
|
|
}
|
|
|
|
$studentCard = $this->studentCardModel->find($student_card_id);
|
|
|
|
if (!$studentCard) {
|
|
$this->handleStudentCardGraduation($studentCard);
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => "Student card with ID {$student_card_id} not found.",
|
|
'data' => null
|
|
], 404);
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Student card retrieved successfully.',
|
|
'data' => $studentCard
|
|
], 200);
|
|
}
|
|
|
|
public function studentCardUpdateStatus($student_card_id = null)
|
|
{
|
|
helper(['form']);
|
|
|
|
if ($student_card_id === null) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Student card ID is required.',
|
|
'data' => null
|
|
], 422);
|
|
}
|
|
|
|
// Validate status input
|
|
$status = $this->request->getVar('status');
|
|
$allowedStatuses = ['approved', 'rejected'];
|
|
|
|
if (!in_array($status, $allowedStatuses)) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Invalid status value. Allowed values are: approved, rejected.',
|
|
'data' => null
|
|
], 422);
|
|
}
|
|
|
|
// Check if the student card exists
|
|
$studentCard = $this->studentCardModel->find($student_card_id);
|
|
if (!$studentCard) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => "Student card with ID {$student_card_id} not found.",
|
|
'data' => null
|
|
], 404);
|
|
}
|
|
|
|
$data = [
|
|
'status' => $status,
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
if ($this->studentCardModel->update($student_card_id, $data)) {
|
|
|
|
// If approved, also update the customer_type in the Customer table
|
|
if ($status === 'approved' && !empty($studentCard['customer_id'])) {
|
|
|
|
//search membership tier with tier_name = 'Student'
|
|
$membershipTier = $this->membershipTierModel->where('name', 'Student')->first();
|
|
if(!$membershipTier) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Student membership tier not found.',
|
|
'data' => null
|
|
], 404);
|
|
}else{
|
|
$this->customerModel->update($studentCard['customer_id'], [
|
|
'customer_type' => 'Student',
|
|
'customer_tier_id' => $membershipTier['id'],
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => "Student card status updated to '{$status}'.",
|
|
'data' => $data
|
|
], 200);
|
|
} else {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Failed to update student card status.',
|
|
'data' => null
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|