denyAccessUnlessGranted('warranty.list', null, 'No access.'); return $this->render('warranty/list.html.twig'); } public function rows(Request $req) { $this->denyAccessUnlessGranted('warranty.list', null, 'No access.'); // get query builder $qb = $this->getDoctrine() ->getRepository(Warranty::class) ->createQueryBuilder('q'); // get datatable params $datatable = $req->request->get('datatable'); // count total records $tquery = $qb->select('COUNT(q)'); // add filters to count query $this->setQueryFilters($datatable, $tquery); $total = $tquery->getQuery() ->getSingleScalarResult(); // get current page number $page = $datatable['pagination']['page'] ?? 1; $perpage = $datatable['pagination']['perpage']; $offset = ($page - 1) * $perpage; // add metadata $meta = [ 'page' => $page, 'perpage' => $perpage, 'pages' => ceil($total / $perpage), 'total' => $total, 'sort' => 'asc', 'field' => 'id' ]; // build query $query = $qb->select('q'); // add filters to query $this->setQueryFilters($datatable, $query); // check if sorting is present, otherwise use default if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) { $order = $datatable['sort']['sort'] ?? 'asc'; $query->orderBy('q.' . $datatable['sort']['field'], $order); } else { $query->orderBy('q.id', 'asc'); } // get rows for this page $obj_rows = $query->setFirstResult($offset) ->setMaxResults($perpage) ->getQuery() ->getResult(); // process rows $rows = []; foreach ($obj_rows as $orow) { // add row data $row['id'] = $orow->getID(); $row['serial'] = $orow->getSerial(); $row['plate_number'] = $orow->getPlateNumber(); $row['warranty_class'] = $orow->getWarrantyClass(); $row['status'] = $orow->getStatus(); $row['is_activated'] = $orow->isActivated(); // add row metadata $row['meta'] = [ 'update_url' => '', ]; // add crud urls if ($this->isGranted('warranty.update')) $row['meta']['update_url'] = $this->generateUrl('warranty_update', ['id' => $row['id']]); $rows[] = $row; } // response return $this->json([ 'meta' => $meta, 'data' => $rows ]); } /** * @Menu(selected="warranty_list") */ public function addForm() { $this->denyAccessUnlessGranted('warranty.add', null, 'No access.'); $params['obj'] = new Warranty(); $params['mode'] = 'create'; // get dropdown parameters $this->fillDropdownParameters($params); // response return $this->render('warranty/form.html.twig', $params); } public function addSubmit(Request $req, ValidatorInterface $validator, WarrantyAPILogger $logger) { $this->denyAccessUnlessGranted('warranty.add', null, 'No access.'); // create new row $em = $this->getDoctrine()->getManager(); $obj = new Warranty(); $date_purchase = DateTime::createFromFormat('d M Y', $req->request->get('date_purchase')); $date_claim = DateTime::createFromFormat('d M Y', $req->request->get('date_claim')); $date_expire = DateTime::createFromFormat('d M Y', $req->request->get('date_expire')); // set and save values $obj->setSerial($req->request->get('serial')) ->setWarrantyClass($req->request->get('warranty_class')) ->setFirstName($req->request->get('first_name')) ->setLastName($req->request->get('last_name')) ->setMobileNumber($req->request->get('mobile_number')) ->setDatePurchase($date_purchase) ->setClaimedFrom($req->request->get('claim_from')) ->setStatus($req->request->get('status')); if ($date_claim) { $obj->setDateClaim($date_claim); } if ($date_expire) { $obj->setDateExpire($date_expire); } // custom validation for battery model // check if battery model is blank $bmodel = $req->request->get('battery_model'); if (!empty($bmodel)) { $model = $em->getRepository(BatteryModel::class) ->find($req->request->get('battery_model')); if (empty($model)) $error_array['battery_model'] = 'Invalid model selected.'; else $obj->setBatteryModel($model); } else $obj->setBatteryModel(NULL); // custom validation for battery size // check if battery size is blank $bsize = $req->request->get('battery_size'); if (!empty($bsize)) { $size = $em->getRepository(BatterySize::class) ->find($req->request->get('battery_size')); if (empty($size)) $error_array['battery_size'] = 'Invalid size selected.'; else $obj->setBatterySize($size); } else $obj->setBatterySize(NULL); // custom validation for SAP battery // check if sap battery is blank $sap_battery = $req->request->get('sap_battery'); if (!empty($sap_battery)) { $sap = $em->getRepository(SAPBattery::class) ->find($req->request->get('sap_battery')); if (empty($sap)) $error_array['sap_battery'] = 'Invalid SAP battery selected.'; else $obj->setSAPBattery($sap); } else $obj->setSAPBattery(NULL); // validate $errors = $validator->validate($obj); $cleaned_plate_number = Warranty::cleanPlateNumber($req->request->get('plate_number')); if (!$cleaned_plate_number) { $error_array['plate_number'] = 'Invalid plate number specified.'; } $obj->setPlateNumber($cleaned_plate_number); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } // check if any errors were found if (!empty($error_array)) { // return validation failure response return $this->json([ 'success' => false, 'errors' => $error_array ], 422); } else { // validated! save the entity $em->persist($obj); $em->flush(); // log warranty creation $action = 'create'; $user_id = $this->getUser()->getUsername(); $source = WarrantySource::ADMIN_PANEL; $log_data = [ 'serial' => $req->request->get('serial'), 'warranty_class' => $req->request->get('warranty_class'), 'first_name' => $req->request->get('first_name'), 'last_name' => $req->request->get('last_name'), 'mobile_number' => $req->request->get('mobile_number'), 'date_purchase' => $req->request->get('date_purchase'), 'claim_from' => $req->request->get('claim_from'), 'status' => $req->request->get('status'), 'date_claim' => $req->request->get('date_claim'), 'date_expire' => $req->request->get('date_expire'), 'battery_model' => $req->request->get('battery_model'), 'battery_size' => $req->request->get('battery_size'), 'sap_battery' => $req->request->get('sap_battery'), 'plate_number' => $req->request->get('plate_number'), ]; $logger->logWarrantyInfo($log_data, '', $user_id, $action, $source); // return successful response return $this->json([ 'success' => 'Changes have been saved!' ]); } } /** * @Menu(selected="warranty_list") */ public function updateForm($id) { $this->denyAccessUnlessGranted('warranty.update', null, 'No access.'); $params['mode'] = 'update'; // get row data $em = $this->getDoctrine()->getManager(); $obj = $em->getRepository(Warranty::class)->find($id); // make sure this row exists if (empty($obj)) throw $this->createNotFoundException('The item does not exist'); $params['obj'] = $obj; // get dropdown parameters $this->fillDropdownParameters($params); // response return $this->render('warranty/form.html.twig', $params); } public function updateSubmit(Request $req, ValidatorInterface $validator, $id) { $this->denyAccessUnlessGranted('warranty.update', null, 'No access.'); // get row data $em = $this->getDoctrine()->getManager(); $obj = $em->getRepository(Warranty::class)->find($id); // make sure this row exists if (empty($obj)) throw $this->createNotFoundException('The item does not exist'); $date_purchase = DateTime::createFromFormat('d M Y', $req->request->get('date_purchase')); $date_claim = DateTime::createFromFormat('d M Y', $req->request->get('date_claim')); $date_expire = DateTime::createFromFormat('d M Y', $req->request->get('date_expire')); // set and save values $obj->setSerial($req->request->get('serial')) ->setWarrantyClass($req->request->get('warranty_class')) ->setFirstName($req->request->get('first_name')) ->setLastName($req->request->get('last_name')) ->setMobileNumber($req->request->get('mobile_number')) ->setDatePurchase($date_purchase) ->setClaimedFrom($req->request->get('claim_from')) ->setStatus($req->request->get('status')); if ($date_claim) { $obj->setDateClaim($date_claim); } if ($date_expire) { $obj->setDateExpire($date_expire); } // custom validation for battery model // check if battery model is blank $bmodel = $req->request->get('battery_model'); if (!empty($bmodel)) { $model = $em->getRepository(BatteryModel::class) ->find($req->request->get('battery_model')); if (empty($model)) $error_array['battery_model'] = 'Invalid model selected.'; else $obj->setBatteryModel($model); } else $obj->setBatteryModel(NULL); // custom validation for battery size // check if battery size is blank $bsize = $req->request->get('battery_size'); if (!empty($bsize)) { $size = $em->getRepository(BatterySize::class) ->find($req->request->get('battery_size')); if (empty($size)) $error_array['battery_size'] = 'Invalid size selected.'; else $obj->setBatterySize($size); } else $obj->setBatterySize(NULL); // custom validation for SAP battery // check if sap battery is blank $sap_battery = $req->request->get('sap_battery'); if (!empty($sap_battery)) { $sap = $em->getRepository(SAPBattery::class) ->find($req->request->get('sap_battery')); if (empty($sap)) $error_array['sap_battery'] = 'Invalid SAP battery selected.'; else $obj->setSAPBattery($sap); } else $obj->setSAPBattery(NULL); // validate $errors = $validator->validate($obj); $cleaned_plate_number = Warranty::cleanPlateNumber($req->request->get('plate_number')); if (!$cleaned_plate_number) { $error_array['plate_number'] = 'Invalid plate number specified.'; } $obj->setPlateNumber($cleaned_plate_number); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } // check if any errors were found if (!empty($error_array)) { // return validation failure response return $this->json([ 'success' => false, 'errors' => $error_array ], 422); } else { // validated! save the entity $em->persist($obj); $em->flush(); // return successful response return $this->json([ 'success' => 'Changes have been saved!' ]); } } /** * @Menu(selected="warranty_list") */ public function uploadForm() { $this->denyAccessUnlessGranted('warranty.upload', null, 'No access.'); return $this->render('warranty/upload.form.html.twig'); } /** * @Menu(selected="warranty_list") */ public function uploadSubmit(Request $req, EntityManagerInterface $em, WarrantyHandler $wh) { // retrieve temporary info for file $file = $req->files->get('csv_file'); // process the csv file $inv_entries = $this->processWarrantyFile($file, $em, $wh); $resp = new StreamedResponse(); if (count($inv_entries) > 0) { $resp->setCallback(function() use($inv_entries) { // csv output $csv_handle = fopen('php://output', 'w+'); fputcsv($csv_handle, [ 'Owner First Name', 'Owner Last Name', 'Owner Email', 'Owner Address', 'Owner Mobile', 'Owner Telephone', 'Vehicle Make', 'Vehicle Model', 'Vehicle Year', 'Vehicle Plate Number', 'Battery Serial Number', 'Battery Sales Invoice', 'Battery Date of Purchase', 'Distributor Name', 'Distributor Address', 'Application Type ID', 'Battery ID', 'Ownership Type', 'Reason Warranty Not Added', ]); foreach ($inv_entries as $row) { fputcsv($csv_handle, $row); } fclose($csv_handle); }); } else { $resp->setCallback(function() { // csv output $csv_handle = fopen('php://output', 'w+'); fputcsv($csv_handle, ['No Invalid Warranties']); fclose($csv_handle); }); } $filename = 'invalid_warranties' . '.csv'; $resp->headers->set('Content-Type', 'text/csv; charset=utf-8'); $resp->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '"'); $resp->setStatusCode(200); return $resp; } protected function processWarrantyFile(UploadedFile $csv_file, EntityManagerInterface $em, WarrantyHandler $wh) { // attempt to open file try { $fh = fopen($csv_file, "r"); } catch (Exception $e) { throw new Exception('The file "' . $csv_file . '" could be read.'); } // loop through the rows // 0 - Owner First Name // 1 - Owner Last Name // 2 - Owner Email // 3 - Owner Address // 4 - Owner Mobile // 5 - Owner Telephone // 6 - Vehicle Make // 7 - Vehicle Model // 8 - Vehicle Year // 9 - Vehicle Plate Number // 10 - Battery Serial Number // 11 - Battery Sales Invoice // 12 - Battery Date of Purchase // 13 - Distributor Name // 14 - Distributor Address // 15 - Application Type ID // 16 - Battery ID // 17 - Ownership Type $row_num = 0; $invalid_entries = []; while (($fields = fgetcsv($fh)) !== false) { // start processing at row 1, not 0 if ($row_num < 1) { $row_num++; continue; } // get the data $first_name = trim($fields[0]); $last_name = trim($fields[1]); $mobile_number = trim($fields[4]); $plate = trim($fields[9]); $serial = trim($fields[10]); $purchase_date = trim($fields[12]); $battery_id = trim($fields[16]); $batt_invoice = trim($fields[11]); $plate_number = $wh->cleanPlateNumber($plate); // check if purchase_date or plate_number or serial is empty or if // purchase date is valid $date_purchase = DateTime::createFromFormat('d-M-y', $purchase_date); if (empty($purchase_date) || empty($plate_number) || empty($serial) || ($date_purchase == false)) { $reason = ''; if (empty($plate_number)) $reason = $reason . self::PLATE_NUM_EMPTY . ' '; if (empty($serial)) $reason = $reason . self::SERIAL_EMPTY . ' '; if (empty($purchase_date)) $reason = $reason . self::PURCHASE_DATE_EMPTY . ' '; else { if ($date_purchase == false) $reason = $reason . self::PURCHASE_DATE_INVALID . ' '; } // add to invalid_entries $invalid_entries[] = [ 'owner_first_name' => $first_name, 'owner_last_name' => $last_name, 'owner_email' => trim($fields[2]), 'owner_address' => trim($fields[3]), 'owner_mobile' => $mobile_number, 'owner_telephone' => trim($fields[5]), 'vehicle_make' => trim($fields[6]), 'vehicle_model' => trim($fields[7]), 'vehicle_year' => trim($fields[8]), 'vehicle_plate_number' => $plate_number, 'battery_serial_number' => $serial, 'battery_sales_invoice' => $batt_invoice, 'battery_date_purchase' => $purchase_date, 'distributor_name' => trim($fields[13]), 'distributor_address' => trim($fields[14]), 'application_type_id' => trim($fields[15]), 'battery_id' => $battery_id, 'ownership_type' => trim($fields[17]), 'reason' => $reason, ]; } else { // additional validation // check if serial number and plate number already exists $warr_results = $em->getRepository(Warranty::class)->findBy(['serial' => $serial, 'plate_number' => $plate_number]); // get battery via the invoice because battery_id doesn't match what's in the live data // get job order via invoice to get the warranty class $warranty_class = ''; $batt_list = array(); // find invoice $invoice = $em->getRepository(Invoice::class)->find($batt_invoice); if (!empty($invoice)) { // get job order $jo = $invoice->getJobOrder(); // get warranty class $warranty_class = $jo->getWarrantyClass(); // get battery $invoice_items = $invoice->getItems(); foreach ($invoice_items as $item) { $battery = $item->getBattery(); if ($battery != null) { $batt_list[] = $item->getBattery(); } } } if (!empty($warr_results)) { foreach($warr_results as $warr) { // call service to check if warranty details is incomplete and then update warranty // using details from csv file // error_log('Updating warranty for ' . $warr->getID()); $wh->updateWarranty($warr, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase); } } else { // TODO: what if serial exists but plate number is different? // check if just the serial exists // if warranty exists, ignore for now $w_results = $em->getRepository(Warranty::class)->findBy(['serial' => $serial]); if (!empty($w_results)) { continue; } // error_log('Creating warranty for serial ' . $serial . ' and plate number ' . $plate_number); $user_id = $this->getUser()->getUsername(); $source = WarrantySource::BULK_UPLOAD; $wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class, $user_id, $source); } } $em->clear(); $row_num++; } return $invalid_entries; } protected function fillDropdownParameters(&$params) { $em = $this->getDoctrine()->getManager(); $params['batt_models'] = $em->getRepository(BatteryModel::class)->findAll(); $params['batt_sizes'] = $em->getRepository(BatterySize::class)->findAll(); $params['sap_batts'] = $em->getRepository(SAPBattery::class)->findAll(); $params['warranty_classes'] = WarrantyClass::getCollection(); $params['warranty_statuses'] = WarrantyStatus::getCollection(); } // check if datatable filter is present and append to query protected function setQueryFilters($datatable, &$query) { if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) { $query->where('q.serial LIKE :filter') ->orWhere('q.plate_number LIKE :filter') ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); } } protected function cleanPlateNumber($plate) { // remove spaces and make upper case return strtoupper(str_replace(' ', '', $plate)); } }