1126 lines
39 KiB
PHP
1126 lines
39 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Frontend;
|
|
|
|
use App\Models\Customer;
|
|
use App\Models\StudentCard;
|
|
use App\Models\CustomerAddress;
|
|
use App\Models\SettingCustomerType;
|
|
use App\Models\SettingMembershipTier;
|
|
use App\Models\CustomerPoint;
|
|
use App\Models\CustomerWallet;
|
|
use App\Models\CheckinStreak;
|
|
use App\Models\LogCustomerCheckin;
|
|
use App\Models\SettingCheckinRule;
|
|
use CodeIgniter\HTTP\ResponseInterface;
|
|
use CodeIgniter\RESTful\ResourceController;
|
|
|
|
class CustomerController extends ResourceController
|
|
{
|
|
protected $customerAddressModel;
|
|
protected $customerModel;
|
|
protected $studentCardModel;
|
|
protected $customerTypeModel;
|
|
protected $customerPointModel;
|
|
protected $customerWalletModel;
|
|
protected $membershipTierModel;
|
|
protected $checkinStreakModel;
|
|
protected $logCheckinModel;
|
|
protected $checkinRuleModel;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->customerAddressModel = new CustomerAddress();
|
|
$this->customerModel = new Customer();
|
|
$this->studentCardModel = new StudentCard();
|
|
$this->customerTypeModel = new SettingCustomerType();
|
|
$this->checkinStreakModel = new CheckinStreak();
|
|
$this->logCheckinModel = new LogCustomerCheckin();
|
|
$this->checkinRuleModel = new SettingCheckinRule();
|
|
$this->customerPointModel = new CustomerPoint();
|
|
$this->customerWalletModel = new CustomerWallet();
|
|
$this->membershipTierModel = new SettingMembershipTier();
|
|
}
|
|
|
|
public function updateProfile($id = null){
|
|
helper(['form']);
|
|
|
|
// Check customer exists
|
|
$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 = [
|
|
'name' => 'required',
|
|
'email' => "required|valid_email|is_unique[customers.email,id,{$id}]",
|
|
'birthday' => 'required|valid_date[Y-m-d]',
|
|
'password_hash' => 'permit_empty|min_length[6]',
|
|
'customer_referral_code' => 'permit_empty|regex_match[/^\d{6}$/]'
|
|
];
|
|
|
|
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'),
|
|
'email' => $this->request->getVar('email'),
|
|
'birthday' => $this->request->getVar('birthday'),
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
$passwordHash = $this->request->getVar('password_hash');
|
|
if (!empty($passwordHash)) {
|
|
$data['password_hash'] = md5($passwordHash);
|
|
}
|
|
|
|
if (empty($customer['customer_type'])) {
|
|
$defaultTypeName = 'Regular Customer';
|
|
$customerType = $this->customerTypeModel
|
|
->where('name', $defaultTypeName)
|
|
->first();
|
|
|
|
if (!$customerType) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Default customer type '{$defaultTypeName}' not found.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
|
|
$data['customer_type'] = $defaultTypeName;
|
|
}
|
|
|
|
$referralCode = $this->request->getVar('customer_referral_code');
|
|
if (!empty($referralCode)) {
|
|
|
|
// Look up referral customer by code
|
|
$referrer = $this->customerModel
|
|
->where('customer_referral_code', $referralCode)
|
|
->first();
|
|
|
|
|
|
if ($referrer) {
|
|
$data['customer_referral_id'] = $referrer['id'];
|
|
} else {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Invalid referral code provided.",
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
}
|
|
|
|
// Handle profile picture upload
|
|
$profileImage = $this->request->getFile('profile_picture');
|
|
if ($profileImage && $profileImage->isValid() && !$profileImage->hasMoved()) {
|
|
$originalPath = $_SERVER['DOCUMENT_ROOT'] . '/backend/uploads/customers';
|
|
$compressedPath = $_SERVER['DOCUMENT_ROOT'] . '/backend/uploads/customers_compressed/';
|
|
$baseUrl = 'https://icom.ipsgroup.com.my';
|
|
|
|
try {
|
|
$result = save_image_with_compression($profileImage, $originalPath, $compressedPath, 900, 80);
|
|
|
|
// Remove old images if they exist
|
|
if (!empty($customer['profile_picture'])) {
|
|
$oldOriginal = $_SERVER['DOCUMENT_ROOT'] . '/backend/' . $customer['profile_picture'];
|
|
if (file_exists($oldOriginal)) unlink($oldOriginal);
|
|
|
|
// Also remove old compressed version if it exists (adjust field name as needed)
|
|
if (!empty($customer['profile_picture_compressed'])) {
|
|
$oldCompressed = $_SERVER['DOCUMENT_ROOT'] . '/backend/' . $customer['profile_picture_compressed'];
|
|
if (file_exists($oldCompressed)) unlink($oldCompressed);
|
|
}
|
|
}
|
|
|
|
$relativeOriginal = str_replace($_SERVER['DOCUMENT_ROOT'].'/backend/', '', $result['original']);
|
|
$relativeCompressed = str_replace($_SERVER['DOCUMENT_ROOT'].'/backend/', '', $result['compressed']);
|
|
|
|
$data['profile_picture'] = $relativeOriginal;
|
|
$data['profile_picture_compressed'] = $relativeCompressed;
|
|
$data['profile_picture_url'] = $baseUrl . "/backend/" . $relativeOriginal;
|
|
$data['profile_picture_compressed_url'] = $baseUrl . "/backend/" . $relativeCompressed;
|
|
|
|
} catch (\Exception $e) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Failed to upload profile picture: ' . $e->getMessage(),
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
if ($this->customerModel->update($id, $data)) {
|
|
$updatedCustomer = $this->customerModel->find($id);
|
|
unset($updatedCustomer['password_hash']);
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer profile updated successfully.',
|
|
'data' => $updatedCustomer
|
|
];
|
|
return $this->respond($response, 200);
|
|
} else {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Failed to update profile.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
public function getCustomerProfile($id= null){
|
|
|
|
if ($id === null) {
|
|
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
];
|
|
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$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,
|
|
customers.customer_tier_id,
|
|
setting_membership_tiers.name AS customer_tier_name,
|
|
setting_membership_tiers.min_points AS customer_tier_min_points,
|
|
setting_membership_tiers.color AS customer_tier_color,
|
|
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);
|
|
}
|
|
// Build full URL for profile picture if it exists
|
|
if (!empty($customer['profile_picture'])) {
|
|
$baseUrl = 'https://icom.ipsgroup.com.my'; // Change if dynamic base URL is preferred
|
|
$customer['profile_picture_url'] = $baseUrl . '/backend/' . $customer['profile_picture'];
|
|
} else {
|
|
$customer['profile_picture_url'] = null;
|
|
}
|
|
|
|
//get customer cumulative point
|
|
$customer_cumulative_point = $this->customerPointModel->where('customer_id', $id)->where('deleted_at', null)->selectSum('in')->get()->getRow()->in ?? 0;
|
|
// echo($customer_cumulative_point);exit;
|
|
//check if customer tier is null
|
|
if($customer['customer_tier_id'] == null || $customer['customer_tier_id'] == 0 || $customer['customer_tier_min_points'] == null){
|
|
// Customer has no tier, get the first available tier
|
|
$next_tier = $this->membershipTierModel->where('deleted_at', null)->orderBy('min_points', 'ASC')->first();
|
|
}else{
|
|
// Customer has a tier, get the next higher tier
|
|
$next_tier = $this->membershipTierModel->where('deleted_at', null)
|
|
->where('min_points >', $customer['customer_tier_min_points'])
|
|
->orderBy('min_points', 'ASC')
|
|
->first();
|
|
}
|
|
// print_r($customer);exit;
|
|
if($next_tier){
|
|
$customer['next_tier'] = $next_tier['name'];
|
|
$customer['next_tier_min_points'] = $next_tier['min_points'];
|
|
$customer['point_needed_for_next_tier'] = number_format($next_tier['min_points'] - $customer_cumulative_point, 2);
|
|
}else{
|
|
$customer['next_tier'] = null;
|
|
$customer['next_tier_min_points'] = null;
|
|
$customer['point_needed_for_next_tier'] = null;
|
|
}
|
|
$customer['current_streak'] = 0;
|
|
$customer['checkin_today'] = false;
|
|
|
|
$streak = $this->checkinStreakModel->where('customer_id', $id)->orderBy('created_at', 'DESC')->first();
|
|
$yesterday = date('Y-m-d', strtotime('-1 day'));
|
|
$today = date('Y-m-d');
|
|
if ($streak) {
|
|
if ($streak['last_checkin_date'] === $yesterday) {
|
|
$customer['current_streak'] = $streak['current_streak'];
|
|
}else if ($streak['last_checkin_date'] === $today) {
|
|
$customer['current_streak'] = $streak['current_streak'];
|
|
$customer['checkin_today'] = true;
|
|
}
|
|
}
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Customer retrieved successfully.',
|
|
'data' => $customer
|
|
];
|
|
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function getCustomerAddresses($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' => 'success',
|
|
'message' => 'Customer addresses retrieved successfully.',
|
|
'data' => $addresses
|
|
];
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function getCustomerPoint($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 retrieved successfully.',
|
|
'data' => [
|
|
'current_point' => $customer['customer_point'], // from customer table
|
|
'transactions' => $transactions
|
|
]
|
|
];
|
|
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function getCustomerWallet($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->customerWalletModel
|
|
->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 wallet retrieved successfully.',
|
|
'data' => [
|
|
'current_wallet' => $customer['customer_wallet'], // from customer table
|
|
'transactions' => $transactions
|
|
]
|
|
];
|
|
|
|
return $this->respond($response, 200);
|
|
}
|
|
|
|
public function createCustomerAddress()
|
|
{
|
|
helper(['form']);
|
|
|
|
$validationRules = [
|
|
'name' => 'required',
|
|
'phone' => 'required',
|
|
'address' => 'required',
|
|
'unit' => 'permit_empty',
|
|
'note' => 'permit_empty',
|
|
'longitude' => 'required',
|
|
'latitude' => 'required'
|
|
];
|
|
|
|
if (!$this->validate($validationRules)) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Validation failed.',
|
|
'data' => $this->validator->getErrors()
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$customerId = $this->request->getVar('customer_id');
|
|
|
|
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'),
|
|
'longitude' => $this->request->getVar('longitude'),
|
|
'latitude' => $this->request->getVar('latitude'),
|
|
'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 showCustomerAddress($customerId = null)
|
|
{
|
|
if ($customerId === null) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$address = $this->customerAddressModel->where('customer_id', $customerId)->findAll();
|
|
|
|
if (!$address) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => "Customer address with ID {$customerId} 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 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',
|
|
'longitude' => 'required',
|
|
'latitude' => 'required',
|
|
'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'),
|
|
'longitude' => $this->request->getVar('longitude'),
|
|
'latitude' => $this->request->getVar('latitude'),
|
|
'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);
|
|
}
|
|
|
|
public function getCustomerAddressDetail($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 submitStudentCard($id = null)
|
|
{
|
|
|
|
if (empty($id)) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
], 422);
|
|
}
|
|
|
|
helper(['form']);
|
|
helper(['image']);
|
|
|
|
$validationRules = [
|
|
'studentName' => 'required',
|
|
'studentId' => 'required',
|
|
'institution' => 'required',
|
|
'graduationDate' => 'required',
|
|
];
|
|
|
|
if (!$this->validate($validationRules)) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Validation failed.',
|
|
'data' => $this->validator->getErrors()
|
|
], 422);
|
|
}
|
|
|
|
// Check if customer exists
|
|
$customer = $this->customerModel
|
|
->where('id', $id)
|
|
->where('status', 'active')
|
|
->where('deleted_at', null)
|
|
->first();
|
|
|
|
if (!$customer) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => "Customer with ID {$id} not found or not active.",
|
|
'data' => null
|
|
], 404);
|
|
}
|
|
|
|
// Prepare data for insertion
|
|
$data = [
|
|
'customer_id' => $id,
|
|
'name' => $this->request->getVar('studentName'),
|
|
'student_id' => $this->request->getVar('studentId'),
|
|
'institution' => $this->request->getVar('institution'),
|
|
'graduate_date' => $this->request->getVar('graduationDate'),
|
|
'status' => 'pending',
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
// Handle uploaded image
|
|
$profilePicture = $this->request->getFile('profile_picture');
|
|
|
|
if(!$profilePicture) {
|
|
$response = [
|
|
'status' => 'error',
|
|
'message' => 'Student ID picture is required.',
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 422);
|
|
}
|
|
|
|
$imagePath = null;
|
|
$compressedPath = null;
|
|
|
|
if ($profilePicture && $profilePicture->isValid() && !$profilePicture->hasMoved()) {
|
|
$originalPath = $_SERVER['DOCUMENT_ROOT'] . '/backend/uploads/students';
|
|
$compressedDir = $_SERVER['DOCUMENT_ROOT'] . '/backend/uploads/students_compressed';
|
|
|
|
try {
|
|
$result = save_image_with_compression($profilePicture, $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' => 'Student Id picture upload failed: ' . $e->getMessage(),
|
|
'data' => null
|
|
];
|
|
return $this->respond($response, 500);
|
|
}
|
|
}
|
|
|
|
if ($imagePath) {
|
|
$data['student_id_image'] = $imagePath;
|
|
}
|
|
|
|
if ($this->studentCardModel->insert($data)) {
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Student Card submitted successfully.',
|
|
'data' => $data
|
|
], 201);
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Failed to submit student card submission.',
|
|
'data' => null
|
|
], 500);
|
|
}
|
|
|
|
public function getStudentCardStatus($customer_id = null)
|
|
{
|
|
if ($customer_id === null) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
], 422);
|
|
}
|
|
|
|
// Get the latest student card by created_at for this customer
|
|
$studentCard = $this->studentCardModel
|
|
->where('customer_id', $customer_id)
|
|
->orderBy('created_at', 'DESC')
|
|
->first();
|
|
|
|
if (!$studentCard) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => "Student card for Customer ID {$customer_id} not found.",
|
|
'data' => null
|
|
], 404);
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Latest student card retrieved successfully.',
|
|
'data' => $studentCard
|
|
], 200);
|
|
}
|
|
|
|
public function customerNextTierInformation($customer_id = null)
|
|
{
|
|
if ($customer_id === null) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Customer ID is required.',
|
|
'data' => null
|
|
], 422);
|
|
}
|
|
|
|
// Query customer points and next tier info
|
|
$result = $this->customerModel
|
|
->select('
|
|
CAST(IFNULL(cp.acc_p, 0) AS UNSIGNED) AS accumulate_points,
|
|
CAST(next_tier.min_points AS UNSIGNED) AS next_tier_min_points,
|
|
next_tier.name AS next_tier_name
|
|
')
|
|
->join(
|
|
'(SELECT customer_id, SUM(`in`) AS acc_p
|
|
FROM customer_points
|
|
GROUP BY customer_id) cp',
|
|
'cp.customer_id = customers.id',
|
|
'left'
|
|
)
|
|
->join(
|
|
'(SELECT t1.id, t1.name, t1.min_points
|
|
FROM setting_membership_tiers t1) next_tier',
|
|
'next_tier.min_points = (
|
|
SELECT MIN(t2.min_points)
|
|
FROM setting_membership_tiers t2
|
|
WHERE t2.min_points > IFNULL(cp.acc_p, 0)
|
|
)',
|
|
'left',
|
|
false
|
|
)
|
|
->where('customers.id', $customer_id)
|
|
->get()
|
|
->getRow();
|
|
|
|
if (!$result) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => "Customer ID {$customer_id} not found.",
|
|
'data' => null
|
|
], 404);
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Customer next tier information retrieved successfully.',
|
|
'data' => $result
|
|
], 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);
|
|
|
|
}
|
|
|
|
public function referralList($customer_id)
|
|
{
|
|
if(empty($customer_id)){
|
|
return $this->respond(['status' => 400, 'message' => 'Customer ID is required']);
|
|
}
|
|
|
|
$customer = $this->customerModel->select('name, created_at')->where('customer_referral_id', $customer_id)->findAll();
|
|
|
|
return $this->respond(['status' => 200, 'data' => $customer]);
|
|
}
|
|
|
|
}
|