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

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]);
}
}