diff --git a/src/Command/ImportCarClubCustomerHubCommand.php b/src/Command/ImportCarClubCustomerHubCommand.php new file mode 100644 index 00000000..15275a1c --- /dev/null +++ b/src/Command/ImportCarClubCustomerHubCommand.php @@ -0,0 +1,392 @@ +em = $em; + $this->loadCustomerTags(); + + parent::__construct(); + } + + protected function configure() + { + $this->setName('customer:importcarclubhub') + ->setDescription('Import assigned hubs for customers for car club.') + ->setHelp('Import assigned hubs for customers for car club.') + ->addArgument('promo_tag', InputArgument::REQUIRED, 'Promo customer tag ID to use.') + ->addArgument('input_file', InputArgument::REQUIRED, 'Path to the input CSV file with the customer and hub info.') + ->addArgument('output_file', InputArgument::REQUIRED, 'Output filename'); + } + + 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->outputCarClubCustomerHubInfo($output_file, $output_info); + + fclose($fh); + + return 0; + } + + protected function processRow($fields, $promo_tag) + { + $contact_number = trim($fields[SELF::F_CONTACT_NUMBER]); + $car_club = trim($fields[self::F_CAR_CLUB]); + + if (empty($contact_number)) + { + // add info to output array + return $this->setOutputInfo($fields, 'NOT ADDED', 'No mobile number', ''); + } + + $hub_name = trim($fields[SELF::F_HUB_NAME]); + + // find hub using name + $hub = $this->findHubByName($hub_name); + + if ($hub == null) + { + // add info to output array + return $this->setOutputInfo($fields, 'NOT ADDED', 'No hub found', ''); + } + + // clean up contact number + $valid_contact_numbers = $this->getValidContactNumbers($contact_number); + + // check customer tag + $cust_tag = $this->findCustomerTag($car_club); + $cust_id = ''; + + // get first customer that matches any of the numbers + $customer = $this->findCustomerByNumbers($valid_contact_numbers); + + // if no customer found, create one and add the hub + if ($customer == null) + { + error_log('Creating customer...'); + + // 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(); + + // create the carclubcustomerhub + $this->createCarClubCustomerHub($fields, $new_cust, $hub); + + // add info to output array + return $this->setOutputInfo($fields, 'CUSTOMER CREATED AND CUSTOMER HUB ADDED', '', $cust_id); + } + + //customer exists, we just need to add the hub and the promo tag + // NOTE: for now, we add the promo tag and the hub + // to the first customer found with the mobile number + $customer->addCustomerTag($promo_tag) + ->addCustomerTag($cust_tag); + + // create the carclubcustomerhub + $this->createCarClubCustomerHub($fields, $customer, $hub); + + return $this->setOutputInfo($fields, 'CUSTOMER HUB ADDED', '', $customer->getID()); + } + + protected function createCarClubCustomerHub($fields, $cust, $hub) + { + $car_club_cust_hub = new CarClubCustomerHub(); + $car_club_cust_hub->setHub($hub); + + $cust->setCarClubCustomerHub($car_club_cust_hub); + + $this->em->persist($car_club_cust_hub); + $this->em->flush(); + } + + protected function createNewCustomer($fields, $contact_numbers, $cust_tag, $promo_tag) + { + $fname = trim($fields[self::F_FIRST_NAME]); + $lname = trim($fields[self::F_LAST_NAME]); + + if (count($contact_numbers) > 0) + $clean_number = $contact_numbers[0]; + else + $clean_number = ''; + + // create new customer + $new_cust = new Customer(); + $new_cust->setFirstName($fname) + ->setLastName($lname) + ->setPhoneMobile($clean_number) + ->setCreateSource('car_club_file') + ->addCustomerTag($cust_tag) + ->addCustomerTag($promo_tag); + + $this->em->persist($new_cust); + $this->em->flush(); + + return $new_cust; + } + + protected function setOutputInfo($fields, $status, $reason, $cust_id) + { + $hub_name = trim($fields[SELF::F_HUB_NAME]); + $hub_address = trim($fields[SELF::F_HUB_ADDRESS]); + $car_club_name = trim($fields[SELF::F_CAR_CLUB]); + $first_name = trim($fields[self::F_FIRST_NAME]); + $last_name = trim($fields[self::F_LAST_NAME]); + $contact_number = trim($fields[SELF::F_CONTACT_NUMBER]); + $battery_size = trim($fields[SELF::F_BATT_SIZE]); + $serial = trim($fields[SELF::F_SERIAL]); + + return [ + $hub_name, + $hub_address, + $car_club_name, + $first_name, + $last_name, + $contact_number, + $battery_size, + $serial, + $status, + $reason, + $cust_id + ]; + } + + protected function outputCarClubCustomerHubInfo($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, [ + 'Hub Name', + 'Hub Address', + 'Car Club Name', + 'First Name', + 'Last Name', + 'Contact Number', + 'Battery Size', + 'Serial', + 'Status', + 'Reason', + 'Customer ID', + ]); + + foreach($entries as $row) + { + fputcsv($fh, $row); + } + + fclose($fh); + + } + protected function findCustomerByNumbers($numbers) + { + error_log(print_r($numbers, true)); + $customer = $this->em->getRepository(Customer::class)->findOneBy(['phone_mobile' => $numbers]); + return $customer; + } + + protected function findHubByName($hub_name) + { + $hname = strtoupper($hub_name); + $hub = $this->em->getRepository(Hub::class)->findOneBy(['name' => $hname]); + return $hub; + } + + 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 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; + } + + // collect 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 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 ""; + } + + 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; + } +} diff --git a/src/Controller/APIController.php b/src/Controller/APIController.php index cfe35773..c0f23ea8 100644 --- a/src/Controller/APIController.php +++ b/src/Controller/APIController.php @@ -2555,13 +2555,39 @@ class APIController extends Controller implements LoggedController return $res->getReturnResponse(); $coordinates = new Point($req->query->get('longitude'), $req->query->get('latitude')); - $nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $em, $map_tools); - if (empty($nearest_hub_slots['hub'])) + // add checking if customer has a pre-registered hub + $cust = $this->session->getCustomer(); + if ($cust == null) { $res->setError(true) - ->setErrorMessage('Thank you for reaching out to us. Due to the General Community Quarantine, our Operations are from 8AM to 6PM only. Please expect a call from us tomorrow and we will assist you with your request. Thank you and stay safe!'); + ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); + } + + // check if customer has customer tag promo + if (($cust->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) || + ($cust->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO'))) + { + // if has customer tag, customer has not availed of promo, get the hub where customer is pre-registered + $car_club_cust_hub = $cust->getCarClubCustomerHub(); + if ($car_club_cust_hub != null) + { + // need to get the rider slots for the pre-registered hub + $hub = $car_club_cust_hub->getHub(); + $nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $em, $map_tools, $hub); + } + } + else + { + $nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $em, $map_tools); + + if (empty($nearest_hub_slots['hub'])) + { + $res->setError(true) + ->setErrorMessage('Thank you for reaching out to us. Due to the General Community Quarantine, our Operations are from 8AM to 6PM only. Please expect a call from us tomorrow and we will assist you with your request. Thank you and stay safe!'); + return $res->getReturnResponse(); + } } // make hub data @@ -3111,6 +3137,26 @@ class APIController extends Controller implements LoggedController if (($hour < 8) || ($hour > 16)) $schedule_choice = false; + // add checking if customer has a pre-registered hub + $cust = $this->session->getCustomer(); + if ($cust == null) + { + $res->setError(true) + ->setErrorMessage('No customer information found'); + return $res->getReturnResponse(); + } + // check if customer has customer tag promo + if (($cust->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) || + ($cust->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO'))) + { + // if has customer tag, customer has not availed of promo, get the hub where customer is pre-registered + $car_club_hub = $cust->getCarClubCustomerHub(); + if ($car_club_hub != null) + { + $schedule_choice = false; + } + } + $data = [ 'display_schedule_choice' => $schedule_choice, ]; @@ -3764,10 +3810,23 @@ class APIController extends Controller implements LoggedController return $selected_rider; } - protected function findAdvanceNearestHubAndSlots(Point $coordinates, EntityManagerInterface $em, MapTools $map_tools) + protected function findAdvanceNearestHubAndSlots(Point $coordinates, EntityManagerInterface $em, MapTools $map_tools, $hub=null) { - // get the nearest 10 hubs $hub_data = []; + + if ($hub != null) + { + // get the slots of hub + $hub_slots = $this->getHubRiderSlots($hub, $em); + + $hub_data = [ + 'hub' => $hub, + 'slots' => $hub_slots, + ]; + return $hub_data; + } + + // get the nearest 10 hubs $nearest_hubs_with_distance = []; $hubs = $map_tools->getClosestOpenHubs($coordinates, 10); diff --git a/src/Entity/CarClubCustomerHub.php b/src/Entity/CarClubCustomerHub.php new file mode 100644 index 00000000..397de567 --- /dev/null +++ b/src/Entity/CarClubCustomerHub.php @@ -0,0 +1,42 @@ +id; + } + + public function setHub(Hub $hub) + { + $this->hub = $hub; + return $this; + } + + public function getHub() + { + return $this->hub; + } + +} diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php index 2257bfb2..4af84d96 100644 --- a/src/Entity/Customer.php +++ b/src/Entity/Customer.php @@ -215,6 +215,13 @@ class Customer */ protected $customer_tags; + // customer car club hub + /** + * One customer has one car club customer info + * @ORM\OneToOne(targetEntity="CarClubCustomerHub") + */ + protected $car_club_customer_hub; + public function __construct() { $this->numbers = new ArrayCollection(); @@ -656,4 +663,15 @@ class Customer $this->customer_tags->removeElement($customer_tag); $customer_tag->removeCustomer($this); } + + public function setCarClubCustomerHub(CarClubCustomerHub $car_club_customer_hub) + { + $this->car_club_customer_hub = $car_club_customer_hub; + return $this; + } + + public function getCarClubCustomerHub() + { + return $this->car_club_customer_hub; + } } diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index e50f12ec..eacf6195 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -446,6 +446,29 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } } + // check service type if new battery + // check if new JO + if (($stype == ServiceType::BATTERY_REPLACEMENT_NEW) && + ($flag_new_jo)) + { + // check if customer has customer tag promo + if (($customer->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) || + ($customer->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO'))) + { + // if has customer tag, customer has not availed of promo, get the hub where customer is pre-registered + $car_club_hub = $customer->getCarClubCustomerHub(); + if ($car_club_hub != null) + { + // assign hub, change the jo status + $hub = $car_club_hub->getHub(); + $jo->setHub($hub) + ->setStatus(JOStatus::RIDER_ASSIGN); + } + } + + } + + // call service to generate job order and invoice $invoice_items = $req->request->get('invoice_items', []); $promo_id = $req->request->get('invoice_promo'); @@ -484,6 +507,22 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } $em->persist($event); + + // check if JOStatus is rider assign + if ($jo->getStatus() == JOStatus::RIDER_ASSIGN) + { + $rider_assign_event = new JOEvent(); + $rider_assign_event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::HUB_ASSIGN) + ->setJobOrder($jo); + + if ($user != null) + { + $rider_assign_event->setUser($user); + } + $em->persist($rider_assign_event); + } + $em->flush(); } }