acl_gen = $acl_gen; parent::__construct($menu_gen); } public function index() { $this->denyAccessUnlessGranted('customer.list', null, 'No access.'); $params = $this->initParameters('customer_list'); // response return $this->render('customer/list.html.twig', $params); } public function rows(Request $req) { $this->denyAccessUnlessGranted('customer.list', null, 'No access.'); // build query $qb = $this->getDoctrine() ->getRepository(Customer::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.first_name', '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['title'] = $orow->getTitle(); $row['first_name'] = $orow->getFirstName(); $row['last_name'] = $orow->getLastName(); $row['customer_classification'] = CustomerClassification::getName($orow->getCustomerClassification()); $row['flag_mobile_app'] = $orow->hasMobileApp(); $row['app_mobile_number'] = $orow->hasMobileApp() && !empty($orow->getMobileSessions()) ? $orow->getMobileSessions()[0]->getPhoneNumber() : ''; $row['flag_active'] = $orow->isActive(); // TODO: properly add mobile numbers and plate numbers as searchable/sortable fields, use doctrine events $row['mobile_numbers'] = implode("
", $orow->getMobileNumberList()); $row['plate_numbers'] = implode("
", $orow->getPlateNumberList()); // add row metadata $row['meta'] = [ 'update_url' => '', 'delete_url' => '' ]; // add crud urls if ($this->isGranted('customer.update')) $row['meta']['update_url'] = $this->generateUrl('customer_update', ['id' => $row['id']]); if ($this->isGranted('customer.delete')) $row['meta']['delete_url'] = $this->generateUrl('customer_delete', ['id' => $row['id']]); $rows[] = $row; } // response return $this->json([ 'meta' => $meta, 'data' => $rows ]); } protected function fillDropdownParameters(&$params) { $em = $this->getDoctrine()->getManager(); $params['bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll(); $params['vmfgs'] = $em->getRepository(VehicleManufacturer::class)->findAll(); $params['classifications'] = CustomerClassification::getCollection(); $params['fuel_types'] = FuelType::getCollection(); $params['status_conditions'] = VehicleStatusCondition::getCollection(); $params['years'] = $this->generateYearOptions(); $params['batteries'] = $em->getRepository(Battery::class)->findAll(); } public function addForm() { $this->denyAccessUnlessGranted('customer.add', null, 'No access.'); $params = $this->initParameters('customer_list'); $params['obj'] = new Customer(); $params['mode'] = 'create'; // get dropdown parameters $this->fillDropdownParameters($params); // response return $this->render('customer/form.html.twig', $params); } protected function setObject($obj, $req) { // set and save values $obj->setTitle($req->request->get('title')) ->setFirstName($req->request->get('first_name')) ->setLastName($req->request->get('last_name')) ->setCustomerClassification($req->request->get('customer_classification')) ->setCustomerNotes($req->request->get('customer_notes')) ->setEmail($req->request->get('email')) ->setActive($req->request->get('flag_active') ? true : false); // phone numbers $obj->setPhoneMobile($req->request->get('phone_mobile')) ->setPhoneLandline($req->request->get('phone_landline')) ->setPhoneOffice($req->request->get('phone_office')) ->setPhoneFax($req->request->get('phone_fax')); } public function addSubmit(Request $req, ValidatorInterface $validator) { $this->denyAccessUnlessGranted('customer.add', null, 'No access.'); // create new row $em = $this->getDoctrine()->getManager(); $row = new Customer(); $this->setObject($row, $req); // initialize error lists $error_array = []; $nerror_array = []; $verror_array = []; // error_log(print_r($req->request->all(), true)); // custom validation for vehicles $vehicles = json_decode($req->request->get('vehicles')); if (!empty($vehicles)) { foreach ($vehicles as $vehicle) { // check if vehicle exists $vobj = $em->getRepository(Vehicle::class)->find($vehicle->vehicle); if (empty($vobj)) { $verror_array[$vehicle->index]['vehicle'] = 'Invalid vehicle specified.'; } else { $cust_vehicle = new CustomerVehicle(); $cust_vehicle->setName($vehicle->name) ->setVehicle($vobj) ->setPlateNumber($vehicle->plate_number) ->setModelYear($vehicle->model_year) ->setColor($vehicle->color) ->setStatusCondition($vehicle->status_condition) ->setFuelType($vehicle->fuel_type) ->setActive($vehicle->flag_active) ->setCustomer($row); // if specified, check if battery exists if ($vehicle->battery) { // check if battery exists $bobj = $em->getRepository(Battery::class)->find($vehicle->battery); if (empty($bobj)) { $verror_array[$vehicle->index]['battery'] = 'Invalid battery specified.'; } else { // check if warranty expiration was specified $warr_ex = DateTime::createFromFormat("d M Y", $vehicle->warranty_expiration); if (!$warr_ex) $warr_ex = null; $cust_vehicle->setHasMotoliteBattery(true) ->setCurrentBattery($bobj) ->setWarrantyCode($vehicle->warranty_code) ->setWarrantyExpiration($warr_ex); } } else { $cust_vehicle->setHasMotoliteBattery(false); } $verrors = $validator->validate($cust_vehicle); // add errors to list foreach ($verrors as $error) { if (!isset($verror_array[$vehicle->index])) $verror_array[$vehicle->index] = []; $verror_array[$vehicle->index][$error->getPropertyPath()] = $error->getMessage(); } // add to entity if (!isset($verror_array[$vehicle->index])) { $row->addVehicle($cust_vehicle); } } } } // validate $errors = $validator->validate($row); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } // check if any errors were found if (!empty($error_array) || !empty($nerror_array) || !empty($verror_array)) { // return validation failure response return $this->json([ 'success' => false, 'errors' => $error_array, 'nerrors' => $nerror_array, 'verrors' => $verror_array ], 422); } else { // validated! save the entity $em->persist($row); $em->flush(); // return successful response return $this->json([ 'success' => 'Changes have been saved!', 'id' => $row->getID() ]); } } public function updateForm($id) { $this->denyAccessUnlessGranted('customer.update', null, 'No access.'); $params = $this->initParameters('customer_list'); $params['mode'] = 'update'; // get row data $em = $this->getDoctrine()->getManager(); $row = $em->getRepository(Customer::class)->find($id); // make sure this row exists if (empty($row)) throw $this->createNotFoundException('The item does not exist'); // get dropdown parameters $this->fillDropdownParameters($params); $params['obj'] = $row; // response return $this->render('customer/form.html.twig', $params); } protected function updateVehicles($em, Customer $cust, $vehicles) { $vehicle_ids = []; foreach ($vehicles as $vehicle) { // check if customer vehicle exists if (!empty($vehicle->id)) { $cust_vehicle = $em->getRepository(CustomerVehicle::class)->find($vehicle->id); if ($cust_vehicle == null) throw new CrudException("Could not find customer vehicle."); } // this is a new vehicle else { $cust_vehicle = new CustomerVehicle(); $cust_vehicle->setCustomer($cust); $cust->addVehicle($cust_vehicle); $em->persist($cust_vehicle); } // vehicle, because they could have changed vehicle type $vobj = $em->getRepository(Vehicle::class)->find($vehicle->vehicle); if ($vobj == null) throw new CrudException("Could not find vehicle."); // TODO: validate details $cust_vehicle->setName($vehicle->name) ->setVehicle($vobj) ->setPlateNumber($vehicle->plate_number) ->setModelYear($vehicle->model_year) ->setColor($vehicle->color) ->setStatusCondition($vehicle->status_condition) ->setFuelType($vehicle->fuel_type) ->setActive($vehicle->flag_active); // if specified, check if battery exists if ($vehicle->battery) { // check if battery exists $bobj = $em->getRepository(Battery::class)->find($vehicle->battery); if ($bobj == null) throw new CrudException("Could not find battery."); // check if warranty expiration was specified $warr_ex = DateTime::createFromFormat("d M Y", $vehicle->warranty_expiration); if (!$warr_ex) $warr_ex = null; $cust_vehicle->setHasMotoliteBattery(true) ->setCurrentBattery($bobj) ->setWarrantyCode($vehicle->warranty_code) ->setWarrantyExpiration($warr_ex); } else { $cust_vehicle->setHasMotoliteBattery(false); } // add to list of vehicles to keep $vehicle_ids[$cust_vehicle->getID()] = true; } // cleanup // delete all vehicles not in list $cvs = $cust->getVehicles(); foreach ($cvs as $cv) { if (!isset($vehicle_ids[$cv->getID()])) { $cust->removeVehicle($cv); $em->remove($cv); } } } public function updateSubmit(Request $req, ValidatorInterface $validator, $id) { $this->denyAccessUnlessGranted('customer.update', null, 'No access.'); // get row data $em = $this->getDoctrine()->getManager(); $cust = $em->getRepository(Customer::class)->find($id); // make sure this row exists if (empty($cust)) throw $this->createNotFoundException('The item does not exist'); $this->setObject($cust, $req); // initialize error lists $error_array = []; $nerror_array = []; $verror_array = []; // TODO: validate mobile numbers // TODO: validate vehicles // custom validation for vehicles $vehicles = json_decode($req->request->get('vehicles')); $this->updateVehicles($em, $cust, $vehicles); // validate $errors = $validator->validate($cust); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } // check if any errors were found if (!empty($error_array) || !empty($nerror_array) || !empty($verror_array)) { // return validation failure response return $this->json([ 'success' => false, 'errors' => $error_array, 'nerrors' => $nerror_array, 'verrors' => $verror_array ], 422); } else { // validated! save the entity. do a persist anyway to save child entities $em->persist($cust); $em->flush(); // return successful response return $this->json([ 'success' => 'Changes have been saved!', 'id' => $cust->getID() ]); } } public function destroy($id) { $this->denyAccessUnlessGranted('customer.delete', null, 'No access.'); $params = $this->initParameters('customer_list'); // get row data $em = $this->getDoctrine()->getManager(); $row = $em->getRepository(Customer::class)->find($id); if (empty($row)) throw $this->createNotFoundException('The item does not exist'); // delete this row $em->remove($row); $em->flush(); // response $response = new Response(); $response->setStatusCode(Response::HTTP_OK); $response->send(); } protected function generateYearOptions() { $start_year = 1950; return range($start_year, date("Y") + 1); } public function getCustomerVehicles(Request $req) { if (!$this->isGranted('jo_in.list')) { $exception = $this->createAccessDeniedException('No access.'); throw $exception; } // get search term $term = $req->query->get('search'); // get querybuilder $qb = $this->getDoctrine() ->getRepository(CustomerVehicle::class) ->createQueryBuilder('q'); // build expression now since we're reusing it $vehicle_label = $qb->expr()->concat( 'q.plate_number', $qb->expr()->literal(' - '), 'c.first_name', $qb->expr()->literal(' '), 'c.last_name', $qb->expr()->literal(' (+63'), 'c.phone_mobile', $qb->expr()->literal(')') ); // count total records $tquery = $qb->select('COUNT(q)') ->join('q.customer', 'c'); // add filters to count query if (!empty($term)) { $tquery->where('q.plate_number LIKE :filter') ->setParameter('filter', '%' . $term . '%'); } $total = $tquery->getQuery() ->getSingleScalarResult(); // pagination vars $page = $req->query->get('page') ?? 1; $perpage = 20; $offset = ($page - 1) * $perpage; $pages = ceil($total / $perpage); $has_more_pages = $page < $pages ? true : false; // build main query $query = $qb->select('q') ->addSelect($vehicle_label . ' as vehicle_label') ->addSelect('c.first_name as cust_first_name') ->addSelect('c.last_name as cust_last_name'); // add filters if needed if (!empty($term)) { $query->where('q.plate_number LIKE :filter') ->setParameter('filter', '%' . $term . '%'); } // get rows $query_obj = $query->orderBy('q.plate_number', 'asc') ->setFirstResult($offset) ->setMaxResults($perpage) ->getQuery(); // error_log($query_obj->getSql()); $obj_rows = $query_obj->getResult(); // build vehicles array $vehicles = []; foreach ($obj_rows as $vehicle) { $vehicles[] = [ 'id' => $vehicle[0]->getID(), 'text' => $vehicle['vehicle_label'] ]; } // response return $this->json([ 'success' => true, 'results' => $vehicles, 'pagination' => [ 'more' => $has_more_pages ] ]); } public function getCustomerVehicleInfo(Request $req) { $this->denyAccessUnlessGranted('jo_in.list', null, 'No access.'); // get id $id = $req->query->get('id'); // get row data $em = $this->getDoctrine()->getManager(); $obj = $em->getRepository(CustomerVehicle::class)->find($id); // make sure this row exists if (empty($obj)) { return $this->json([ 'success' => false, 'error' => 'The item does not exist' ]); } $customer = $obj->getCustomer(); $vehicle = $obj->getVehicle(); $battery = $obj->getCurrentBattery(); // build response $row = [ 'customer' => [ 'id' => $customer->getID(), 'first_name' => $customer->getFirstName(), 'last_name' => $customer->getLastName(), 'customer_notes' => $customer->getCustomerNotes(), 'phone_mobile' => $customer->getPhoneMobile(), 'phone_landline' => $customer->getPhoneLandline(), 'phone_office' => $customer->getPhoneOffice(), 'phone_fax' => $customer->getPhoneFax(), ], 'vehicle' => [ 'id' => $vehicle->getID(), 'mfg_name' => $vehicle->getManufacturer()->getName(), 'make' => $vehicle->getMake(), 'model_year_from' => $vehicle->getModelYearFrom(), 'model_year_to' => $vehicle->getModelYearTo(), 'model_year' => $obj->getModelYear(), 'color' => $obj->getColor(), 'plate_number' => $obj->getPlateNumber(), //'fuel_type' => $obj->getFuelType(), //'status_condition' => $obj->getStatusCondition(), ] ]; if (!empty($battery)) { $row['battery'] = [ 'id' => $battery->getID(), 'mfg_name' => $battery->getManufacturer()->getName(), 'model_name' => $battery->getModel()->getName(), 'size_name' => $battery->getSize()->getName(), 'prod_code' => $battery->getProductCode(), 'warranty_code' => $obj->getWarrantyCode(), 'warranty_expiration' => $obj->getWarrantyExpiration() ? $obj->getWarrantyExpiration()->format("d M Y") : "", 'has_motolite_battery' => $obj->hasMotoliteBattery(), 'is_active' => $obj->isActive() ]; } // response return $this->json([ 'success' => true, 'data' => $row ]); } // 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.first_name LIKE :filter') ->orWhere('q.last_name LIKE :filter') ->orWhere('q.customer_classification LIKE :filter') ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); } } }