From 5bdf6d396b20effda1a15c18fe85d9d35457825d Mon Sep 17 00:00:00 2001 From: FrancescoUK Date: Sat, 22 Apr 2017 13:42:34 +0100 Subject: [PATCH] Add Mailchimp connector (#113) avoid email duplicates (#112) --- application/controllers/Config.php | 158 +++++- application/controllers/Customers.php | 178 +++++-- application/controllers/Employees.php | 7 - application/controllers/Persons.php | 11 +- application/controllers/Suppliers.php | 4 +- application/helpers/table_helper.php | 6 +- application/language/en/config_lang.php | 7 + application/language/en/customers_lang.php | 20 +- application/language/en/sales_lang.php | 1 + application/libraries/Mailchimp_lib.php | 357 +++++++++++++ application/models/Customer.php | 74 ++- application/models/Person.php | 2 +- application/views/configs/general_config.php | 2 +- .../views/configs/mailchimp_config.php | 83 +++ application/views/configs/manage.php | 6 + application/views/customers/form.php | 476 ++++++++++++++---- application/views/employees/form.php | 2 +- application/views/item_kits/form.php | 3 +- application/views/sales/register.php | 11 + application/views/suppliers/form.php | 2 +- 20 files changed, 1216 insertions(+), 194 deletions(-) create mode 100644 application/libraries/Mailchimp_lib.php create mode 100644 application/views/configs/mailchimp_config.php diff --git a/application/controllers/Config.php b/application/controllers/Config.php index e6d05cf8d..74e3329de 100644 --- a/application/controllers/Config.php +++ b/application/controllers/Config.php @@ -94,9 +94,9 @@ class Config extends Secure_Controller $license[$i]['text'] .= 'component: ' . $key1 . "\n"; foreach($val1 as $key2 => $val2) - { + { if(is_array($val2)) - { + { $license[$i]['text'] .= $key2 . ': '; foreach($val2 as $key3 => $val3) @@ -163,13 +163,16 @@ class Config extends Secure_Controller $license[$i]['text'] .= "\n"; } } - + $license[$i]['text'] = $this->xss_clean($license[$i]['text']); } - + return $license; } + /* + * This function loads all the available themes in the dist/bootswatch directory + */ private function _themes() { $themes = array(); @@ -181,7 +184,8 @@ class Config extends Secure_Controller { if($dirinfo->isDir() && !$dirinfo->isDot() && $dirinfo->getFileName() != 'fonts') { - $themes[$dirinfo->getFileName()] = $dirinfo->getFileName(); + $file = $this->xss_clean($dirinfo->getFileName()); + $themes[$file] = $file; } } @@ -189,7 +193,7 @@ class Config extends Secure_Controller return $themes; } - + public function index() { $data['stock_locations'] = $this->Stock_location->get_all()->result_array(); @@ -202,16 +206,30 @@ class Config extends Secure_Controller $data['rounding_options'] = Rounding_code::get_rounding_options(); $data = $this->xss_clean($data); - + // load all the license statements, they are already XSS cleaned in the private function $data['licenses'] = $this->_licenses(); + // load all the themes, already XSS cleaned in the private function $data['themes'] = $this->_themes(); - $this->_check_encryption(); + $data['mailchimp'] = array(); + if($this->_check_encryption()) + { + $data['mailchimp']['api_key'] = $this->encryption->decrypt($this->config->item('mailchimp_api_key')); + $data['mailchimp']['list_id'] = $this->encryption->decrypt($this->config->item('mailchimp_list_id')); + } + else + { + $data['mailchimp']['api_key'] = ''; + $data['mailchimp']['list_id'] = ''; + } + + // load mailchimp lists associated to the given api key, already XSS cleaned in the private function + $data['mailchimp']['lists'] = $this->_mailchimp(); $this->load->view("configs/manage", $data); } - + public function save_info() { $upload_success = $this->_handle_logo_upload(); @@ -226,7 +244,7 @@ class Config extends Secure_Controller 'website' => $this->input->post('website'), 'return_policy' => $this->input->post('return_policy') ); - + if (!empty($upload_data['orig_name'])) { // XSS file image sanity check @@ -235,15 +253,18 @@ class Config extends Secure_Controller $batch_save_data['company_logo'] = $upload_data['raw_name'] . $upload_data['file_ext']; } } - + $result = $this->Appconfig->batch_save($batch_save_data); $success = $upload_success && $result ? TRUE : FALSE; $message = $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'); $message = $upload_success ? $message : strip_tags($this->upload->display_errors()); - echo json_encode(array('success' => $success, 'message' => $message)); + echo json_encode(array( + 'success' => $success, + 'message' => $message + )); } - + public function save_general() { $batch_save_data = array( @@ -273,11 +294,14 @@ class Config extends Secure_Controller 'statistics' => $this->input->post('statistics') != NULL, 'giftcard_number' => $this->input->post('giftcard_number'), ); - + $result = $this->Appconfig->batch_save($batch_save_data); $success = $result ? TRUE : FALSE; - echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'))); + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); } public function check_number_locale() @@ -321,11 +345,14 @@ class Config extends Secure_Controller 'cash_rounding_code' => $this->input->post('cash_rounding_code'), 'financial_year' => $this->input->post('financial_year') ); - + $result = $this->Appconfig->batch_save($batch_save_data); $success = $result ? TRUE : FALSE; - echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'))); + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); } public function save_email() @@ -351,7 +378,10 @@ class Config extends Secure_Controller $result = $this->Appconfig->batch_save($batch_save_data); $success = $result ? TRUE : FALSE; - echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'))); + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); } public function save_message() @@ -373,7 +403,75 @@ class Config extends Secure_Controller $result = $this->Appconfig->batch_save($batch_save_data); $success = $result ? TRUE : FALSE; - echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'))); + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); + } + + /* + * This function fetches all the available lists from Mailchimp for the given API key + */ + private function _mailchimp($api_key = '') + { + $this->load->library('mailchimp_lib', array('api_key' => $api_key)); + + $result = array(); + + if(($lists = $this->mailchimp_lib->getLists()) !== FALSE) + { + if(is_array($lists) && !empty($lists['lists']) && is_array($lists['lists'])) + { + foreach($lists['lists'] as $list) + { + $list = $this->xss_clean($list); + $result[$list['id']] = $list['name'] . ' [' . $list['stats']['member_count'] . ']'; + } + } + } + + return $result; + } + + /* + AJAX call from mailchimp config form to fetch the Mailchimp lists when a valid API key is inserted + */ + public function ajax_check_mailchimp_api_key() + { + // load mailchimp lists associated to the given api key, already XSS cleaned in the private function + $lists = $this->_mailchimp($this->input->post('mailchimp_api_key')); + $success = count($lists) > 0 ? TRUE : FALSE; + + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_mailchimp_key_' . ($success ? '' : 'un') . 'successfully'), + 'mailchimp_lists' => $lists + )); + } + + public function save_mailchimp() + { + $api_key = ''; + $list_id = ''; + + if($this->_check_encryption()) + { + $api_key = $this->encryption->encrypt($this->input->post('mailchimp_api_key')); + $list_id = $this->encryption->encrypt($this->input->post('mailchimp_list_id')); + } + + $batch_save_data = array( + 'mailchimp_api_key' => $api_key, + 'mailchimp_list_id' => $list_id + ); + + $result = $this->Appconfig->batch_save($batch_save_data); + $success = $result ? TRUE : FALSE; + + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); } public function stock_locations() @@ -535,7 +633,10 @@ class Config extends Secure_Controller $success = $this->db->trans_status(); - echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'))); + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); } public function save_barcode() @@ -560,7 +661,10 @@ class Config extends Secure_Controller $result = $this->Appconfig->batch_save($batch_save_data); $success = $result ? TRUE : FALSE; - echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'))); + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); } public function save_receipt() @@ -583,7 +687,10 @@ class Config extends Secure_Controller $result = $this->Appconfig->batch_save($batch_save_data); $success = $result ? TRUE : FALSE; - echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'))); + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); } public function save_invoice() @@ -618,7 +725,10 @@ class Config extends Secure_Controller } } - echo json_encode(array('success' => $success, 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully'))); + echo json_encode(array( + 'success' => $success, + 'message' => $this->lang->line('config_saved_' . ($success ? '' : 'un') . 'successfully') + )); } public function remove_logo() @@ -681,7 +791,7 @@ class Config extends Secure_Controller // Write the file $result = (fwrite($handle, $config) === FALSE) ? FALSE : TRUE; } - + fclose($handle); // Chmod the file diff --git a/application/controllers/Customers.php b/application/controllers/Customers.php index d537cd9cb..b442dcb50 100644 --- a/application/controllers/Customers.php +++ b/application/controllers/Customers.php @@ -4,18 +4,19 @@ require_once("Persons.php"); class Customers extends Persons { + private $_list_id; + public function __construct() { parent::__construct('customers'); - } - - public function index() - { - $data['table_headers'] = $this->xss_clean(get_people_manage_table_headers()); - $this->load->view('people/manage', $data); + $this->load->library('mailchimp_lib'); + + $CI =& get_instance(); + + $this->_list_id = $CI->encryption->decrypt($CI->Appconfig->get('mailchimp_list_id')); } - + /* Returns customer table data rows. This will be called with AJAX. */ @@ -72,7 +73,6 @@ class Customers extends Persons } $data['person_info'] = $info; $data['sales_tax_code_label'] = $info->sales_tax_code . ' ' . $this->Tax->get_info($info->sales_tax_code)->tax_code_name; - $data['total'] = $this->xss_clean($this->Customer->get_stats($customer_id)->total); $packages = array('' => $this->lang->line('items_none')); foreach($this->Customer_rewards->get_all()->result_array() as $row) { @@ -90,6 +90,72 @@ class Customers extends Persons $data['customer_sales_tax_enabled'] = FALSE; } + // show 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 = $this->xss_clean($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'] = $this->xss_clean($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; + } + } + } + } + $this->load->view("customers/form", $data); } @@ -98,11 +164,15 @@ class Customers extends Persons */ public function save($customer_id = -1) { + $first_name = $this->xss_clean($this->input->post('first_name')); + $last_name = $this->xss_clean($this->input->post('last_name')); + $email = $this->xss_clean(strtolower($this->input->post('email'))); + $person_data = array( - 'first_name' => $this->input->post('first_name'), - 'last_name' => $this->input->post('last_name'), + 'first_name' => $first_name, + 'last_name' => $last_name, 'gender' => $this->input->post('gender'), - 'email' => $this->input->post('email'), + 'email' => $email, 'phone_number' => $this->input->post('phone_number'), 'address_1' => $this->input->post('address_1'), 'address_2' => $this->input->post('address_2'), @@ -133,33 +203,50 @@ class Customers extends Persons if($this->Customer->save_customer($person_data, $customer_data, $customer_id)) { - $person_data = $this->xss_clean($person_data); - $customer_data = $this->xss_clean($customer_data); - - //New customer + // save customer to Mailchimp selected list + $this->mailchimp_lib->addOrUpdateMember($this->_list_id, $email, $first_name, $last_name, $this->input->post('mailchimp_status'), array('vip' => $this->input->post('mailchimp_vip') != NULL)); + + // New customer if($customer_id == -1) { - echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('customers_successful_adding').' '. - $person_data['first_name'].' '.$person_data['last_name'], 'id' => $customer_data['person_id'])); + echo json_encode(array( + 'success' => TRUE, + 'message' => $this->lang->line('customers_successful_adding') . ' ' . $first_name . ' ' . $last_name, + 'id' => $this->xss_clean($customer_data['person_id']))); } - else //Existing customer + else // Existing customer { - echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('customers_successful_updating').' '. - $person_data['first_name'].' '.$person_data['last_name'], 'id' => $customer_id)); + echo json_encode(array( + 'success' => TRUE, + 'message' => $this->lang->line('customers_successful_updating') . ' ' . $first_name . ' ' . $last_name, + 'id' => $customer_id)); } } - else//failure + else // Failure { - $person_data = $this->xss_clean($person_data); - - echo json_encode(array('success' => FALSE, 'message' => $this->lang->line('customers_error_adding_updating').' '. - $person_data['first_name'].' '.$person_data['last_name'], 'id' => -1)); + echo json_encode(array( + 'success' => FALSE, + 'message' => $this->lang->line('customers_error_adding_updating') . ' ' . $first_name . ' ' . $last_name, + 'id' => -1)); } } - - public function check_account_number() + + /* + AJAX call to verify if an email address already exists + */ + public function ajax_check_email() { - $exists = $this->Customer->account_number_exists($this->input->post('account_number'), $this->input->post('person_id')); + $exists = $this->Customer->check_email_exists(strtolower($this->input->post('email')), $this->input->post('person_id')); + + echo !$exists ? 'true' : 'false'; + } + + /* + AJAX call to verify if an account number already exists + */ + public function ajax_check_account_number() + { + $exists = $this->Customer->check_account_number_exists($this->input->post('account_number'), $this->input->post('person_id')); echo !$exists ? 'true' : 'false'; } @@ -169,12 +256,19 @@ class Customers extends Persons */ public function delete() { - $customers_to_delete = $this->xss_clean($this->input->post('ids')); + $customers_to_delete = $this->input->post('ids'); + $customers_info = $this->Customer->get_multiple_info($customers_to_delete); if($this->Customer->delete_list($customers_to_delete)) { - echo json_encode(array('success' => TRUE, 'message' => $this->lang->line('customers_successful_deleted').' '. - count($customers_to_delete).' '.$this->lang->line('customers_one_or_multiple'))); + foreach($customers_info->result() as $info) + { + // remove customer from Mailchimp selected list + $this->mailchimp_lib->removeMember($this->_list_id, $info->email); + } + + echo json_encode(array('success' => TRUE, + 'message' => $this->lang->line('customers_successful_deleted') . ' ' . count($customers_to_delete) . ' ' . $this->lang->line('customers_one_or_multiple'))); } else { @@ -220,11 +314,12 @@ class Customers extends Persons if(sizeof($data) >= 15) { + $email = strtolower($data[3]); $person_data = array( 'first_name' => $data[0], 'last_name' => $data[1], 'gender' => $data[2], - 'email' => $data[3], + 'email' => $email, 'phone_number' => $data[4], 'address_1' => $data[5], 'address_2' => $data[6], @@ -240,13 +335,15 @@ class Customers extends Persons 'discount_percent' => $data[14], 'taxable' => $data[15] == '' ? 0 : 1 ); - $account_number = $data[13]; - $invalidated = FALSE; + + // 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->account_number_exists($account_number); + $invalidated &= $this->Customer->check_account_number_exists($account_number); } } else @@ -254,7 +351,16 @@ class Customers extends Persons $invalidated = TRUE; } - if($invalidated || !$this->Customer->save_customer($person_data, $customer_data)) + if($invalidated) + { + $failCodes[] = $i; + } + 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; } diff --git a/application/controllers/Employees.php b/application/controllers/Employees.php index 7bbd8f3a4..eeac6f19c 100644 --- a/application/controllers/Employees.php +++ b/application/controllers/Employees.php @@ -8,14 +8,7 @@ class Employees extends Persons { parent::__construct('employees'); } - - public function index() - { - $data['table_headers'] = $this->xss_clean(get_people_manage_table_headers()); - $this->load->view('people/manage', $data); - } - /* Returns employee table data rows. This will be called with AJAX. */ diff --git a/application/controllers/Persons.php b/application/controllers/Persons.php index a5ce8de9f..a8f3e7985 100644 --- a/application/controllers/Persons.php +++ b/application/controllers/Persons.php @@ -8,7 +8,14 @@ abstract class Persons extends Secure_Controller { parent::__construct($module_id); } - + + public function index() + { + $data['table_headers'] = $this->xss_clean(get_people_manage_table_headers()); + + $this->load->view('people/manage', $data); + } + /* Gives search suggestions based on what is being searched for */ @@ -18,7 +25,7 @@ abstract class Persons extends Secure_Controller echo json_encode($suggestions); } - + /* Gets one row for a person manage table. This is called using AJAX to update one row. */ diff --git a/application/controllers/Suppliers.php b/application/controllers/Suppliers.php index 8bc80d880..f9e1be56c 100644 --- a/application/controllers/Suppliers.php +++ b/application/controllers/Suppliers.php @@ -8,14 +8,14 @@ class Suppliers extends Persons { parent::__construct('suppliers'); } - + public function index() { $data['table_headers'] = $this->xss_clean(get_suppliers_manage_table_headers()); $this->load->view('people/manage', $data); } - + /* Gets one row for a supplier manage table. This is called using AJAX to update one row. */ diff --git a/application/helpers/table_helper.php b/application/helpers/table_helper.php index 954839598..9555ff162 100644 --- a/application/helpers/table_helper.php +++ b/application/helpers/table_helper.php @@ -177,7 +177,7 @@ function get_people_manage_table_headers() function get_person_data_row($person, $controller) { $CI =& get_instance(); - $controller_name=strtolower(get_class($CI)); + $controller_name = strtolower(get_class($CI)); return array ( 'people.person_id' => $person->person_id, @@ -217,7 +217,7 @@ function get_suppliers_manage_table_headers() function get_supplier_data_row($supplier, $controller) { $CI =& get_instance(); - $controller_name=strtolower(get_class($CI)); + $controller_name = strtolower(get_class($CI)); return array ( 'people.person_id' => $supplier->person_id, @@ -398,7 +398,7 @@ function get_item_kits_manage_table_headers() function get_item_kit_data_row($item_kit, $controller) { $CI =& get_instance(); - $controller_name=strtolower(get_class($CI)); + $controller_name = strtolower(get_class($CI)); return array ( 'item_kit_id' => $item_kit->item_kit_id, diff --git a/application/language/en/config_lang.php b/application/language/en/config_lang.php index 3c9cfd181..ef621b3f8 100644 --- a/application/language/en/config_lang.php +++ b/application/language/en/config_lang.php @@ -86,6 +86,7 @@ $lang["config_default_tax_rate"] = "Default Tax Rate %"; $lang["config_default_tax_rate_1"] = "Tax 1 Rate"; $lang["config_default_tax_rate_2"] = "Tax 2 Rate"; $lang["config_default_tax_rate_number"] = "The default tax rate must be a number"; +$lang["config_default_tax_name_number"] = "The default tax name must be a string"; $lang["config_default_tax_rate_required"] = "The default tax rate is a required field"; $lang["config_dinner_table"] = "Table"; $lang["config_dinner_table_duplicate"] = "Please use an unique table name"; @@ -145,6 +146,12 @@ $lang["config_location"] = "Stock"; $lang["config_location_configuration"] = "Stock Locations"; $lang["config_location_info"] = "Location Configuration Information"; $lang["config_logout"] = "Don't you want to make a backup before logging out? Click [OK] to backup, [Cancel] to logout"; +$lang["config_mailchimp"] = "Mailchimp"; +$lang["config_mailchimp_configuration"] = "Mailchimp Configuration"; +$lang["config_mailchimp_api_key"] = "Mailchimp API Key"; +$lang["config_mailchimp_lists"] = "Mailchimp List(s)"; +$lang["config_mailchimp_key_successfully"] = "Valid API Key"; +$lang["config_mailchimp_key_unsuccessfully"] = "Invalid API Key"; $lang["config_message"] = "Message"; $lang["config_message_configuration"] = "Message Configuration"; $lang["config_msg_msg"] = "Saved Text Message"; diff --git a/application/language/en/customers_lang.php b/application/language/en/customers_lang.php index 4e16b923b..f0af8f0c5 100644 --- a/application/language/en/customers_lang.php +++ b/application/language/en/customers_lang.php @@ -3,25 +3,43 @@ $lang["customers_account_number"] = "Account #"; $lang["customers_account_number_duplicate"] = "This account number is already present in the database"; $lang["customers_available_points"] = "Available Points"; +$lang["customers_average"] = "Average spent"; +$lang["customers_avg_discount"] = "Average discount"; +$lang["customers_basic_information"] = "Information"; $lang["customers_cannot_be_deleted"] = "Could not deleted selected customers, one or more of the selected customers has sales."; $lang["customers_company_name"] = "Company"; $lang["customers_confirm_delete"] = "Are you sure you want to delete the selected customers?"; $lang["customers_customer"] = "Customer"; $lang["customers_discount"] = "Discount"; +$lang["customers_email_duplicate"] = "The email address is already present in the database"; $lang["customers_error_adding_updating"] = "Error adding/updating customer"; +$lang["customers_stats_info"] = "Stats"; $lang["customers_excel_import_failed"] = "Excel import failed"; $lang["customers_excel_import_nodata_wrongformat"] = "Your uploaded file has no data or wrong format"; $lang["customers_excel_import_partially_failed"] = "Most Customers imported. But some were not, here is the list"; $lang["customers_excel_import_success"] = "Import of Customers successful"; $lang["customers_import_items_excel"] = "Import customers from Excel sheet"; +$lang["customers_mailchimp_activity_click"] = "Email click"; +$lang["customers_mailchimp_activity_lastopen"] = "Last open email"; +$lang["customers_mailchimp_activity_open"] = "Email open"; +$lang["customers_mailchimp_activity_total"] = "Email sent"; +$lang["customers_mailchimp_activity_unopen"] = "Email unopen"; +$lang["customers_mailchimp_info"] = "Mailchimp"; +$lang["customers_mailchimp_member_rating"] = "Rating"; +$lang["customers_mailchimp_status"] = "Status"; +$lang["customers_mailchimp_vip"] = "VIP"; +$lang["customers_mailchimp_email_client"] = "Email client"; +$lang["customers_max"] = "Max spent"; +$lang["customers_min"] = "Min spent"; $lang["customers_new"] = "New Customer"; $lang["customers_none_selected"] = "You have not selected any customers to delete"; $lang["customers_one_or_multiple"] = "customer(s)"; +$lang["customers_quantity"] = "Quantity"; $lang["customers_successful_adding"] = "You have successfully added customer"; $lang["customers_successful_deleted"] = "You have successfully deleted"; $lang["customers_successful_updating"] = "You have successfully updated customer"; $lang["customers_tax_code"] = "Tax Code"; $lang["customers_taxable"] = "Taxable"; -$lang["customers_total"] = "Total"; +$lang["customers_total"] = "Total spent"; $lang["customers_update"] = "Update Customer"; $lang["rewards_package"] = "Rewards Package"; diff --git a/application/language/en/sales_lang.php b/application/language/en/sales_lang.php index 2f7244001..bec6307a2 100644 --- a/application/language/en/sales_lang.php +++ b/application/language/en/sales_lang.php @@ -25,6 +25,7 @@ $lang["sales_customer_discount"] = "Discount"; $lang["sales_customer_email"] = "Email"; $lang["sales_customer_location"] = "Location"; $lang["sales_customer_total"] = "Total"; +$lang["sales_customer_mailchimp_status"] = "Mailchimp status"; $lang["sales_date"] = "Sale Date"; $lang["sales_date_range"] = "Date Range"; $lang["sales_date_required"] = "A correct date needs to be filled in"; diff --git a/application/libraries/Mailchimp_lib.php b/application/libraries/Mailchimp_lib.php new file mode 100644 index 000000000..3dc39c443 --- /dev/null +++ b/application/libraries/Mailchimp_lib.php @@ -0,0 +1,357 @@ +.api.mailchimp.com/3.0/'; + + /** + * Constructor + */ + public function __construct($api_key = '') + { + $CI =& get_instance(); + + if(empty($api_key)) + { + $this->_api_key = $CI->encryption->decrypt($CI->Appconfig->get('mailchimp_api_key')); + } + else + { + $this->_api_key = $api_key; + } + + if(!empty($this->_api_key)) + { + // Replace with correct datacenter obtained from the last part of the api key + $strings = explode('-', $this->_api_key); + if(is_array($strings) && !empty($strings[1])) + { + $this->_api_endpoint = str_replace('', $strings[1], $this->_api_endpoint); + } + } + } + + /** + * Call an API method. Every request needs the API key + * @param string $httpVerb The HTTP method to be used + * @param string $method The API method to call, e.g. 'lists/list' + * @param array $args An array of arguments to pass to the method. Will be json-encoded for you. + * @return array Associative array of json decoded API response. + */ + public function call($httpVerb = 'POST', $method, $args = array()) + { + if(!empty($this->_api_key)) + { + return $this->_request($httpVerb, $method, $args); + } + + return FALSE; + } + + /** + * Builds the request URL based on request type + * @param string $httpVerb The HTTP method to be used + * @param string $method The API method to be called + * @param array $args Assoc array of parameters to be passed + * @return string Request URL + */ + private function _build_request_url($httpVerb = 'POST', $method, $args = array()) + { + if($httpVerb == 'GET') + { + return $this->_api_endpoint . $method . '?' . http_build_query($args); + } + + return $this->_api_endpoint . $method; + } + + /** + * Performs the underlying HTTP request. + * @param string $httpVerb The HTTP method to be used + * @param string $method The API method to be called + * @param array $args Assoc array of parameters to be passed + * @return array Assoc array of decoded result + */ + private function _request($httpVerb, $method, $args = array()) + { + $result = FALSE; + + if(($ch = curl_init()) !== FALSE) + { + curl_setopt($ch, CURLOPT_URL, $this->_build_request_url($httpVerb, $method, $args)); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); + curl_setopt($ch, CURLOPT_USERPWD, "user:" . $this->_api_key); + curl_setopt($ch, CURLOPT_USERAGENT, 'PHP-MCAPI/3.0'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_POST, TRUE); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($args)); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpVerb); + + $result = curl_exec($ch); + + curl_close($ch); + } + + return $result ? json_decode($result, TRUE) : FALSE; + } +} + + +/** + * Mailchimp library, usable from CI code + * + * Library with utility queries to interface Mailchimp v3 API + * + * Inspired by the work of ThinkShout: https://github.com/thinkshout/mailchimp-api-php + */ + +class Mailchimp_lib +{ + private $_connector; + + public function __construct(array $params = array()) + { + $api_key = (count($params) > 0 && !empty($params['api_key'])) ? $params['api_key'] : ''; + $this->_connector = new MailchimpConnector($api_key); + } + + /** + * Gets information about all lists owned by the authenticated account. + * + * @param array $parameters + * Associative array of optional request parameters. + * By the default it places a simple query to list name & id and count of members & merge_fields + * NOTE: no space between , and next word is allowed. You will not get the filter to work in full but just the first tag + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/#read-get_lists + */ + public function getLists(array $parameters = array('fields' => 'lists.id,lists.name,lists.stats.member_count,lists.stats.merge_field_count')) + { + return $this->_connector->call('GET', '/lists', $parameters); + } + + /** + * Gets a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param array $parameters + * Associative array of optional request parameters. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/#read-get_lists_list_id + */ + public function getList($list_id, $parameters = array('fields' => 'id,name,stats.member_count,stats.merge_field_count')) + { + return $this->_connector->call('GET', '/lists/' . $list_id, $parameters); + } + + /** + * Gets information about all members of a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param array $parameters + * Associative array of optional request parameters. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#read-get_lists_list_id_members + */ + public function getMembers($list_id, $count, $offset, $parameters = array('fields' => 'members.id,members.email_address,members.unique_email_id,members.status,members.merge_fields')) + { + $parameters += [ + 'count' => $count, + 'offset' => $offset + ]; + + return $this->_connector->call('GET', '/lists/' . $list_id . '/members', $parameters); + } + + /** + * Gets information about a member of a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param string $md5id + * The member's email address md5 hash which is the id. + * @param array $parameters + * Associative array of optional request parameters. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#read-get_lists_list_id_members_subscriber_hash + */ + public function getMemberInfoById($list_id, $md5id, $parameters = array('fields' => 'email_address,status,merge_fields')) + { + return $this->_connector->call('GET', '/lists/' . $list_id . '/members/' . $md5id, $parameters); + } + + /** + * Gets information about a member of a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param string $email + * The member's email address. + * @param array $parameters + * Associative array of optional request parameters. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#read-get_lists_list_id_members_subscriber_hash + */ + public function getMemberInfo($list_id, $email, $parameters = array()) + { + return $this->_connector->call('GET', '/lists/' . $list_id . '/members/' . md5(strtolower($email)), $parameters); + } + + /** + * Gets activity related to a member of a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param string $email + * The member's email address. + * @param array $parameters + * Associative array of optional request parameters. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/activity/#read-get_lists_list_id_members_subscriber_hash_activity + */ + public function getMemberActivity($list_id, $email, $parameters = array()) + { + return $this->_connector->call('GET', '/lists/' . $list_id . '/members/' . md5(strtolower($email)) . '/activity', $parameters); + } + + /** + * Adds a new member to a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param string $email + * The email address to add. + * @param array $parameters + * Associative array of optional request parameters. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#create-post_lists_list_id_members + */ + public function addMember($list_id, $email, $first_name, $last_name, $parameters = array()) + { + $parameters += [ + 'email_address' => $email, + 'status' => 'subscribed', + 'merge_fields' => array( + 'FNAME' => $first_name, + 'LNAME' => $last_name + ) + ]; + + return $this->_connector->call('POST', '/lists/' . $list_id . '/members/', $parameters); + } + + /** + * Removes a member from a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param string $email + * The member's email address. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#delete-delete_lists_list_id_members_subscriber_hash + */ + public function removeMember($list_id, $email) + { + return $this->_connector->call('DELETE', '/lists/' . $list_id . '/members/' . md5(strtolower($email))); + } + + /** + * Updates a member of a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param string $email + * The member's email address. + * @param array $parameters + * Associative array of optional request parameters. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#edit-patch_lists_list_id_members_subscriber_hash + */ + public function updateMember($list_id, $email, $first_name, $last_name, $parameters = array()) + { + $parameters += [ + 'status' => 'subscribed', + 'merge_fields' => array( + 'FNAME' => $first_name, + 'LNAME' => $last_name + ) + ]; + + return $this->_connector->call('PATCH', '/lists/' . $list_id . '/members/' . md5(strtolower($email)), $parameters); + } + + /** + * Adds a new or update an existing member of a MailChimp list. + * + * @param string $list_id + * The ID of the list. + * @param string $email + * The member's email address. + * @param array $parameters + * Associative array of optional request parameters. + * + * @return object + * + * @see http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#edit-put_lists_list_id_members_subscriber_hash + */ + public function addOrUpdateMember($list_id, $email, $first_name, $last_name, $status, $parameters = array()) + { + $parameters += [ + 'email_address' => $email, + 'status' => $status, + 'status_if_new' => 'subscribed', + 'merge_fields' => array( + 'FNAME' => $first_name, + 'LNAME' => $last_name + ) + ]; + + return $this->_connector->call('PUT', '/lists/' . $list_id . '/members/' . md5(strtolower($email)), $parameters); + } +} + +?> \ No newline at end of file diff --git a/application/models/Customer.php b/application/models/Customer.php index 1cb9f464e..9b5f472cb 100644 --- a/application/models/Customer.php +++ b/application/models/Customer.php @@ -16,7 +16,7 @@ class Customer extends Person /* Checks if account number exists */ - public function account_number_exists($account_number, $person_id = '') + public function check_account_number_exists($account_number, $person_id = '') { $this->db->from('customers'); $this->db->where('account_number', $account_number); @@ -93,13 +93,47 @@ class Customer extends Person */ public function get_stats($customer_id) { - $this->db->select('SUM(payment_amount) AS total'); - $this->db->from('sales'); - $this->db->join('sales_payments', 'sales.sale_id = sales_payments.sale_id'); - $this->db->where('sales.customer_id', $customer_id); - $this->db->where('sale_status',0); + // create a temporary table to contain all the sum and average of items + $this->db->query('CREATE TEMPORARY TABLE IF NOT EXISTS ' . $this->db->dbprefix('sales_items_temp') . + ' (INDEX(sale_id)) + ( + SELECT + sales.sale_id AS sale_id, + AVG(sales_items.discount_percent) AS avg_discount, + SUM(sales_items.quantity_purchased) AS quantity + FROM ' . $this->db->dbprefix('sales') . ' AS sales + INNER JOIN ' . $this->db->dbprefix('sales_items') . ' AS sales_items + ON sales_items.sale_id = sales.sale_id + WHERE sales.customer_id=' . $this->db->escape($customer_id) . ' + GROUP BY sale_id + )' + ); - return $this->db->get()->row(); + $totals_decimals = totals_decimals(); + $quantity_decimals = quantity_decimals(); + + $this->db->select(' + SUM(sales_payments.payment_amount) AS total, + MIN(sales_payments.payment_amount) AS min, + MAX(sales_payments.payment_amount) AS max, + AVG(sales_payments.payment_amount) AS average, + ' . " + ROUND(AVG(sales_items_temp.avg_discount), $totals_decimals) AS avg_discount, + ROUND(SUM(sales_items_temp.quantity), $quantity_decimals) AS quantity + "); + $this->db->from('sales'); + $this->db->join('sales_payments AS sales_payments', 'sales.sale_id = sales_payments.sale_id'); + $this->db->join('sales_items_temp AS sales_items_temp', 'sales.sale_id = sales_items_temp.sale_id'); + $this->db->where('sales.customer_id', $customer_id); + $this->db->where('sales.sale_status', 0); + $this->db->group_by('sales.customer_id'); + + $stat = $this->db->get()->row(); + + // drop the temporary table to contain memory consumption as it's no longer required + $this->db->query('DROP TEMPORARY TABLE IF EXISTS ' . $this->db->dbprefix('sales_items_temp')); + + return $stat; } /* @@ -114,7 +148,31 @@ class Customer extends Person return $this->db->get(); } - + + /* + Checks if customer email exists + */ + public function check_email_exists($email, $customer_id = '') + { + // if the email is empty return like it is not existing + if(empty($email)) + { + return FALSE; + } + + $this->db->from('customers'); + $this->db->join('people', 'people.person_id = customers.person_id'); + $this->db->where('people.email', $email); + $this->db->where('customers.deleted', 0); + + if(!empty($customer_id)) + { + $this->db->where('customers.person_id !=', $customer_id); + } + + return ($this->db->get()->num_rows() == 1); + } + /* Inserts or updates a customer */ diff --git a/application/models/Person.php b/application/models/Person.php index 2ae0dff13..230200d97 100644 --- a/application/models/Person.php +++ b/application/models/Person.php @@ -72,7 +72,7 @@ class Person extends CI_Model return $this->db->get(); } - + /* Inserts or updates a person */ diff --git a/application/views/configs/general_config.php b/application/views/configs/general_config.php index 1d7f44ff3..0c4ad8c25 100644 --- a/application/views/configs/general_config.php +++ b/application/views/configs/general_config.php @@ -6,7 +6,7 @@
lang->line('config_theme'), 'theme', array('class' => 'control-label col-xs-2')); ?> -
+
config->item('theme'), array('class' => 'form-control input-sm')); ?>
diff --git a/application/views/configs/mailchimp_config.php b/application/views/configs/mailchimp_config.php new file mode 100644 index 000000000..1cd2d098b --- /dev/null +++ b/application/views/configs/mailchimp_config.php @@ -0,0 +1,83 @@ + 'mailchimp_config_form', 'enctype' => 'multipart/form-data', 'class' => 'form-horizontal')); ?> +
+
+
lang->line('common_fields_required_message'); ?>
+
    + +
    + lang->line('config_mailchimp_api_key'), 'mailchimp_api_key', array('class' => 'control-label col-xs-2')); ?> +
    +
    + + 'mailchimp_api_key', + 'id' => 'mailchimp_api_key', + 'class' => 'form-control input-sm', + 'value' => $mailchimp['api_key'])); ?> +
    +
    +
    + +
    + lang->line('config_mailchimp_lists'), 'mailchimp_list_id', array('class' => 'control-label col-xs-2')); ?> +
    +
    + + 'mailchimp_list_id', 'class' => 'form-control input-sm')); ?> +
    +
    +
    + + 'submit_form', + 'id' => 'submit_form', + 'value'=>$this->lang->line('common_submit'), + 'class' => 'btn btn-primary btn-sm pull-right')); ?> +
    +
    + + + \ No newline at end of file diff --git a/application/views/configs/manage.php b/application/views/configs/manage.php index bd8b28038..58de68850 100644 --- a/application/views/configs/manage.php +++ b/application/views/configs/manage.php @@ -34,6 +34,9 @@
  • lang->line('config_message'); ?>
  • +
  • + lang->line('config_mailchimp'); ?> +
  • lang->line('config_license'); ?>
  • @@ -73,6 +76,9 @@
    load->view("configs/message_config"); ?>
    +
    + load->view("configs/mailchimp_config"); ?> +
    load->view("configs/license_config"); ?>
    diff --git a/application/views/customers/form.php b/application/views/customers/form.php index 78988c6ab..b35ec882f 100644 --- a/application/views/customers/form.php +++ b/application/views/customers/form.php @@ -2,101 +2,358 @@
      -person_id, array('id'=>'customer_form', 'class'=>'form-horizontal')); ?> -
      - load->view("people/form_basic_info"); ?> +person_id, array('id'=>'customer_form', 'class'=>'form-horizontal')); ?> + -
      - lang->line('customers_company_name'), 'company_name', array('class' => 'control-label col-xs-3')); ?> -
      - 'company_name', - 'class'=>'form-control input-sm', - 'value'=>$person_info->company_name) - );?> -
      -
      +
      +
      +
      + load->view("people/form_basic_info"); ?> -
      - lang->line('customers_account_number'), 'account_number', array('class' => 'control-label col-xs-3')); ?> -
      - 'account_number', - 'id'=>'account_number', - 'class'=>'form-control input-sm', - 'value'=>$person_info->account_number) - );?> -
      -
      - -
      - lang->line('customers_total'), 'total', array('class' => 'control-label col-xs-3')); ?> -
      -
      - - config->item('currency_symbol'); ?> - - 'total', - 'id'=>'total', - 'class'=>'form-control input-sm', - 'value'=>to_currency_no_money($total), - 'disabled'=>'') - );?> - - config->item('currency_symbol'); ?> - +
      + lang->line('customers_discount'), 'discount_percent', array('class' => 'control-label col-xs-3')); ?> +
      +
      + 'discount_percent', + 'id'=>'discount_percent', + 'class'=>'form-control input-sm', + 'value'=>$person_info->discount_percent) + );?> + % +
      +
      -
      -
      - -
      - lang->line('customers_discount'), 'discount_percent', array('class' => 'control-label col-xs-3')); ?> -
      -
      - 'discount_percent', - 'id'=>'discount_percent', - 'class'=>'form-control input-sm', - 'value'=>$person_info->discount_percent) - );?> - % + +
      + lang->line('customers_company_name'), 'company_name', array('class' => 'control-label col-xs-3')); ?> +
      + 'company_name', + 'id'=>'company_name', + 'class'=>'form-control input-sm', + 'value'=>$person_info->company_name) + );?> +
      -
      -
      - config->item('customer_reward_enable') == TRUE): ?> -
      - lang->line('rewards_package'), 'rewards', array('class'=>'control-label col-xs-3')); ?> -
      - 'form-control')); ?> -
      + +
      + lang->line('customers_account_number'), 'account_number', array('class' => 'control-label col-xs-3')); ?> +
      + 'account_number', + 'id'=>'account_number', + 'class'=>'form-control input-sm', + 'value'=>$person_info->account_number) + );?> +
      +
      + + config->item('customer_reward_enable') == TRUE): ?> +
      + lang->line('rewards_package'), 'rewards', array('class'=>'control-label col-xs-3')); ?> +
      + 'form-control')); ?> +
      +
      + +
      + lang->line('customers_available_points'), 'available_points', array('class' => 'control-label col-xs-3')); ?> +
      + 'available_points', + 'id'=>'available_points', + 'class'=>'form-control input-sm', + 'value'=>$person_info->points, + 'disabled'=>'') + );?> +
      +
      + + +
      + lang->line('customers_taxable'), 'taxable', array('class' => 'control-label col-xs-3')); ?> +
      + taxable == '' ? TRUE : (boolean)$person_info->taxable);?> +
      +
      +
      -
      - lang->line('customers_available_points'), 'available_points', array('class' => 'control-label col-xs-3')); ?> -
      - 'available_points', - 'id'=>'available_points', - 'class'=>'form-control input-sm', - 'value'=>$person_info->points, - 'disabled'=>'') - );?> -
      -
      - -
      - lang->line('customers_taxable'), 'taxable', array('class' => 'control-label col-xs-3')); ?> -
      - taxable == '' ? TRUE : (boolean)$person_info->taxable);?> -
      -
      + +
      +
      +
      + lang->line('customers_total'), 'total', array('class' => 'control-label col-xs-3')); ?> +
      +
      + + config->item('currency_symbol'); ?> + + 'total', + 'id'=>'total', + 'class'=>'form-control input-sm', + 'value'=>to_currency_no_money($stats->total), + 'disabled'=>'') + );?> + + config->item('currency_symbol'); ?> + +
      +
      +
      + +
      + lang->line('customers_max'), 'max', array('class' => 'control-label col-xs-3')); ?> +
      +
      + + config->item('currency_symbol'); ?> + + 'max', + 'id'=>'max', + 'class'=>'form-control input-sm', + 'value'=>to_currency_no_money($stats->max), + 'disabled'=>'') + );?> + + config->item('currency_symbol'); ?> + +
      +
      +
      + +
      + lang->line('customers_min'), 'min', array('class' => 'control-label col-xs-3')); ?> +
      +
      + + config->item('currency_symbol'); ?> + + 'min', + 'id'=>'min', + 'class'=>'form-control input-sm', + 'value'=>to_currency_no_money($stats->min), + 'disabled'=>'') + );?> + + config->item('currency_symbol'); ?> + +
      +
      +
      + +
      + lang->line('customers_average'), 'average', array('class' => 'control-label col-xs-3')); ?> +
      +
      + + config->item('currency_symbol'); ?> + + 'average', + 'id'=>'average', + 'class'=>'form-control input-sm', + 'value'=>to_currency_no_money($stats->average), + 'disabled'=>'') + );?> + + config->item('currency_symbol'); ?> + +
      +
      +
      + +
      + lang->line('customers_quantity'), 'quantity', array('class' => 'control-label col-xs-3')); ?> +
      +
      + 'quantity', + 'id'=>'quantity', + 'class'=>'form-control input-sm', + 'value'=>$stats->quantity, + 'disabled'=>'') + );?> +
      +
      +
      - -
      +
      + lang->line('customers_avg_discount'), 'avg_discount', array('class' => 'control-label col-xs-3')); ?> +
      +
      + 'avg_discount', + 'id'=>'avg_discount', + 'class'=>'form-control input-sm', + 'value'=>$stats->avg_discount, + 'disabled'=>'') + );?> + % +
      +
      +
      +
      +
      + + + +
      +
      +
      + lang->line('customers_mailchimp_status'), 'mailchimp_status', array('class' => 'control-label col-xs-3')); ?> +
      + 'subscribed', + 'unsubscribed' => 'unsubscribed', + 'cleaned' => 'cleaned', + 'pending' => 'pending' + ), + $mailchimp_info['status'], + array('id' => 'mailchimp_status', 'class' => 'form-control input-sm')); ?> +
      +
      + +
      + lang->line('customers_mailchimp_vip'), 'mailchimp_vip', array('class' => 'control-label col-xs-3')); ?> +
      + +
      +
      + +
      + lang->line('customers_mailchimp_member_rating'), 'mailchimp_member_rating', array('class' => 'control-label col-xs-3')); ?> +
      + 'mailchimp_member_rating', + 'class'=>'form-control input-sm', + 'value'=>$mailchimp_info['member_rating'], + 'disabled'=>'') + );?> +
      +
      + +
      + lang->line('customers_mailchimp_activity_total'), 'mailchimp_activity_total', array('class' => 'control-label col-xs-3')); ?> +
      + 'mailchimp_activity_total', + 'class'=>'form-control input-sm', + 'value'=>$mailchimp_activity['total'], + 'disabled'=>'') + );?> +
      +
      + +
      + lang->line('customers_mailchimp_activity_lastopen'), 'mailchimp_activity_lastopen', array('class' => 'control-label col-xs-3')); ?> +
      + 'mailchimp_activity_lastopen', + 'class'=>'form-control input-sm', + 'value'=>$mailchimp_activity['lastopen'], + 'disabled'=>'') + );?> +
      +
      + +
      + lang->line('customers_mailchimp_activity_open'), 'mailchimp_activity_open', array('class' => 'control-label col-xs-3')); ?> +
      + 'mailchimp_activity_open', + 'class'=>'form-control input-sm', + 'value'=>$mailchimp_activity['open'], + 'disabled'=>'') + );?> +
      +
      + +
      + lang->line('customers_mailchimp_activity_click'), 'mailchimp_activity_click', array('class' => 'control-label col-xs-3')); ?> +
      + 'mailchimp_activity_click', + 'class'=>'form-control input-sm', + 'value'=>$mailchimp_activity['click'], + 'disabled'=>'') + );?> +
      +
      + +
      + lang->line('customers_mailchimp_activity_unopen'), 'mailchimp_activity_unopen', array('class' => 'control-label col-xs-3')); ?> +
      + 'mailchimp_activity_unopen', + 'class'=>'form-control input-sm', + 'value'=>$mailchimp_activity['unopen'], + 'disabled'=>'') + );?> +
      +
      + +
      + lang->line('customers_mailchimp_email_client'), 'mailchimp_email_client', array('class' => 'control-label col-xs-3')); ?> +
      + 'mailchimp_email_client', + 'class'=>'form-control input-sm', + 'value'=>$mailchimp_info['email_client'], + 'disabled'=>'') + );?> +
      +
      +
      +
      + +
      + + + +
      lang->line('customers_tax_code'), 'sales_tax_code_name', array('class'=>'control-label col-xs-3')); ?> -
      -
      +
      +
      'sales_tax_code_name', 'id'=>'sales_tax_code_name', @@ -105,59 +362,66 @@ 'value'=>$sales_tax_code_label) ); ?> sales_tax_code);?> - -
      -
      -
      - - -
      +
      + + +