mailchimp_lib = new Mailchimp_lib(); $this->customer_rewards = model(Customer_rewards::class); $this->customer = model(Customer::class); $this->tax_code = model(Tax_code::class); $this->config = config(OSPOS::class)->settings; $encrypter = Services::encrypter(); if (!empty($this->config['mailchimp_list_id'])) { $this->_list_id = $encrypter->decrypt($this->config['mailchimp_list_id']); } else { $this->_list_id = ''; } } /** * @return void */ public function getIndex(): void { $data['table_headers'] = get_customer_manage_table_headers(); echo view('people/manage', $data); } /** * Gets one row for a customer manage table. This is called using AJAX to update one row. */ public function getRow(int $row_id): void { $person = $this->customer->get_info($row_id); // Retrieve the total amount the customer spent so far together with min, max and average values $stats = $this->customer->get_stats($person->person_id); // TODO: This and the next 11 lines are duplicated in search(). Extract a method. if (empty($stats)) { // Create object with empty properties. $stats = new stdClass(); $stats->total = 0; $stats->min = 0; $stats->max = 0; $stats->average = 0; $stats->avg_discount = 0; $stats->quantity = 0; } $data_row = get_customer_data_row($person, $stats); echo json_encode($data_row); } /** * Returns customer table data rows. This will be called with AJAX. * * @return void */ public function getSearch(): void { $search = $this->request->getGet('search'); $limit = $this->request->getGet('limit', FILTER_SANITIZE_NUMBER_INT); $offset = $this->request->getGet('offset', FILTER_SANITIZE_NUMBER_INT); $sort = $this->sanitizeSortColumn(customer_headers(), $this->request->getGet('sort', FILTER_SANITIZE_FULL_SPECIAL_CHARS), 'people.person_id'); $order = $this->request->getGet('order', FILTER_SANITIZE_FULL_SPECIAL_CHARS); $customers = $this->customer->search($search, $limit, $offset, $sort, $order); $total_rows = $this->customer->get_found_rows($search); $data_rows = []; foreach ($customers->getResult() as $person) { // Retrieve the total amount the customer spent so far together with min, max and average values $stats = $this->customer->get_stats($person->person_id); // TODO: duplicated... see above if (empty($stats)) { // Create object with empty properties. $stats = new stdClass(); $stats->total = 0; $stats->min = 0; $stats->max = 0; $stats->average = 0; $stats->avg_discount = 0; $stats->quantity = 0; } $data_rows[] = get_customer_data_row($person, $stats); } echo json_encode(['total' => $total_rows, 'rows' => $data_rows]); } /** * Gives search suggestions based on what is being searched for */ public function getSuggest(): void { $search = $this->request->getGet('term'); $suggestions = $this->customer->get_search_suggestions($search); echo json_encode($suggestions); } /** * @return void */ public function suggest_search(): void { $search = $this->request->getGet('term'); $suggestions = $this->customer->get_search_suggestions($search, 25, false); echo json_encode($suggestions); } /** * Loads the customer edit form */ public function getView(int $customer_id = NEW_ENTRY): void { // Set default values if ($customer_id == null) $customer_id = NEW_ENTRY; $info = $this->customer->get_info($customer_id); foreach (get_object_vars($info) as $property => $value) { $info->$property = $value; } $data['person_info'] = $info; if (empty($info->person_id) || empty($info->date) || empty($info->employee_id)) { $data['person_info']->date = date('Y-m-d H:i:s'); $data['person_info']->employee_id = $this->employee->get_logged_in_employee_info()->person_id; } $employee_info = $this->employee->get_info($info->employee_id); $data['employee'] = $employee_info->first_name . ' ' . $employee_info->last_name; $tax_code_info = $this->tax_code->get_info($info->sales_tax_code_id); if ($tax_code_info->tax_code != null) { $data['sales_tax_code_label'] = $tax_code_info->tax_code . ' ' . $tax_code_info->tax_code_name; } else { $data['sales_tax_code_label'] = ''; } $packages = ['' => lang('Items.none')]; foreach ($this->customer_rewards->get_all()->getResultArray() as $row) { $packages[$row['package_id']] = $row['package_name']; } $data['packages'] = $packages; $data['selected_package'] = $info->package_id; $data['use_destination_based_tax'] = $this->config['use_destination_based_tax']; // Retrieve the total amount the customer spent so far together with min, max and average values $stats = $this->customer->get_stats($customer_id); if (!empty($stats)) { foreach (get_object_vars($stats) as $property => $value) { $info->$property = $value; } $data['stats'] = $stats; } // Retrieve the info from Mailchimp only if there is an email address assigned if (!empty($info->email)) { // Collect Mailchimp customer info if (($mailchimp_info = $this->mailchimp_lib->getMemberInfo($this->_list_id, $info->email)) !== false) { $data['mailchimp_info'] = $mailchimp_info; // Collect customer Mailchimp emails activities (stats) if (($activities = $this->mailchimp_lib->getMemberActivity($this->_list_id, $info->email)) !== false) { if (array_key_exists('activity', $activities)) { $open = 0; $unopen = 0; $click = 0; $total = 0; $lastopen = ''; foreach ($activities['activity'] as $activity) { if ($activity['action'] == 'sent') { ++$unopen; } elseif ($activity['action'] == 'open') { if (empty($lastopen)) { $lastopen = substr($activity['timestamp'], 0, 10); } ++$open; } elseif ($activity['action'] == 'click') { if (empty($lastopen)) { $lastopen = substr($activity['timestamp'], 0, 10); } ++$click; } ++$total; } $data['mailchimp_activity']['total'] = $total; $data['mailchimp_activity']['open'] = $open; $data['mailchimp_activity']['unopen'] = $unopen; $data['mailchimp_activity']['click'] = $click; $data['mailchimp_activity']['lastopen'] = $lastopen; } } } } echo view("customers/form", $data); } /** * Inserts/updates a customer */ public function postSave(int $customer_id = NEW_ENTRY): void { $first_name = $this->request->getPost('first_name'); $last_name = $this->request->getPost('last_name'); $email = strtolower($this->request->getPost('email', FILTER_SANITIZE_EMAIL)); // Format first and last name properly $first_name = $this->nameize($first_name); $last_name = $this->nameize($last_name); $person_data = [ 'first_name' => $first_name, 'last_name' => $last_name, 'gender' => $this->request->getPost('gender', FILTER_SANITIZE_NUMBER_INT), 'email' => $email, 'phone_number' => $this->request->getPost('phone_number'), 'address_1' => $this->request->getPost('address_1'), 'address_2' => $this->request->getPost('address_2'), 'city' => $this->request->getPost('city'), 'state' => $this->request->getPost('state'), 'zip' => $this->request->getPost('zip'), 'country' => $this->request->getPost('country'), 'comments' => $this->request->getPost('comments') ]; $date_formatter = date_create_from_format($this->config['dateformat'] . ' ' . $this->config['timeformat'], $this->request->getPost('date')); $customer_data = [ 'consent' => $this->request->getPost('consent') != null, 'account_number' => $this->request->getPost('account_number') == '' ? null : $this->request->getPost('account_number'), 'tax_id' => $this->request->getPost('tax_id'), 'company_name' => $this->request->getPost('company_name') == '' ? null : $this->request->getPost('company_name'), 'discount' => $this->request->getPost('discount') == '' ? 0.00 : parse_decimals($this->request->getPost('discount')), 'discount_type' => $this->request->getPost('discount_type') == null ? PERCENT : $this->request->getPost('discount_type', FILTER_SANITIZE_NUMBER_INT), 'package_id' => $this->request->getPost('package_id') == '' ? null : $this->request->getPost('package_id'), 'taxable' => $this->request->getPost('taxable') != null, 'date' => $date_formatter->format('Y-m-d H:i:s'), 'employee_id' => $this->request->getPost('employee_id', FILTER_SANITIZE_NUMBER_INT), 'sales_tax_code_id' => $this->request->getPost('sales_tax_code_id') == '' ? null : $this->request->getPost('sales_tax_code_id', FILTER_SANITIZE_NUMBER_INT) ]; if ($this->customer->save_customer($person_data, $customer_data, $customer_id)) { // Save customer to Mailchimp selected list // TODO: addOrUpdateMember should be refactored. Potentially pass an array or object instead of 6 parameters. $mailchimp_status = $this->request->getPost('mailchimp_status'); $this->mailchimp_lib->addOrUpdateMember( $this->_list_id, $email, $first_name, $last_name, $mailchimp_status == null ? "" : $mailchimp_status, ['vip' => $this->request->getPost('mailchimp_vip') != null] ); // New customer if ($customer_id == NEW_ENTRY) { echo json_encode([ 'success' => true, 'message' => lang('Customers.successful_adding') . ' ' . $first_name . ' ' . $last_name, 'id' => $customer_data['person_id'] ]); } else { // Existing customer echo json_encode([ 'success' => true, 'message' => lang('Customers.successful_updating') . ' ' . $first_name . ' ' . $last_name, 'id' => $customer_id ]); } } else { // Failure echo json_encode([ 'success' => false, 'message' => lang('Customers.error_adding_updating') . ' ' . $first_name . ' ' . $last_name, 'id' => NEW_ENTRY ]); } } /** * Verifies if an email address already exists. Used in app/Views/customers/form.php * * @return void * @noinspection PhpUnused */ public function postCheckEmail(): void { $email = strtolower($this->request->getPost('email', FILTER_SANITIZE_EMAIL)); $person_id = $this->request->getPost('person_id', FILTER_SANITIZE_NUMBER_INT); $exists = $this->customer->check_email_exists($email, $person_id); echo !$exists ? 'true' : 'false'; } /** * Verifies if an account number already exists. Used in app/Views/customers/form.php * * @return void * @noinspection PhpUnused */ public function postCheckAccountNumber(): void { $exists = $this->customer->check_account_number_exists($this->request->getPost('account_number'), $this->request->getPost('person_id', FILTER_SANITIZE_NUMBER_INT)); echo !$exists ? 'true' : 'false'; } /** * This deletes customers from the customers table */ public function postDelete(): void { $customers_to_delete = $this->request->getPost('ids'); $customers_info = $this->customer->get_multiple_info($customers_to_delete); $count = 0; foreach ($customers_info->getResult() as $info) { if ($this->customer->delete($info->person_id)) { // remove customer from Mailchimp selected list $this->mailchimp_lib->removeMember($this->_list_id, $info->email); $count++; } } if ($count == count($customers_to_delete)) { echo json_encode([ 'success' => true, 'message' => lang('Customers.successful_deleted') . ' ' . $count . ' ' . lang('Customers.one_or_multiple') ]); } else { echo json_encode(['success' => false, 'message' => lang('Customers.cannot_be_deleted')]); } } /** * Customers import from csv spreadsheet * * @return DownloadResponse The template for Customer CSV imports is returned and download forced. * @noinspection PhpUnused */ public function getCsv(): DownloadResponse { $name = 'importCustomers.csv'; $data = file_get_contents(WRITEPATH . "uploads/$name"); return $this->response->download($name, $data); } /** * Displays the customer CSV import modal. Used in app/Views/people/manage.php * * @return void * @noinspection PhpUnused */ public function getCsvImport(): void { echo view('customers/form_csv_import'); } /** * Imports a CSV file containing customers. Used in app/Views/customers/form_csv_import.php * * @return void * @noinspection PhpUnused */ public function postImportCsvFile(): void { if ($_FILES['file_path']['error'] != UPLOAD_ERR_OK) { echo json_encode(['success' => false, 'message' => lang('Customers.csv_import_failed')]); } else { if (($handle = fopen($_FILES['file_path']['tmp_name'], 'r')) !== false) { // Skip the first row as it's the table description fgetcsv($handle); $i = 1; $failCodes = []; while (($data = fgetcsv($handle)) !== false) { $consent = $data[3] == '' ? 0 : 1; if (sizeof($data) >= 16 && $consent) { $email = strtolower($data[4]); $person_data = [ 'first_name' => $data[0], 'last_name' => $data[1], 'gender' => $data[2], 'email' => $email, 'phone_number' => $data[5], 'address_1' => $data[6], 'address_2' => $data[7], 'city' => $data[8], 'state' => $data[9], 'zip' => $data[10], 'country' => $data[11], 'comments' => $data[12] ]; $customer_data = [ 'consent' => $consent, 'company_name' => $data[13], 'discount' => $data[15], 'discount_type' => $data[16], 'taxable' => $data[17] == '' ? 0 : 1, 'date' => date('Y-m-d H:i:s'), 'employee_id' => $this->employee->get_logged_in_employee_info()->person_id ]; $account_number = $data[14]; // Don't duplicate people with same email $invalidated = $this->customer->check_email_exists($email); if ($account_number != '') { $customer_data['account_number'] = $account_number; $invalidated &= $this->customer->check_account_number_exists($account_number); } } else { $invalidated = true; } if ($invalidated) { $failCodes[] = $i; log_message('error', "Row $i was not imported: Either email or account number already exist or data was invalid."); } elseif ($this->customer->save_customer($person_data, $customer_data)) { // Save customer to Mailchimp selected list $this->mailchimp_lib->addOrUpdateMember($this->_list_id, $person_data['email'], $person_data['first_name'], '', $person_data['last_name']); } else { $failCodes[] = $i; } ++$i; } if (count($failCodes) > 0) { $message = lang('Customers.csv_import_partially_failed', [count($failCodes), implode(', ', $failCodes)]); echo json_encode(['success' => false, 'message' => $message]); } else { echo json_encode(['success' => true, 'message' => lang('Customers.csv_import_success')]); } } else { echo json_encode(['success' => false, 'message' => lang('Customers.csv_import_nodata_wrongformat')]); } } } }