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)); } }