resq/src/Service/WarrantyBulkUploader.php

515 lines
18 KiB
PHP

<?php
namespace App\Service;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use App\Ramcar\WarrantySource;
use App\Ramcar\WarrantyStatus;
use App\Ramcar\WarrantyClass;
use DateTime;
use DateInterval;
use PDO;
class WarrantyBulkUploader
{
const F_ENCODER = 0;
const F_DATE_ENCODED = 1;
const F_FNAME = 2;
const F_LNAME = 3;
const F_EMAIL = 4;
const F_ADDRESS = 5;
const F_MOBILE = 6;
const F_TELEPHONE = 7;
const F_VMAKE = 8;
const F_VMODEL = 9;
const F_MYEAR = 10;
const F_PLATE_NUMBER = 11;
const F_SERIAL = 12;
const F_INVOICE_NUM = 13;
const F_DATE_PURCHASE = 14;
const F_DIST_NAME = 15;
const F_DIST_ADDRESS = 16;
const F_APP_TYPE_ID = 17;
const F_BATT_ID = 18; // sap code in system
const F_OWNER_TYPE = 19;
const FIELD_COUNT = 20;
const DEFAULT_SAP_WARRANTY = 12; // default warranty period for sap batteries
protected $em;
protected $batt_hash;
protected $serial_hash;
protected $sap_batt_hash;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function warrantyUpload(UploadedFile $csv_file)
{
// hash the battery table using sap_code as index
$this->populateBatteryIndex();
// hash the sap battery table
$this->populateSAPBatteryIndex();
// 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);
$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, $row_num)
{
// 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, $row_num);
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'];
}
// initialize bty id, size id and sap bty id to NULL by default
$model_id = 'NULL';
$size_id = 'NULL';
$sap_bty_id = 'NULL';
// get battery info from hash if sap_code in hash
// (battery model id, battery size id, warranty periods (needed for expiration date)
// if not, use info from sap_batt_hash
if (isset($this->batt_hash[$sap_code]))
{
$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'];
// check that sap code is also in sap battery
if (isset($this->sap_batt_hash[$sap_code]))
$sap_bty_id = $sap_code;
}
else
{
// we get id and warranty period
$batt_info = $this->sap_batt_hash[$sap_code];
$sap_bty_id = $sap_code;
$warr = $batt_info['warranty'];
}
// format purchase date to DateTime and then change the format to Y-m-d
$purchase_date = DateTime::createFromFormat('d-M-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
if (isset($this->batt_hash[$sap_code]))
{
// by default, we use private for resq batteries for bulk upload
$warranty_period = $warr_private;
}
else
{
// set warranty class to the default sap battery
$warranty_period = $warr;
}
// by default we set warranty class to private
$warranty_class = WarrantyClass::WTY_PRIVATE;
$date_expire = $this->computeDateExpire($purchase_date, $warranty_period);
// 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);
// if it exists in sap battery hash, we insert sap_code in warranty
// if not, we insert NULL in its place.
// populate the values string for the values to be inserted into warranty
if ($sap_bty_id == 'NULL')
{
// set the sap_bty_id with no quotes since it's null
$value_string = '(' . $model_id . ',' . $size_id . ',' . $sap_bty_id . ',\'' . $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 . '\')';
}
else
{
// set the sap_bty_id with quotes since it's a string
$value_string = '(' . $model_id . ',' . $size_id . ',\'' . $sap_bty_id . '\',\'' . $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', '', $row_num);
return $output_info;
}
protected function validateFields($fields, $row_num)
{
$errors = [];
// check number of fields.
// (1) should match what is in F_TOTAL
if (count($fields) != self::FIELD_COUNT)
{
$message = 'Invalid row.';
$errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message, $row_num);
return $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, $row_num);
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 UPLOADED', $message, $row_num);
return $errors;
}
$purchase_date = DateTime::createFromFormat('d-M-y', $date_purchase);
if ($purchase_date === false)
{
$message = 'Invalid date format. Date format should be: dd-mmm-yyyy (example: 06-Jun-16)';
$errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message, $row_num);
return $errors;
}
// validate serial
// (1) should not be empty
if (empty($serial))
{
$message = 'No battery serial number.';
$errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message, $row_num);
return $errors;
}
// validate battery
// (1) should not be empty
// (2) should find battery using sap_code. But if no battery with sap_code,
// we check sap_battery hash
if (empty($sap_code))
{
$message = 'No battery ID.';
$errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message, $row_num);
return $errors;
}
if (!(isset($this->batt_hash[$sap_code])))
{
// check sap_battery_hash for sap battery. We need to at least set either:
// (1) bty model + bty size or (2) sap bty id.
if (!(isset($this->sap_batt_hash[$sap_code])))
{
$message = 'Battery not found.';
$errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message, $row_num);
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, $row_num);
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, $row_num);
return $errors;
}
// check if serial is a duplicate of serial within the file
// add serial to hash, in case csv file has duplicate serials in it.
if (!isset($this->serial_hash[$serial]))
$this->serial_hash[$serial] = $serial;
else
{
$message = 'Duplicate serial in file.';
$errors = $this->setOutputInfo($fields, 'NOT UPLOADED', $message, $row_num);
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, $row_num)
{
if (count($fields) != self::FIELD_COUNT)
{
// since we don't know field might be missing, we just output status and reason
return [
$row_num,'','','','','','','','','','','','','','','','','','','','', $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 [
$row_num,
$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 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)
{
// set warranty period to default warranty period for SAP batteries
$this->sap_batt_hash[$row['id']] = [
'sap_brand' => $row['brand_id'],
'sap_size' => $row['size_id'],
'warranty' => self::DEFAULT_SAP_WARRANTY,
];
}
}
protected function cleanPlateNumber($plate)
{
return strtoupper(str_replace(' ', '', $plate));
}
}