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