diff --git a/src/Command/ImportCarClubCustomerDataCommand.php b/src/Command/ImportCarClubCustomerDataCommand.php new file mode 100644 index 00000000..49ef7679 --- /dev/null +++ b/src/Command/ImportCarClubCustomerDataCommand.php @@ -0,0 +1,436 @@ +em = $em; + $this->loadCustomerTags(); + + parent::__construct(); + } + + protected function configure() + { + $this->setName('customer:createfromcarclub') + ->setDescription('Create customers from car club file.') + ->setHelp('Creates customers from car club file.') + ->addArgument('promo_tag', InputArgument::REQUIRED, 'Promo customer tag ID to use.') + ->addArgument('input_file', InputArgument::REQUIRED, 'Path to the output CSV file with the car club info.') + ->addArgument('output_file', InputArgument::REQUIRED, 'Output filename'); + } + + protected function getValidContactNumbers($contact_number) + { + // check contact number if mobile or not + // check for spaces, slash, and forward slash + $contact_num_array = []; + if (preg_match('/[\\\s\/]/', $contact_number)) + { + $contact_num_array = preg_split('/[\\\s\/]/', $contact_number); + } + else + { + // only one mobile number + $contact_num_array[] = $contact_number; + } + + // collet clean numbers + $clean_nums = []; + foreach ($contact_num_array as $contact_num) + { + $c_num = trim($contact_num); + + // clean the numbers + $cleaned_number = $this->normalizeContactNumber($c_num); + + // not a blank, save it + if ($cleaned_number != '') + { + $clean_nums[] = $cleaned_number; + } + } + + return $clean_nums; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $csv_file = $input->getArgument('input_file'); + $output_file = $input->getArgument('output_file'); + $promo_tag_id = $input->getArgument('promo_tag'); + + // fetch promo tag + $promo_tag = $this->em->getRepository(CustomerTag::class)->find($promo_tag_id); + if ($promo_tag == null) + { + throw new Exception('Could not find promo tag - ' . $promo_tag_id); + } + + // attempt to open file + try + { + $fh = fopen($csv_file, "r"); + } + catch (Exception $e) + { + throw new Exception('The file "' . $csv_file . '" could be opened.'); + } + + $row_num = 1; + $output_info = []; + while (($fields = fgetcsv($fh)) !== false) + { + // ignore first row + if ($row_num == 1) + { + $row_num++; + continue; + } + + // process row + $output_info[] = $this->processRow($fields, $promo_tag); + + $row_num++; + } + + // write to output file + $this->outputCustomerInfo($output_file, $output_info); + + fclose($fh); + return 0; + } + + protected function setOutputInfo($fields, $status, $reason, $cust_id) + { + $timestamp = trim($fields[self::F_TIMESTAMP]); + $mname = trim($fields[self::F_MIDDLE_NAME]); + $birthdate = trim($fields[self::F_BIRTHDATE]); + $address = trim($fields[self::F_ADDRESS]); + $city = trim($fields[self::F_CITY]); + $region = trim($fields[self::F_REGION]); + $car_club = trim($fields[self::F_CAR_CLUB]); + $position = trim($fields[self::F_POSITION]); + $member_number = trim($fields[self::F_MEMBER_NUM]); + $vehicle = trim($fields[self::F_VEHICLE]); + $vehicle_excel = trim($fields[self::F_VEHICLE_EXCEL]); + $batt_size = trim($fields[self::F_BATT_SIZE]); + $replace_sked = trim($fields[self::F_REPLACE_SKED]); + $dealer_visit = trim($fields[self::F_DEALER_VISIT]); + $bwi_location = trim($fields[self::F_BWI_LOCATION]); + $sap_code = trim($fields[self::F_SAP_CODE]); + $sku = trim($fields[self::F_SKU]); + $qty = trim($fields[self::F_QTY]); + $fname = trim($fields[self::F_FIRST_NAME]); + $lname = trim($fields[self::F_LAST_NAME]); + $dpa = trim($fields[self::F_DPA]); + $contact_number = trim($fields[SELF::F_CONTACT_NUM]); + + return [ + $timestamp, + $dpa, + $fname, + $mname, + $lname, + $birthdate, + $address, + $city, + $region, + $car_club, + $position, + $member_number, + $vehicle, + $vehicle_excel, + $batt_size, + $replace_sked, + $dealer_visit, + $contact_number, + $bwi_location, + $sap_code, + $sku, + $qty, + $status, + $reason, + $cust_id + ]; + } + + protected function createNewCustomer($fields, $contact_numbers, $cust_tag, $promo_tag) + { + $fname = trim($fields[self::F_FIRST_NAME]); + $lname = trim($fields[self::F_LAST_NAME]); + $dpa = trim($fields[self::F_DPA]); + + if (count($contact_numbers) > 0) + $clean_number = $contact_numbers[0]; + else + $clean_number = ''; + + // check dpa + $is_dpa = false; + if (strtoupper($dpa) == 'YES') + $is_dpa = true; + + // create new customer + $new_cust = new Customer(); + $new_cust->setFirstName($fname) + ->setLastName($lname) + ->setPhoneMobile($clean_number) + ->setDpaConsent($is_dpa) + ->setCreateSource('car_club_file') + ->addCustomerTag($cust_tag) + ->addCustomerTag($promo_tag); + + $this->em->persist($new_cust); + $this->em->flush(); + + return $new_cust; + } + + protected function processRow($fields, $promo_tag) + { + $contact_number = trim($fields[SELF::F_CONTACT_NUM]); + $car_club = trim($fields[self::F_CAR_CLUB]); + + // clean up contact number + $valid_contact_numbers = $this->getValidContactNumbers($contact_number); + + // NOTE: commenting this out because we want to add customers without mobile number + /* + // QUESTION: do we need this? + // QUESTION: why are we not adding those without contact numbers? Add customer, leave contact number blank was what was agreed, right? + if (count($valid_contact_numbers) <= 0) + { + // add info to output array + return $this->setOutputInfo($fields, 'NOT CREATED', 'Missing contact number', 0); + } + */ + + // check customer tag + $cust_tag = $this->findCustomerTag($car_club); + $cust_id = ''; + + // give me first customer that matches any of the numbers + $customer = $this->findCustomerByNumbers($valid_contact_numbers); + + // if no customer found, create one + if ($customer == null) + { + error_log('Creating customer...'); + error_log('cust tag id ' . $cust_tag->getID()); + + // create new customer + $new_cust = $this->createNewCustomer($fields, $valid_contact_numbers, $cust_tag, $promo_tag); + + // get customer id of new customer here + $cust_id = $new_cust->getID(); + + // add info to output array + return $this->setOutputInfo($fields, 'CREATED', '', $cust_id); + } + + error_log('Updating customer...'); + // add customer tag to existing customer + + $cust_id = $customer->getID(); + // need to check if customer tag already exists for customer + $tag_exists = $customer->getCustomerTag($cust_tag->getID()); + if ($tag_exists == null) + { + $customer->addCustomerTag($cust_tag) + ->addCustomerTag($promo_tag); + $this->em->flush(); + + // add info to output array + return $this->setOutputInfo($fields, 'UPDATED', '', $cust_id); + } + + return $this->setOutputInfo($fields, 'NOT CREATED/UPDATED', 'Customer and tag already exist', $cust_id); + } + + protected function findCustomerByNumbers($numbers) + { + error_log(print_r($numbers, true)); + $customer = $this->em->getRepository(Customer::class)->findOneBy(['phone_mobile' => $numbers]); + return $customer; + } + + protected function outputCustomerInfo($output_file, $entries) + { + try + { + $fh = fopen($output_file, "w"); + } + catch (Exception $e) + { + throw new Exception('The file "' . $report_file . '" could be opened.'); + } + + // write the headers + fputcsv($fh, [ + 'Timestamp', + 'I have read and understood and agreed to the confidentiality of this form request', + 'First Name', + 'Middle Name', + 'Last Name', + 'Birth date', + 'Address', + 'City', + 'Region', + 'Car Club/Organization', + 'Position in the organization', + 'Official Member Number (type NA if not applicable)', + 'Vehicles you own? Year - Make -Model - Plate/Conduction number (ex 2017 NISSAN NAVARA ABC 1234) you can indicate all your vehicles', + 'If your were given a chance to EXperience the New Motolite Excel, Choose 1 vehicle and it\'s plate/conduction number', + 'What is the Battery size of the vehicle you with to EXperience the New Motolite Excel', + 'Given a specific date, when can you schedule your replacement?', + 'Will you be able to visit our dealer that we will assign nearest to you?', + 'Contact Number', + 'BWI LOCATION', + 'SAP CODE', + 'SKU', + 'QTY', + 'Status', + 'Reason', + 'Customer ID', + ]); + + foreach($entries as $row) + { + fputcsv($fh, $row); + } + + fclose($fh); + } + + protected function loadCustomerTags() + { + $this->cust_tag_hash = []; + + $cust_tags = $this->em->getRepository(CustomerTag::class)->findAll(); + foreach ($cust_tags as $cust_tag) + { + $tag_id = $cust_tag->getID(); + $this->cust_tag_hash[$tag_id] = $cust_tag; + } + } + + protected function normalizeClubName($name) + { + // uppercase the name + $clean_name = trim(strtoupper($name)); + + // remove apostrophes + $clean_name = str_replace("'", '', $clean_name); + + // replace all special characters except ampersand (&) with whitespace + $clean_name = trim(preg_replace('/[^A-Za-z0-9&]/', ' ', $clean_name)); + + // replace spaces with underscore (_) + $clean_name = preg_replace('/\s+/', '_', $clean_name); + + // prepend 'TAG_' + $tag_name = 'TAG_' . $clean_name; + + return $tag_name; + } + + protected function findCustomerTag($car_club) + { + // find the customer tag for club + $tag_name = $this->normalizeClubName($car_club); + error_log($tag_name); + if (isset($this->cust_tag_hash[$tag_name])) + return $this->cust_tag_hash[$tag_name]; + + error_log('customer tag not in hash...'); + // create the customer tag + $new_cust_tag = new CustomerTag(); + $tag_details = json_decode('{"type":"car club"}', true); + $new_cust_tag->setID($tag_name) + ->setName(strtoupper($car_club)) + ->setTagDetails($tag_details); + $this->em->persist($new_cust_tag); + + // need to flush before adding to hash + $this->em->flush(); + + $this->cust_tag_hash[$tag_name] = $new_cust_tag; + + return $new_cust_tag; + } + + protected function normalizeContactNumber($c_num) + { + // remove any non digit character from string + $clean_number = preg_replace('~\D~', '', $c_num); + error_log('cleaned ' . $clean_number); + + // does it fit our 09XXXXXXXXX pattern? + if (preg_match('/^09[0-9]{9}$/', $clean_number)) + { + // remove first '0' + $clean_number = substr($clean_number, 1); + error_log("CONVERTED TO $clean_number"); + return $clean_number; + } + // does it fit our 63XXXXXXXXXX pattern? + else if (preg_match('/^63[0-9]{10}$/', $clean_number)) + { + // remove the 63 + $clean_number = substr($clean_number, 2); + error_log("CONVERTED TO $clean_number"); + return $clean_number; + } + // does it fit our 9XXXXXXXXX pattern? + else if (preg_match('/^9[0-9]{9}$/', $clean_number)) + { + error_log("already cleaned $clean_number"); + return $clean_number; + } + + return ""; + } +} diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php index 2f593e00..2257bfb2 100644 --- a/src/Entity/Customer.php +++ b/src/Entity/Customer.php @@ -210,7 +210,7 @@ class Customer // customer tags /** - * @ORM\ManyToMany(targetEntity="CustomerTag", inversedBy="customers") + * @ORM\ManyToMany(targetEntity="CustomerTag", inversedBy="customers", indexBy="id") * @ORM\JoinTable(name="customer_customer_tags") */ protected $customer_tags; @@ -643,6 +643,14 @@ class Customer return $this->customer_tags; } + public function getCustomerTag($id) + { + if (isset($this->customer_tags[$id])) + return $this->customer_tags[$id]; + + return null; + } + public function removeCustomerTag(CustomerTag $customer_tag) { $this->customer_tags->removeElement($customer_tag); diff --git a/src/Entity/CustomerTag.php b/src/Entity/CustomerTag.php index 4ce840ab..57d9a22f 100644 --- a/src/Entity/CustomerTag.php +++ b/src/Entity/CustomerTag.php @@ -15,14 +15,14 @@ class CustomerTag { /** * @ORM\Id - * @ORM\Column(type="string", length=80, nullable=false, unique=true) + * @ORM\Column(type="string", length=200, nullable=false, unique=true) * @Assert\NotBlank() */ protected $id; // name of tag /** - * @ORM\Column(type="string", length=80) + * @ORM\Column(type="string", length=200) * @Assert\NotBlank() */ protected $name;