From 0dee028045cc5c952da944983846175b1d7632f0 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Mon, 25 Apr 2022 08:42:43 +0000 Subject: [PATCH 1/5] Find customer and customer vehicle before warranty creation for warranty upload. #660 --- src/Controller/WarrantyController.php | 40 ++++++++++++++++++++++++++- src/Entity/Warranty.php | 4 +-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Controller/WarrantyController.php b/src/Controller/WarrantyController.php index af75e3b2..9c74dee8 100644 --- a/src/Controller/WarrantyController.php +++ b/src/Controller/WarrantyController.php @@ -9,6 +9,7 @@ use App\Entity\BatteryModel; use App\Entity\BatterySize; use App\Entity\Invoice; use App\Entity\CustomerVehicle; +use App\Entity\Customer; use App\Ramcar\WarrantyClass; use App\Ramcar\WarrantyStatus; @@ -647,6 +648,43 @@ class WarrantyController extends Controller } } + // find customer vehicle using plate number + $vehicle = null; + //$customer_vehicle = $em->getRepository(CustomerVehicle::class)->findOneBy(['plate_number' => $plate_number]); + $cv_query = $em->createQuery('SELECT cv FROM App\Entity\CustomerVehicle cv WHERE cv.plate_number = :plate_number'); + $customer_vehicle = $cv_query->setParameters([ + 'plate_number' => $plate_number, + ]) + ->setMaxResults(1) + ->getOneOrNullResult(); + + if ($customer_vehicle != null) + $vehicle = $customer_vehicle->getVehicle(); + + // find customer using mobile number + $customer = null; + //$customer_results = $em->getRepository(Customer::class)->findBy(['phone_mobile' => $mobile_number]); + $cust_query = $em->createQuery('SELECT c FROM App\Entity\Customer c where c.phone_mobile = :mobile_number'); + $customer_results = $cust_query->setParameters([ + 'mobile_number' => $mobile_number + ]) + ->getResult(); + + foreach ($customer_results as $cust) + { + if ($customer_vehicle != null) + { + if ($cust->getID() == $customer_vehicle->getCustomer()->getID()) + break; + } + } + + if ($customer_vehicle != null) + { + // set customer to associated customer of customer vehicle + $customer = $customer_vehicle->getCustomer(); + } + if (!empty($warr_results)) { foreach($warr_results as $warr) @@ -674,7 +712,7 @@ class WarrantyController extends Controller $source = WarrantySource::BULK_UPLOAD; $wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class, - $user_id, $source); + $user_id, $source, $customer, $vehicle); } } diff --git a/src/Entity/Warranty.php b/src/Entity/Warranty.php index 06c18590..4782a0c4 100644 --- a/src/Entity/Warranty.php +++ b/src/Entity/Warranty.php @@ -538,7 +538,7 @@ class Warranty return $this->file_warr_card; } - public function setVehicle(Vehicle $v) + public function setVehicle(Vehicle $v = null) { $this->vehicle = $v; return $this; @@ -637,7 +637,7 @@ class Warranty return $this->flag_validated; } - public function setCustomer(Customer $customer) + public function setCustomer(Customer $customer = null) { $this->customer = $customer; return $this; From ecff27524a52acde72ee924550168aa398eea4ed Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Thu, 5 May 2022 11:27:35 +0000 Subject: [PATCH 2/5] Create test command for bulk warranty upload optimization. #660 --- src/Command/TestWarrantyUploadCommand.php | 413 ++++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 src/Command/TestWarrantyUploadCommand.php diff --git a/src/Command/TestWarrantyUploadCommand.php b/src/Command/TestWarrantyUploadCommand.php new file mode 100644 index 00000000..e8b976dc --- /dev/null +++ b/src/Command/TestWarrantyUploadCommand.php @@ -0,0 +1,413 @@ +em = $em; + + parent::__construct(); + } + + protected function configure() + { + $this->setName('testwarranty:upload') + ->setDescription('Test bulk warranty upload.') + ->setHelp('Test bulk warranty upload.') + ->addArgument('input_file', InputArgument::REQUIRED, 'Path to the CSV file.') + ->addArgument('output_file', InputArgument::REQUIRED, 'Path to output file.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $csv_file = $input->getArgument('input_file'); + $output_file = $input->getArgument('output_file'); + + // attempt to open file + try + { + $fh = fopen($csv_file, "r"); + } + catch (Exception $e) + { + throw new Exception('The file "' . $csv_file . '" could be read.'); + } + + $output_info = []; + + // loop through rows + // ignore first row since that's header data + $row_num = 1; + + $sql_values = ''; + while (($fields = fgetcsv($fh)) !== false) + { + // process row + $output_info[] = $this->processRow($fields, $sql_values); + + $row_num++; + } + + // check if we have values to insert + if (strlen($sql_values) > 0) + { + $sql_statement = 'INSERT INTO `warranty` (bty_model_id,bty_size_id,serial,warranty_class,plate_number,status,date_create,date_purchase,date_expire,sap_code,first_name,last_name,mobile_number,flag_activated,create_source,vehicle_id,customer_id, create_source) VALUES ' . $sql_values . ';' . "\n"; + $conn = $this->em->getConnection(); + $stmt = $conn->prepare($sql_statement); + $stmt->execute(); + } + + // write to output file + $this->outputWarrantyInfo($output_file, $output_info); + + fclose($fh); + + return 0; + } + + protected function processRow($fields, &$sql_values) + { + // hash the battery table using sap_code as index + $this->populateBatteryIndex(); + + $output_info = []; + + // get necessary fields + $fname = trim($fields[self::F_FNAME]); + $lname = trim($fields[self::F_LNAME]); + $mobile = trim($fields[self::F_MOBILE]); + $serial = trim($fields[self::F_SERIAL]); + $date_purchase = trim($fields[self::F_DATE_PURCHASE]); + $sap_code = trim($fields[self::F_BATT_ID]); + $plate_number = $this->cleanPlateNumber($fields[self::F_PLATE_NUMBER]); + + // validate the necessary fields + $output_info = $this->validateFields($fields); + if (!empty($output_info)) + return $output_info; + + // see if we can get customer id and vehicle id, given the plate number. + // this is not required so if we can't find customer and/or vehicle, + // we still create the warranty + $conn = $this->em->getConnection(); + + // NOTE: what happens if there's more than one result? + // for now, get the first one + $sql = 'SELECT c.id AS c_id, v.id FROM customer_vehicle cv, customer c, vehicle v + WHERE cv.customer_id = c.id AND cv.vehicle_id = v.id AND cv.plate_number = :plate_number LIMIT 1'; + $stmt = $conn->prepare($sql); + $stmt->execute(['plate_number' => $plate_number]); + + $cv_results = $stmt->fetch(); + + $cust_id = null; + $vehicle_id = null; + if (!empty($cv_results)) + { + $cust_id = $cv_results[0]; + $vehicle_id = $cv_results[1]; + } + + // get battery info from hash (battery model id, battery size id, warranty periods (needed for expiration date) + $batt_info = $this->batt_hash[$sap_code]; + $b_id = $batt_info['id']; + $model_id = $batt_info['model_id']; + $size_id = $batt_info['size_id']; + $warr_private = $batt_info['warr_private']; + $warr_commercial = $batt_info['warr_commercial']; + $warr_tnv = $batt_info['warr_tnv']; + + // format purchase date to DateTime and then change the format to Y-m-d + $purchase_date = DateTime::createFromFormat('m/d/y', $date_purchase); + $p_date = $purchase_date->format('Y-m-d'); + + // need to manually create the created date + $date_create = date('Y-m-d H:i:s'); + + // compute expiration date + // TODO: might need checking for what kind of warranty for the warranty period + // by default, we use private + $date_expire = $this->computeDateExpire($p_date, $warr_private); + + $first_name = addslashes($fname); + $last_name = addslashes($lname); + $mobile_number = addslashes($mobile); + + // populate the values string for the values to be inserted into warranty + $value_string = '(' . $model_id . ',' . $size_id . ',\'' . $serial . ',\'' . $warranty_class . '\',\'' + . $plate_number . '\',\'' . WarrantyStatus::ACTIVE . '\',\'' . $date_create . '\',\'' . $p_date + . '\',\'' . $date_expire . ',\'' . $sap_code . ',\'' . $first_name . '\',\'' . $last_name . '\',\'' . $mobile_number . '\',' . 1 . ',' . $vehicle_id . ',' . $cust_id . ',' . WarrantySource::BULK_UPLOAD . '\')'; + + $sql_values = $sql_values . ',' . $value_string; + + // TODO: remove this later, this is for debugging + echo $sql_values; + + return $output_info; + } + + protected function validateFields($fields) + { + $errors = []; + + $serial = trim($fields[self::F_SERIAL]); + $date_purchase = trim($fields[self::F_DATE_PURCHASE]); + $sap_code = trim($fields[self::F_BATT_ID]); + $plate_number = trim($fields[self::F_PLATE_NUMBER]); + + // clean the plate number + $clean_plate = $this->cleanPlateNumber($plate_number); + + // validate the plate number + // (1) plate number should not be empty + if (empty($clean_plate)) + { + $message = 'No plate number.'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + // validate date purchase + // (1) date purchase should not be empty + // (2) date purchase should be of format: m/d/y + if (empty($date_purchase)) + { + $message = 'No date purchase.'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + + $purchase_date = DateTime::createFromFormat('m/d/y', $date_purchase); + if ($purchase_date === false) + { + $message = 'Invalid date format. Date format should be: mm/dd/yy'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + // validate serial + // (1) should not be empty + if (empty($serial)) + { + $message = 'No battery serial number.'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + // validate battery + // (1) should not be empty + // (2) should find battery using sap_code. + if ($empty($sap_code)) + { + $message = 'No battery ID.'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + + if (!(isset($this->batt_hash[$sap_code]))) + { + $message = 'Battery not found.'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + + // check if warranty exists using serial + plate number + $conn = $this->em->getConnection(); + + // find warranties + $sql = 'SELECT w.id FROM warranty w WHERE w.serial = :serial AND w.plate_number = :plate_number'; + $stmt = $conn->prepare($sql); + $stmt->execute([ + 'serial' => $serial, + 'plate_number' => $plate_number, + ]); + + $warr_results = $stmt->fetchAll(); + + if (!empty($warr_results)) + { + $message = 'Warranty already exists for serial and plate number.'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + + return $errors; + } + + protected function computeDateExpire($date_create, $warranty_period) + { + $expire_date = clone $date_create; + $expire_date->add(new DateInterval('P'.$warranty_period.'M')); + return $expire_date; + } + + protected function setOutputInfo($fields, $status, $message) + { + $encoder = trim($fields[self::F_ENCODER]); + $date_encoded = trim($fields[self::F_DATE_ENCODED]); + $fname = trim($fields[self::F_FNAME]); + $lname = trim($fields[self::F_LNAME]); + $email = trim($fields[self::F_EMAIL]); + $address = trim($fields[self::F_ADDRESS]); + $mobile = trim($fields[self::F_MOBILE]); + $telephone = trim($fields[self::F_TELEPHONE]); + $vmake = trim($fields[self::F_VMAKE]); + $vmodel = trim($fields[self::F_VMODEL]); + $year = trim($fields[self::F_MYEAR]); + $serial = trim($fields[self::F_SERIAL]); + $invoice_number = trim($fields[self::F_INVOICE_NUM]); + $date_purchase = trim($fields[self::F_DATE_PURCHASE]); + $dist_name = trim($fields[self::F_DIST_NAME]); + $dist_address = trim($fields[self::F_DIST_ADDRESS]); + $app_type_id = trim($fields[self::F_APP_TYPE_ID]); + $sap_code = trim($fields[self::F_BATT_ID]); + $plate_number = trim($fields[self::F_PLATE_NUMBER]); + $owner_type = trim($fields[self::F_OWNER_TYPE]); + + return [ + $encoder, + $date_encoded, + $fname, + $lname, + $email, + $address, + $mobile, + $telephone, + $vmake, + $vmodel, + $year, + $plate_number, + $serial, + $invoice_number, + $date_purchase, + $dist_name, + $dist_address, + $app_type_id, + $sap_code, + $owner_type, + $status, + $reason, + ]; + } + + protected function outputWarrantyInfo($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, [ + 'Encoded By', + 'Date of Encoded', + 'Owner First Name', + 'Owner Last Name', + 'Owner Email', + 'Owner Address', + 'Owner Mobile', + 'Owner Telephone', + 'Vehicle Make', + 'Vehicle Model', + 'Vehicle Year', + 'Vehicle Plate No.', + 'Battery Serial No.', + 'Battery Sales Invoice No.', + 'Battery Date of Purchase', + 'Distributor Name', + 'Distributor Address', + 'Application Type ID', + 'Battery ID', + 'Ownership Type', + 'Status', + 'Reason', + ]); + + foreach($entries as $row) + { + if ($row != null) + fputcsv($fh, $row); + } + + fclose($fh); + } + + + protected function populateBatteryIndex() + { + $conn = $this->em->getConnection(); + + // get all the batteries + $sql = 'SELECT b.id, b.model_id, b.size_id, b.sap_code, b.warr_private, b.warr_commercial, b.warr_tnv + FROM battery b'; + $stmt = $conn->prepare($sql); + $stmt->execute(); + + // go through the rows + while ($row = $stmt->fetchAll()) + { + // breaking this down for clarity + $battery_id = $row[0]; + $model_id = $row[1]; + $size_id = $row[2]; + $sap_code = $row[3]; + $warr_private = $row[4]; + $warr_commercial = $row[5]; + $warr_tnv = $row[6]; + + $this->batt_hash[$sap_code] = [ + 'id' => $battery_id, + 'model_id' => $model_id, + 'size_id' => $size_id, + 'warr_private' => $warr_private, + 'warr_commercial' => $warr_commercial, + 'warr_tnv' => $warr_tnv, + ]; + } + } + + protected function cleanPlateNumber($plate) + { + return strtoupper(str_replace(' ', '', $plate)); + } + +} From 86ef1ec646ab4371a8059ba391d88c0bbba18bf6 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Fri, 6 May 2022 09:03:22 +0000 Subject: [PATCH 3/5] Fix issues found during testing of bulk upload. #660 --- src/Command/TestWarrantyUploadCommand.php | 140 ++++++++++++++++------ 1 file changed, 104 insertions(+), 36 deletions(-) diff --git a/src/Command/TestWarrantyUploadCommand.php b/src/Command/TestWarrantyUploadCommand.php index e8b976dc..99722b04 100644 --- a/src/Command/TestWarrantyUploadCommand.php +++ b/src/Command/TestWarrantyUploadCommand.php @@ -10,8 +10,11 @@ use Symfony\Component\Console\Output\OutputInterface; use Doctrine\ORM\EntityManagerInterface; use App\Ramcar\WarrantySource; +use App\Ramcar\WarrantyStatus; +use App\Ramcar\WarrantyClass; use DateTime; +use DateInterval; use PDO; class TestWarrantyUploadCommand extends Command @@ -39,6 +42,7 @@ class TestWarrantyUploadCommand extends Command protected $em; protected $batt_hash; + protected $sap_batt_hash; public function __construct(EntityManagerInterface $em) { @@ -58,6 +62,12 @@ class TestWarrantyUploadCommand extends Command protected function execute(InputInterface $input, OutputInterface $output) { + // hash the battery table using sap_code as index + $this->populateBatteryIndex(); + + // hash the sap battery table using id aka sap code as index + $this->populateSAPBatteryIndex(); + $csv_file = $input->getArgument('input_file'); $output_file = $input->getArgument('output_file'); @@ -75,11 +85,18 @@ class TestWarrantyUploadCommand extends Command // loop through rows // ignore first row since that's header data - $row_num = 1; + $row_num = 0; $sql_values = ''; while (($fields = fgetcsv($fh)) !== false) { + // ignore first row since that's the header + if ($row_num == 0) + { + $row_num++; + continue; + } + // process row $output_info[] = $this->processRow($fields, $sql_values); @@ -89,11 +106,14 @@ class TestWarrantyUploadCommand extends Command // check if we have values to insert if (strlen($sql_values) > 0) { - $sql_statement = 'INSERT INTO `warranty` (bty_model_id,bty_size_id,serial,warranty_class,plate_number,status,date_create,date_purchase,date_expire,sap_code,first_name,last_name,mobile_number,flag_activated,create_source,vehicle_id,customer_id, create_source) VALUES ' . $sql_values . ';' . "\n"; + $sql_statement = 'INSERT INTO `warranty` (bty_model_id,bty_size_id,sap_bty_id,serial,warranty_class,plate_number,status,date_create,date_purchase,date_expire,first_name,last_name,mobile_number,flag_activated,vehicle_id,customer_id, create_source) VALUES ' . $sql_values . ';' . "\n"; + + // error_log($sql_statement); + $conn = $this->em->getConnection(); $stmt = $conn->prepare($sql_statement); - $stmt->execute(); - } + $stmt->execute(); + } // write to output file $this->outputWarrantyInfo($output_file, $output_info); @@ -105,8 +125,7 @@ class TestWarrantyUploadCommand extends Command protected function processRow($fields, &$sql_values) { - // hash the battery table using sap_code as index - $this->populateBatteryIndex(); + error_log('Processing warranty with serial ' . trim($fields[self::F_SERIAL])); $output_info = []; @@ -131,7 +150,7 @@ class TestWarrantyUploadCommand extends Command // NOTE: what happens if there's more than one result? // for now, get the first one - $sql = 'SELECT c.id AS c_id, v.id FROM customer_vehicle cv, customer c, vehicle v + $sql = 'SELECT c.id AS c_id, v.id AS v_id FROM customer_vehicle cv, customer c, vehicle v WHERE cv.customer_id = c.id AND cv.vehicle_id = v.id AND cv.plate_number = :plate_number LIMIT 1'; $stmt = $conn->prepare($sql); $stmt->execute(['plate_number' => $plate_number]); @@ -142,8 +161,8 @@ class TestWarrantyUploadCommand extends Command $vehicle_id = null; if (!empty($cv_results)) { - $cust_id = $cv_results[0]; - $vehicle_id = $cv_results[1]; + $cust_id = $cv_results['c_id']; + $vehicle_id = $cv_results['v_id']; } // get battery info from hash (battery model id, battery size id, warranty periods (needed for expiration date) @@ -157,7 +176,6 @@ class TestWarrantyUploadCommand extends Command // format purchase date to DateTime and then change the format to Y-m-d $purchase_date = DateTime::createFromFormat('m/d/y', $date_purchase); - $p_date = $purchase_date->format('Y-m-d'); // need to manually create the created date $date_create = date('Y-m-d H:i:s'); @@ -165,21 +183,35 @@ class TestWarrantyUploadCommand extends Command // compute expiration date // TODO: might need checking for what kind of warranty for the warranty period // by default, we use private - $date_expire = $this->computeDateExpire($p_date, $warr_private); + $warranty_class = WarrantyClass::WTY_PRIVATE; + $date_expire = $this->computeDateExpire($purchase_date, $warr_private); + + // convert all dates to string for the values string for the insert statement + //$str_date_create = $date_create->format('Y-m-d H:i:s'); + $str_date_purchase = $purchase_date->format('Y-m-d H:i:s'); + $str_date_expire = $date_expire->format('Y-m-d H:i:s'); $first_name = addslashes($fname); $last_name = addslashes($lname); $mobile_number = addslashes($mobile); // populate the values string for the values to be inserted into warranty - $value_string = '(' . $model_id . ',' . $size_id . ',\'' . $serial . ',\'' . $warranty_class . '\',\'' - . $plate_number . '\',\'' . WarrantyStatus::ACTIVE . '\',\'' . $date_create . '\',\'' . $p_date - . '\',\'' . $date_expire . ',\'' . $sap_code . ',\'' . $first_name . '\',\'' . $last_name . '\',\'' . $mobile_number . '\',' . 1 . ',' . $vehicle_id . ',' . $cust_id . ',' . WarrantySource::BULK_UPLOAD . '\')'; + $value_string = '(' . $model_id . ',' . $size_id . ',\'' . $sap_code . '\',\'' . $serial . '\',\'' . $warranty_class . '\',\'' + . $plate_number . '\',\'' . WarrantyStatus::ACTIVE . '\',\'' . $date_create . '\',\'' . $str_date_purchase + . '\',\'' . $str_date_expire . '\',\'' . $first_name . '\',\'' . $last_name . '\',\'' . $mobile_number . '\',' . 1 . ',' . $vehicle_id . ',' . $cust_id . ',\'' . WarrantySource::BULK_UPLOAD . '\')'; - $sql_values = $sql_values . ',' . $value_string; + if (strlen($sql_values) == 0) + { + // first entry to insert, should have no comma before + $sql_values = $value_string; + } + else + { + // need to insert a comma after the existing string + $sql_values = $sql_values . ',' . $value_string; + } - // TODO: remove this later, this is for debugging - echo $sql_values; + // error_log($sql_values); return $output_info; } @@ -214,10 +246,10 @@ class TestWarrantyUploadCommand extends Command return $errors; } - $purchase_date = DateTime::createFromFormat('m/d/y', $date_purchase); + $purchase_date = DateTime::createFromFormat('d-M-y', $date_purchase); if ($purchase_date === false) { - $message = 'Invalid date format. Date format should be: mm/dd/yy'; + $message = 'Invalid date format. Date format should be: dd-mon-yy (example: 27-Nov-21)'; $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); return $errors; } @@ -232,7 +264,9 @@ class TestWarrantyUploadCommand extends Command // validate battery // (1) should not be empty // (2) should find battery using sap_code. - if ($empty($sap_code)) + // (3) TODO: depending on Myla's answer. If she says sap_code is id of sap battery + // should find sap battery using sap_code + if (empty($sap_code)) { $message = 'No battery ID.'; $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); @@ -246,6 +280,14 @@ class TestWarrantyUploadCommand extends Command return $errors; } + // TODO: remove this if Myla says sap code is not id of sap battery + if (!(isset($this->sap_batt_hash[$sap_code]))) + { + $message = 'SAP Battery not found.'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + // check if warranty exists using serial + plate number $conn = $this->em->getConnection(); @@ -321,7 +363,7 @@ class TestWarrantyUploadCommand extends Command $sap_code, $owner_type, $status, - $reason, + $message, ]; } @@ -382,25 +424,51 @@ class TestWarrantyUploadCommand extends Command $stmt = $conn->prepare($sql); $stmt->execute(); + $results = $stmt->fetchAll(); + // go through the rows - while ($row = $stmt->fetchAll()) + foreach ($results as $row) { // breaking this down for clarity - $battery_id = $row[0]; - $model_id = $row[1]; - $size_id = $row[2]; - $sap_code = $row[3]; - $warr_private = $row[4]; - $warr_commercial = $row[5]; - $warr_tnv = $row[6]; + $battery_id = $row['id']; + $model_id = $row['model_id']; + $size_id = $row['size_id']; + $sap_code = trim($row['sap_code']); + $warr_private = $row['warr_private']; + $warr_commercial = $row['warr_commercial']; + $warr_tnv = $row['warr_tnv']; - $this->batt_hash[$sap_code] = [ - 'id' => $battery_id, - 'model_id' => $model_id, - 'size_id' => $size_id, - 'warr_private' => $warr_private, - 'warr_commercial' => $warr_commercial, - 'warr_tnv' => $warr_tnv, + if(!empty($sap_code)) + { + $this->batt_hash[$sap_code] = [ + 'id' => $battery_id, + 'model_id' => $model_id, + 'size_id' => $size_id, + 'warr_private' => $warr_private, + 'warr_commercial' => $warr_commercial, + 'warr_tnv' => $warr_tnv, + ]; + } + } + } + + protected function populateSAPBatteryIndex() + { + $conn = $this->em->getConnection(); + + // get all the sap batteries + $sql = 'SELECT sap.id, sap.brand_id, sap.size_id FROM sap_battery sap'; + $stmt = $conn->prepare($sql); + $stmt->execute(); + + $results = $stmt->fetchAll(); + + // go through the rows + foreach ($results as $row) + { + $this->sap_batt_hash[$row['id']] = [ + 'sap_brand' => $row['brand_id'], + 'sap_size' => $row['size_id'], ]; } } From e8670e4b60695292416bff10e5d3e7002814d85a Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Fri, 6 May 2022 10:17:27 +0000 Subject: [PATCH 4/5] Add index for serial. Add additional checks for serial in warranty. #660 --- src/Command/TestWarrantyUploadCommand.php | 31 ++++++++++++++++++----- src/Entity/Warranty.php | 5 +++- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/Command/TestWarrantyUploadCommand.php b/src/Command/TestWarrantyUploadCommand.php index 99722b04..4b216472 100644 --- a/src/Command/TestWarrantyUploadCommand.php +++ b/src/Command/TestWarrantyUploadCommand.php @@ -157,8 +157,8 @@ class TestWarrantyUploadCommand extends Command $cv_results = $stmt->fetch(); - $cust_id = null; - $vehicle_id = null; + $cust_id = 'NULL'; + $vehicle_id = 'NULL'; if (!empty($cv_results)) { $cust_id = $cv_results['c_id']; @@ -238,7 +238,7 @@ class TestWarrantyUploadCommand extends Command } // validate date purchase // (1) date purchase should not be empty - // (2) date purchase should be of format: m/d/y + // (2) date purchase should be of format: d-M-y if (empty($date_purchase)) { $message = 'No date purchase.'; @@ -246,10 +246,10 @@ class TestWarrantyUploadCommand extends Command return $errors; } - $purchase_date = DateTime::createFromFormat('d-M-y', $date_purchase); + $purchase_date = DateTime::createFromFormat('m/d/y', $date_purchase); if ($purchase_date === false) { - $message = 'Invalid date format. Date format should be: dd-mon-yy (example: 27-Nov-21)'; + $message = 'Invalid date format. Date format should be: m/d/y (example: 06/13/16)'; $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); return $errors; } @@ -288,10 +288,11 @@ class TestWarrantyUploadCommand extends Command return $errors; } - // check if warranty exists using serial + plate number + // (1) check if warranty exists using serial + plate number + // (2) check if serial already exists even if for another plate number $conn = $this->em->getConnection(); - // find warranties + // find warranties using serial + plate number $sql = 'SELECT w.id FROM warranty w WHERE w.serial = :serial AND w.plate_number = :plate_number'; $stmt = $conn->prepare($sql); $stmt->execute([ @@ -308,6 +309,22 @@ class TestWarrantyUploadCommand extends Command return $errors; } + // find warranties using serial number alone + $w_sql = 'SELECT w.id FROM warranty w WHERE w.serial = :serial'; + $w_stmt = $conn->prepare($w_sql); + $w_stmt->execute([ + 'serial' => $serial, + ]); + + $w_results = $w_stmt->fetchAll(); + + if (!empty($w_results)) + { + $message = 'Warranty already exists for serial.'; + $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); + return $errors; + } + return $errors; } diff --git a/src/Entity/Warranty.php b/src/Entity/Warranty.php index 4782a0c4..9a35b421 100644 --- a/src/Entity/Warranty.php +++ b/src/Entity/Warranty.php @@ -15,7 +15,10 @@ use Exception; * uniqueConstraints={ * @ORM\UniqueConstraint(columns={"serial"}) * }, - * indexes={@ORM\Index(name="plate_number_idx", columns={"plate_number"})}) + * indexes={ + * @ORM\Index(name="plate_number_idx", columns={"plate_number"}), + @ORM\Index(name="serial_idx", columns={"serial"}) + * }) * ) */ class Warranty From 8401eeb0da11c4a8723c111ba27543a32ab1b90c Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Tue, 10 May 2022 06:28:28 +0000 Subject: [PATCH 5/5] Move the bulk warranty upload to its own service. #660 --- config/services.yaml | 5 + src/Command/TestWarrantyUploadCommand.php | 34 -- src/Controller/WarrantyController.php | 14 +- src/Service/WarrantyBulkUploader.php | 401 ++++++++++++++++++++++ 4 files changed, 415 insertions(+), 39 deletions(-) create mode 100644 src/Service/WarrantyBulkUploader.php diff --git a/config/services.yaml b/config/services.yaml index 0498fcdd..9d1ee14e 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -301,3 +301,8 @@ services: App\Service\HubFilteringGeoChecker: arguments: $geofence_flag: "%env(HUB_GEOFENCE_ENABLE)%" + + # bulk warranty uploader + App\Service\WarrantyBulkUploader: + arguments: + $em: "@doctrine.orm.entity_manager" diff --git a/src/Command/TestWarrantyUploadCommand.php b/src/Command/TestWarrantyUploadCommand.php index 4b216472..1155c6f1 100644 --- a/src/Command/TestWarrantyUploadCommand.php +++ b/src/Command/TestWarrantyUploadCommand.php @@ -65,9 +65,6 @@ class TestWarrantyUploadCommand extends Command // hash the battery table using sap_code as index $this->populateBatteryIndex(); - // hash the sap battery table using id aka sap code as index - $this->populateSAPBatteryIndex(); - $csv_file = $input->getArgument('input_file'); $output_file = $input->getArgument('output_file'); @@ -264,8 +261,6 @@ class TestWarrantyUploadCommand extends Command // validate battery // (1) should not be empty // (2) should find battery using sap_code. - // (3) TODO: depending on Myla's answer. If she says sap_code is id of sap battery - // should find sap battery using sap_code if (empty($sap_code)) { $message = 'No battery ID.'; @@ -280,14 +275,6 @@ class TestWarrantyUploadCommand extends Command return $errors; } - // TODO: remove this if Myla says sap code is not id of sap battery - if (!(isset($this->sap_batt_hash[$sap_code]))) - { - $message = 'SAP Battery not found.'; - $errors = $this->setOutputInfo($fields, 'NOT ADDED', $message); - return $errors; - } - // (1) check if warranty exists using serial + plate number // (2) check if serial already exists even if for another plate number $conn = $this->em->getConnection(); @@ -469,27 +456,6 @@ class TestWarrantyUploadCommand extends Command } } - protected function populateSAPBatteryIndex() - { - $conn = $this->em->getConnection(); - - // get all the sap batteries - $sql = 'SELECT sap.id, sap.brand_id, sap.size_id FROM sap_battery sap'; - $stmt = $conn->prepare($sql); - $stmt->execute(); - - $results = $stmt->fetchAll(); - - // go through the rows - foreach ($results as $row) - { - $this->sap_batt_hash[$row['id']] = [ - 'sap_brand' => $row['brand_id'], - 'sap_size' => $row['size_id'], - ]; - } - } - protected function cleanPlateNumber($plate) { return strtoupper(str_replace(' ', '', $plate)); diff --git a/src/Controller/WarrantyController.php b/src/Controller/WarrantyController.php index 9c74dee8..3e45576d 100644 --- a/src/Controller/WarrantyController.php +++ b/src/Controller/WarrantyController.php @@ -15,8 +15,8 @@ use App\Ramcar\WarrantyClass; use App\Ramcar\WarrantyStatus; use App\Ramcar\WarrantySource; -use App\Service\WarrantyHandler; use App\Service\WarrantyAPILogger; +use App\Service\WarrantyBulkUploader; use Doctrine\ORM\Query; use Doctrine\ORM\EntityManagerInterface; @@ -448,14 +448,13 @@ class WarrantyController extends Controller /** * @Menu(selected="warranty_list") */ - public function uploadSubmit(Request $req, EntityManagerInterface $em, - WarrantyHandler $wh) + public function uploadSubmit(Request $req, WarrantyBulkUploader $warr_uploader) { // retrieve temporary info for file $file = $req->files->get('csv_file'); - // process the csv file - $inv_entries = $this->processWarrantyFile($file, $em, $wh); + // process the csv file. Call the WarrantyBulkUploader service + $inv_entries = $warr_uploader->warrantyUpload($file); $resp = new StreamedResponse(); @@ -465,6 +464,8 @@ class WarrantyController extends Controller // csv output $csv_handle = fopen('php://output', 'w+'); fputcsv($csv_handle, [ + 'Encoded By', + 'Date of Encoded', 'Owner First Name', 'Owner Last Name', 'Owner Email', @@ -483,6 +484,7 @@ class WarrantyController extends Controller 'Application Type ID', 'Battery ID', 'Ownership Type', + 'Status', 'Reason Warranty Not Added', ]); foreach ($inv_entries as $row) @@ -514,6 +516,8 @@ class WarrantyController extends Controller return $resp; } + // NOTE: this is not being called anymore. We are now using the WarrantyBulkUploader service. + // Do we delete this? protected function processWarrantyFile(UploadedFile $csv_file, EntityManagerInterface $em, WarrantyHandler $wh) { diff --git a/src/Service/WarrantyBulkUploader.php b/src/Service/WarrantyBulkUploader.php new file mode 100644 index 00000000..085386a4 --- /dev/null +++ b/src/Service/WarrantyBulkUploader.php @@ -0,0 +1,401 @@ +em = $em; + } + + public function warrantyUpload(UploadedFile $csv_file) + { + // hash the battery table using sap_code as index + $this->populateBatteryIndex(); + + // attempt to open file + try + { + $fh = fopen($csv_file, "r"); + } + catch (Exception $e) + { + throw new Exception('The file "' . $csv_file . '" could be read.'); + } + + $output_info = []; + + // loop through rows + // ignore first row since that's header data + $row_num = 0; + + $sql_values = ''; + while (($fields = fgetcsv($fh)) !== false) + { + // ignore first row since that's the header + if ($row_num == 0) + { + $row_num++; + continue; + } + + // process row + $output_info[] = $this->processRow($fields, $sql_values); + + $row_num++; + } + + // check if we have values to insert + if (strlen($sql_values) > 0) + { + $sql_statement = 'INSERT INTO `warranty` (bty_model_id,bty_size_id,sap_bty_id,serial,warranty_class,plate_number,status,date_create,date_purchase,date_expire,first_name,last_name,mobile_number,flag_activated,vehicle_id,customer_id, create_source) VALUES ' . $sql_values . ';' . "\n"; + + // error_log($sql_statement); + + $conn = $this->em->getConnection(); + $stmt = $conn->prepare($sql_statement); + $stmt->execute(); + } + + fclose($fh); + + // return invalid entries + return $output_info; + } + + protected function processRow($fields, &$sql_values) + { + // error_log('Processing warranty with serial ' . trim($fields[self::F_SERIAL])); + + $output_info = []; + + // get necessary fields + $fname = trim($fields[self::F_FNAME]); + $lname = trim($fields[self::F_LNAME]); + $mobile = trim($fields[self::F_MOBILE]); + $serial = trim($fields[self::F_SERIAL]); + $date_purchase = trim($fields[self::F_DATE_PURCHASE]); + $sap_code = trim($fields[self::F_BATT_ID]); + $plate_number = $this->cleanPlateNumber($fields[self::F_PLATE_NUMBER]); + + // validate the necessary fields + $output_info = $this->validateFields($fields); + if (!empty($output_info)) + return $output_info; + + // see if we can get customer id and vehicle id, given the plate number. + // this is not required so if we can't find customer and/or vehicle, + // we still create the warranty + $conn = $this->em->getConnection(); + + // NOTE: what happens if there's more than one result? + // for now, get the first one + $sql = 'SELECT c.id AS c_id, v.id AS v_id FROM customer_vehicle cv, customer c, vehicle v + WHERE cv.customer_id = c.id AND cv.vehicle_id = v.id AND cv.plate_number = :plate_number LIMIT 1'; + $stmt = $conn->prepare($sql); + $stmt->execute(['plate_number' => $plate_number]); + + $cv_results = $stmt->fetch(); + + $cust_id = 'NULL'; + $vehicle_id = 'NULL'; + if (!empty($cv_results)) + { + $cust_id = $cv_results['c_id']; + $vehicle_id = $cv_results['v_id']; + } + + // get battery info from hash (battery model id, battery size id, warranty periods (needed for expiration date) + $batt_info = $this->batt_hash[$sap_code]; + $b_id = $batt_info['id']; + $model_id = $batt_info['model_id']; + $size_id = $batt_info['size_id']; + $warr_private = $batt_info['warr_private']; + $warr_commercial = $batt_info['warr_commercial']; + $warr_tnv = $batt_info['warr_tnv']; + + // format purchase date to DateTime and then change the format to Y-m-d + $purchase_date = DateTime::createFromFormat('m/d/y', $date_purchase); + + // need to manually create the created date + $date_create = date('Y-m-d H:i:s'); + + // compute expiration date + // TODO: might need checking for what kind of warranty for the warranty period + // by default, we use private + $warranty_class = WarrantyClass::WTY_PRIVATE; + $date_expire = $this->computeDateExpire($purchase_date, $warr_private); + + // convert all dates to string for the values string for the insert statement + //$str_date_create = $date_create->format('Y-m-d H:i:s'); + $str_date_purchase = $purchase_date->format('Y-m-d H:i:s'); + $str_date_expire = $date_expire->format('Y-m-d H:i:s'); + + $first_name = addslashes($fname); + $last_name = addslashes($lname); + $mobile_number = addslashes($mobile); + + // populate the values string for the values to be inserted into warranty + $value_string = '(' . $model_id . ',' . $size_id . ',\'' . $sap_code . '\',\'' . $serial . '\',\'' . $warranty_class . '\',\'' + . $plate_number . '\',\'' . WarrantyStatus::ACTIVE . '\',\'' . $date_create . '\',\'' . $str_date_purchase + . '\',\'' . $str_date_expire . '\',\'' . $first_name . '\',\'' . $last_name . '\',\'' . $mobile_number . '\',' . 1 . ',' . $vehicle_id . ',' . $cust_id . ',\'' . WarrantySource::BULK_UPLOAD . '\')'; + + if (strlen($sql_values) == 0) + { + // first entry to insert, should have no comma before + $sql_values = $value_string; + } + else + { + // need to insert a comma after the existing string + $sql_values = $sql_values . ',' . $value_string; + } + + // error_log($sql_values); + + // if we reach this point, warranty is going to be uploaded + // so we also output to file the warranty that will be uploaded + $output_info = $this->setOutputInfo($fields, 'UPLOADED', ''); + + return $output_info; + } + + protected function validateFields($fields) + { + $errors = []; + + $serial = trim($fields[self::F_SERIAL]); + $date_purchase = trim($fields[self::F_DATE_PURCHASE]); + $sap_code = trim($fields[self::F_BATT_ID]); + $plate_number = trim($fields[self::F_PLATE_NUMBER]); + + // clean the plate number + $clean_plate = $this->cleanPlateNumber($plate_number); + + // validate the plate number + // (1) plate number should not be empty + if (empty($clean_plate)) + { + $message = 'No plate number.'; + $errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message); + return $errors; + } + // validate date purchase + // (1) date purchase should not be empty + // (2) date purchase should be of format: d-M-y + if (empty($date_purchase)) + { + $message = 'No date purchase.'; + $errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message); + return $errors; + } + + $purchase_date = DateTime::createFromFormat('m/d/y', $date_purchase); + if ($purchase_date === false) + { + $message = 'Invalid date format. Date format should be: m/d/y (example: 06/13/16)'; + $errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message); + return $errors; + } + // validate serial + // (1) should not be empty + if (empty($serial)) + { + $message = 'No battery serial number.'; + $errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message); + return $errors; + } + // validate battery + // (1) should not be empty + // (2) should find battery using sap_code. + if (empty($sap_code)) + { + $message = 'No battery ID.'; + $errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message); + return $errors; + } + + if (!(isset($this->batt_hash[$sap_code]))) + { + $message = 'Battery not found.'; + $errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message); + return $errors; + } + + // (1) check if warranty exists using serial + plate number + // (2) check if serial already exists even if for another plate number + $conn = $this->em->getConnection(); + + // find warranties using serial + plate number + $sql = 'SELECT w.id FROM warranty w WHERE w.serial = :serial AND w.plate_number = :plate_number'; + $stmt = $conn->prepare($sql); + $stmt->execute([ + 'serial' => $serial, + 'plate_number' => $plate_number, + ]); + + $warr_results = $stmt->fetchAll(); + + if (!empty($warr_results)) + { + $message = 'Warranty already exists for serial and plate number.'; + $errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message); + return $errors; + } + + // find warranties using serial number alone + $w_sql = 'SELECT w.id FROM warranty w WHERE w.serial = :serial'; + $w_stmt = $conn->prepare($w_sql); + $w_stmt->execute([ + 'serial' => $serial, + ]); + + $w_results = $w_stmt->fetchAll(); + + if (!empty($w_results)) + { + $message = 'Warranty already exists for serial.'; + $errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message); + return $errors; + } + + return $errors; + } + + protected function computeDateExpire($date_create, $warranty_period) + { + $expire_date = clone $date_create; + $expire_date->add(new DateInterval('P'.$warranty_period.'M')); + return $expire_date; + } + + protected function setOutputInfo($fields, $status, $message) + { + $encoder = trim($fields[self::F_ENCODER]); + $date_encoded = trim($fields[self::F_DATE_ENCODED]); + $fname = trim($fields[self::F_FNAME]); + $lname = trim($fields[self::F_LNAME]); + $email = trim($fields[self::F_EMAIL]); + $address = trim($fields[self::F_ADDRESS]); + $mobile = trim($fields[self::F_MOBILE]); + $telephone = trim($fields[self::F_TELEPHONE]); + $vmake = trim($fields[self::F_VMAKE]); + $vmodel = trim($fields[self::F_VMODEL]); + $year = trim($fields[self::F_MYEAR]); + $serial = trim($fields[self::F_SERIAL]); + $invoice_number = trim($fields[self::F_INVOICE_NUM]); + $date_purchase = trim($fields[self::F_DATE_PURCHASE]); + $dist_name = trim($fields[self::F_DIST_NAME]); + $dist_address = trim($fields[self::F_DIST_ADDRESS]); + $app_type_id = trim($fields[self::F_APP_TYPE_ID]); + $sap_code = trim($fields[self::F_BATT_ID]); + $plate_number = trim($fields[self::F_PLATE_NUMBER]); + $owner_type = trim($fields[self::F_OWNER_TYPE]); + + return [ + $encoder, + $date_encoded, + $fname, + $lname, + $email, + $address, + $mobile, + $telephone, + $vmake, + $vmodel, + $year, + $plate_number, + $serial, + $invoice_number, + $date_purchase, + $dist_name, + $dist_address, + $app_type_id, + $sap_code, + $owner_type, + $status, + $message, + ]; + } + + protected function populateBatteryIndex() + { + $conn = $this->em->getConnection(); + + // get all the batteries + $sql = 'SELECT b.id, b.model_id, b.size_id, b.sap_code, b.warr_private, b.warr_commercial, b.warr_tnv + FROM battery b'; + $stmt = $conn->prepare($sql); + $stmt->execute(); + + $results = $stmt->fetchAll(); + + // go through the rows + foreach ($results as $row) + { + // breaking this down for clarity + $battery_id = $row['id']; + $model_id = $row['model_id']; + $size_id = $row['size_id']; + $sap_code = trim($row['sap_code']); + $warr_private = $row['warr_private']; + $warr_commercial = $row['warr_commercial']; + $warr_tnv = $row['warr_tnv']; + + if(!empty($sap_code)) + { + $this->batt_hash[$sap_code] = [ + 'id' => $battery_id, + 'model_id' => $model_id, + 'size_id' => $size_id, + 'warr_private' => $warr_private, + 'warr_commercial' => $warr_commercial, + 'warr_tnv' => $warr_tnv, + ]; + } + } + } + + protected function cleanPlateNumber($plate) + { + return strtoupper(str_replace(' ', '', $plate)); + } + +}