em = $em; $this->ic = $ic; $this->security = $security; $this->validator = $validator; $this->translator = $translator; $this->rah = $rah; $this->country_code = $country_code; $this->wh = $wh; $this->cust_handler = $cust_handler; $this->loadTemplates(); } // get job order rows public function getRows(Request $req, $tier) { // check which job order tier is being called for and confirm access $tier_params = $this->checkTier($tier); // get current user $user = $this->security->getUser(); if ($user == null) throw new AccessDeniedHttpException('No access.'); $hubs = $user->getHubs(); // get query builder $qb = $this->em->getRepository(JobOrder::class) ->createQueryBuilder('q'); // get datatable params $datatable = $req->request->get('datatable'); // count total records $tquery = $qb->select('COUNT(q)'); $this->setQueryFilters($datatable, $tquery, $qb, $hubs, $tier, $tier_params['jo_status']); $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 $qb = $this->em->getRepository(JobOrder::class) ->createQueryBuilder('q'); $query = $qb->select('q'); $this->setQueryFilters($datatable, $query, $qb, $hubs, $tier, $tier_params['jo_status']); // 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.date_schedule', 'asc'); } // get rows for this page $query_obj = $query->setFirstResult($offset) ->setMaxResults($perpage) ->getQuery(); // error_log($query_obj->getSQL()); $obj_rows = $query_obj->getResult(); $statuses = JOStatus::getCollection(); $service_types = CMBServiceType::getCollection(); // process rows $rows = []; foreach ($obj_rows as $orow) { // get car model $cv = $orow->getCustomerVehicle(); $cv_manufacturer = $cv->getVehicle()->getManufacturer()->getName(); $cv_make = $cv->getVehicle()->getMake(); $year = $cv->getModelYear(); $car_model = $cv_manufacturer . ' ' . $cv_make . ' ' . $year; // get rider information $rider_name = ''; $rider_plate_number = ''; $rider = $orow->getRider(); if (!empty($rider)) { $rider_name = $rider->getFullName(); $rider_plate_number = $rider->getPlateNumber(); } // add row data $row['id'] = $orow->getID(); $row['customer_name'] = $orow->getCustomer()->getFirstName() . ' ' . $orow->getCustomer()->getLastName(); $row['delivery_address'] = $orow->getDeliveryAddress(); $row['date_schedule'] = $orow->getDateSchedule()->format("d M Y g:i A"); $row['type'] = $orow->isAdvanceOrder() ? 'Advanced Order' : 'Immediate'; $row['service_type'] = $service_types[$orow->getServiceType()] ?? 'Unknown'; $row['status'] = $statuses[$orow->getStatus()]; $row['flag_advance'] = $orow->isAdvanceOrder(); $row['plate_number'] = $orow->getCustomerVehicle()->getPlateNumber(); $row['is_mobile'] = $orow->getSource() == TransactionOrigin::MOBILE_APP; $row['car_model'] = $car_model; $row['rider_name'] = $rider_name; $row['rider_plate_number'] = $rider_plate_number; $processor = $orow->getProcessedBy(); if ($processor == null) $row['processor'] = ''; else $row['processor'] = $orow->getProcessedBy()->getFullName(); $assignor = $orow->getAssignedBy(); if ($assignor == null) $row['assignor'] = ''; else $row['assignor'] = $orow->getAssignedBy()->getFullName(); $rows[] = $row; } $params['meta'] = $meta; $params['rows'] = $rows; $params['tier_params'] = $tier_params; return $params; } // get job orders public function getJobOrders(Request $req) { // get search term $term = $req->query->get('search'); // get querybuilder $qb = $this->em->getRepository(JobOrder::class) ->createQueryBuilder('q'); // build expression now since we're reusing it $jo_label = $qb->expr()->concat($qb->expr()->literal('#'), 'q.id', $qb->expr()->literal(' - '), 'c.first_name', $qb->expr()->literal(' '), 'c.last_name', $qb->expr()->literal(' (Plate No: '), 'v.plate_number', $qb->expr()->literal(')')); // count total records $tquery = $qb->select('COUNT(q)') ->join('q.customer', 'c') ->join('q.cus_vehicle', 'v'); // add filters to count query if (!empty($term)) { $tquery->where($jo_label . ' 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($jo_label . ' as jo_label') ->addSelect('c.first_name as cust_first_name') ->addSelect('c.last_name as cust_last_name') ->addSelect('v.plate_number as vehicle_plate_number'); // add filters if needed if (!empty($term)) { $query->where($jo_label . ' LIKE :filter') ->setParameter('filter', '%' . $term . '%'); } // get rows $obj_rows = $query->orderBy('q.id', 'asc') ->setFirstResult($offset) ->setMaxResults($perpage) ->getQuery() ->getResult(); // build job order array $job_orders = []; foreach ($obj_rows as $jo) { $service_type = CMBServiceType::getName($jo[0]->getServiceType()); $job_orders[] = [ 'id' => $jo[0]->getID(), 'text' => $jo['jo_label'] . ' - ' . $service_type ]; } $params['job_orders'] = $job_orders; $params['has_more_pages'] = $has_more_pages; return $params; } // creates/updates job order public function generateJobOrder(Request $req, $id) { // initialize error list $error_array = []; $em = $this->em; $jo = $em->getRepository(JobOrder::class)->find($id); if (empty($jo)) { // new job order $jo = new JobOrder(); } // check if lat and lng are provided if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) { $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } // check if customer vehicle is set if (empty($req->request->get('customer_vehicle'))) { $error_array['customer_vehicle'] = 'No vehicle selected.'; } else { // get customer vehicle $cust_vehicle = $em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle')); if (empty($cust_vehicle)) { $error_array['customer_vehicle'] = 'Invalid vehicle specified.'; } } if (empty($error_array)) { // get current user $user = $this->security->getUser(); // coordinates $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); $stype = $req->request->get('service_type'); // set and save values $jo->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) ->setCoordinates($point) ->setAdvanceOrder($req->request->get('flag_advance') ?? false) ->setServiceType($stype) ->setWarrantyClass($req->request->get('warranty_class')) ->setCustomer($cust_vehicle->getCustomer()) ->setCustomerVehicle($cust_vehicle) ->setSource($req->request->get('source')) ->setStatus(JOStatus::PENDING) ->setDeliveryInstructions($req->request->get('delivery_instructions')) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setORName($req->request->get('or_name')) ->setPromoDetail($req->request->get('promo_detail')) ->setModeOfPayment($req->request->get('mode_of_payment')) ->setLandmark($req->request->get('landmark')); // check if user is null, meaning call to create came from API if ($user != null) { $jo->setCreatedBy($user); } // check if reference JO is set and validate if (!empty($req->request->get('ref_jo'))) { // get reference JO $ref_jo = $em->getRepository(JobOrder::class)->find($req->request->get('ref_jo')); if (empty($ref_jo)) { $error_array['ref_jo'] = 'Invalid reference job order specified.'; } else { $jo->setReferenceJO($ref_jo); } } // call service to generate job order and invoice $invoice_items = $req->request->get('invoice_items', []); $discount = $req->request->get('invoice_discount'); $invoice_change = $req->request->get('invoice_change', 0); // check if invoice changed if ($invoice_change) { $this->ic->generateInvoiceCriteria($jo, $discount, $invoice_items, $error_array); } // validate $errors = $this->validator->validate($jo); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } // check if errors are found if (empty($error_array)) { // validated, no error. save the job order $em->persist($jo); // the event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::CREATE) ->setJobOrder($jo); if ($user != null) { $event->setUser($user); } $em->persist($event); $em->flush(); } } return $error_array; } public function processOneStepJobOrder(Request $req, $id, MQTTClient $mclient) { // initialize error list $error_array = []; $em = $this->em; $jo = $em->getRepository(JobOrder::class)->find($id); $old_rider = null; if (empty($jo)) { // new job order $jo = new JobOrder(); } else { $old_rider = $jo->getRider(); } // check if lat and lng are provided if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) { $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } // check if new customer if ($req->request->get('new_customer', false)) { // validate mobile phone $valid_mobile = $this->cust_handler->validateMobileNumber($req->request->get('phone_mobile')); if (!($valid_mobile)) $error_array['phone_mobile'] = 'Invalid mobile phone number.'; // check if plate number is in request if (empty(trim($req->request->get('plate_number')))) $error_array['plate_number'] = 'Plate number is required.'; // find the vehicle using vid $new_vehicle = $em->getRepository(Vehicle::class)->find($req->request->get('vid')); if (empty($new_vehicle)) { $error_array['cv_mfg'] = 'Invalid manufacturer specified.'; $error_array['cv_make'] = 'Invalid make specified.'; } if (empty($error_array)) { $new_cust = new Customer(); $new_cv = new CustomerVehicle(); $new_cust->setLastName($req->request->get('last_name')) ->setFirstName($req->request->get('first_name')) ->setPhoneMobile($req->request->get('phone_mobile')) ->setPhoneLandline($req->request->get('phone_landline')) ->setPhoneOffice($req->request->get('phone_office')) ->setPhoneFax($req->request->get('phone_fax')) ->setCustomerNotes($req->request->get('customer_notes')); $new_cv->setCustomer($new_cust) ->setVehicle($new_vehicle) ->setPlateNumber(trim($req->request->get('plate_number'))) ->setModelYear($req->request->get('cv_year')) ->setColor('') ->setStatusCondition('') ->setFuelType('') ->setActive() ->setWarrantyCode($req->request->get('warranty_code')); if (($req->request->get('service_type')) == CMBServiceType::BATTERY_REPLACEMENT_NEW) { $new_cv->setHasMotoliteBattery(true); } else { $new_cv->setHasMotoliteBattery(false); } // link JO to new customer $jo->setCustomer($new_cust); $jo->setCustomerVehicle($new_cv); $em->persist($new_cust); $em->persist($new_cv); } } else { // check if customer vehicle is set if (empty($req->request->get('customer_vehicle'))) { $error_array['customer_vehicle'] = 'No vehicle selected.'; } else { // get customer vehicle $cust_vehicle = $em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle')); if (empty($cust_vehicle)) { $error_array['customer_vehicle'] = 'Invalid vehicle specified.'; } else { $jo->setCustomerVehicle($cust_vehicle); $jo->setCustomer($cust_vehicle->getCustomer()); // save serial into cv $cust_vehicle->setWarrantyCode($req->request->get('warranty_code')); $em->persist($cust_vehicle); } } } // check if hub AND rider is selected if ((empty($req->request->get('hub_id'))) && (empty($req->request->get('rider_id')))) { $error_array['hub'] = 'No hub selected.'; } else { if (empty($req->request->get('rider_id'))) { $error_array['rider'] = 'No rider selected.'; } else { // get hub $hub = $em->getRepository(Hub::class)->find($req->request->get('hub_id')); if (empty($hub)) { $error_array['hub'] = 'Invalid hub specified.'; } else { // get rider $rider = $em->getRepository(Rider::class)->find($req->request->get('rider_id')); if (empty($rider)) { $error_array['rider'] = 'Invalid rider specified.'; } else { // check if rider is still available if (!($rider->isAvailable())) $error_array['rider'] = 'Selected rider is unavailable.'; } } } } // get discount and set to meta $discount = $req->request->get('invoice_discount', []); // check if discount is greater than 50 or negative number if (($discount > 50) || ($discount < 0)) $error_array['invoice_discount'] = 'Invalid discount specified'; // get list of service charges $service_charges = $req->request->get('service_charges', []); if (empty($error_array)) { // set priority based on rider's existing open job orders $rider_jos = $rider->getOpenJobOrders(); // get maximum priority then add 1 // NOTE: this can be a bit buggy due to concurrency issues // ideally have to lock jo table, but that isn't feasible right now $priority = 0; foreach ($rider_jos as $rider_jo) { if ($priority < $rider_jo->getPriority()) $priority = $rider_jo->getPriority() + 1; } // get current user $user = $this->security->getUser(); // coordinates $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); $stype = $req->request->get('service_type'); // set and save values $jo->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) ->setCoordinates($point) ->setAdvanceOrder($req->request->get('flag_advance') ?? false) ->setServiceType($stype) ->setWarrantyClass($req->request->get('warranty_class')) ->setSource($req->request->get('source')) ->setStatus(JOStatus::ASSIGNED) ->setDeliveryInstructions($req->request->get('delivery_instructions')) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setORName($req->request->get('or_name')) ->setPromoDetail($req->request->get('promo_detail')) ->setModeOfPayment($req->request->get('mode_of_payment')) ->setLandmark($req->request->get('landmark')) ->setHub($hub) ->setRider($rider) ->setPriority($priority); $jo->addMeta('discount', $discount); $jo->addMeta('service_charges', $service_charges); // check if user is null, meaning call to create came from API if ($user != null) { $jo->setCreatedBy($user); } // check if reference JO is set and validate if (!empty($req->request->get('ref_jo'))) { // get reference JO $ref_jo = $em->getRepository(JobOrder::class)->find($req->request->get('ref_jo')); if (empty($ref_jo)) { $error_array['ref_jo'] = 'Invalid reference job order specified.'; } else { $jo->setReferenceJO($ref_jo); } } // call service to generate job order and invoice $invoice_items = $req->request->get('invoice_items', []); $discount = $req->request->get('invoice_discount'); $invoice_change = $req->request->get('invoice_change', 0); // check if invoice changed if ($invoice_change) { $this->ic->generateInvoiceCriteria($jo, $discount, $invoice_items, $error_array); } // validate $errors = $this->validator->validate($jo); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } // check if errors are found if (empty($error_array)) { // validated, no error. save the job order $em->persist($jo); // the event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::CREATE) ->setJobOrder($jo); if ($user != null) { $event->setUser($user); } $em->persist($event); $em->flush(); // check if JO has been reassigned if ($old_rider != $rider) { // TODO: refactor later $channel = 'rider/' . $rider->getID() . '/events'; $payload = [ 'event' => 'new_jo', 'jo_id' => $jo->getID(), ]; $mclient->publish($channel, json_encode($payload)); } } } return $error_array; } // dispatch job order public function dispatchJobOrder(Request $req, int $id, MQTTClient $mclient) { // get object data $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); $processor = $obj->getProcessedBy(); $user = $this->security->getUser();; // check if we're the one processing, return error otherwise if ($processor == null) throw new AccessDeniedHttpException('Not the processor'); if ($processor != null && $processor->getID() != $user->getID()) throw new AccessDeniedHttpException('Not the processor'); // initialize error list $error_array = []; // make sure this object exists if (empty($obj)) throw new NotFoundHttpException('The item does not exist'); // check if cancelled already if (!$obj->canDispatch()) { throw new NotFoundHttpException('Could not dispatch. Job Order is not pending.'); // TODO: have this handled better, so UI shows the error // $error_array['dispatch'] = 'Could not dispatch. Job Order is not pending.'; } // check if lat and lng are provided if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) { $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } // check if hub is set if (empty($req->request->get('hub'))) { $error_array['hub'] = 'No hub selected.'; } else { // get hub $hub = $em->getRepository(Hub::class)->find($req->request->get('hub')); if (empty($hub)) { $error_array['hub'] = 'Invalid hub specified.'; } } // check facilitated type $fac_type = $req->request->get('facilitated_type'); if (!empty($fac_type)) { if (!FacilitatedType::validate($fac_type)) $fac_type = null; } else $fac_type = null; // check facilitated by $fac_by_id = $req->request->get('facilitated_by'); $fac_by = null; if (!empty($fac_by_id)) { $fac_by = $em->getRepository(Hub::class)->find($fac_by_id); if (empty($fac_by)) $fac_by = null; } if (empty($error_array)) { // coordinates $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); // set and save values $obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) ->setCoordinates($point) ->setAdvanceOrder($req->request->get('flag_advance') ?? false) ->setServiceType($req->request->get('service_type')) ->setWarrantyClass($req->request->get('warranty_class')) ->setSource($req->request->get('source')) ->setStatus(JOStatus::RIDER_ASSIGN) ->setDeliveryInstructions($req->request->get('delivery_instructions')) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setFacilitatedType($fac_type) ->setFacilitatedBy($fac_by) ->setHub($hub); // validate $errors = $this->validator->validate($obj); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } } if (empty($error_array)) { // the event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::HUB_ASSIGN) ->setJobOrder($obj); if ($user != null) { $event->setUser($user); } $em->persist($event); // validated! save the entity $em->flush(); // send event to mobile app $payload = [ 'event' => 'outlet_assign' ]; $mclient->sendEvent($obj, $payload); } return $error_array; } // assign job order public function assignJobOrder(Request $req, $id) { // get object data $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); // initialize error list $error_array = []; // make sure this object exists if (empty($obj)) throw new NotFoundHttpException('The item does not exist'); // check if we can assign if (!$obj->canAssign()) throw new NotFoundHttpException('Cannot assign rider to this job order.'); // check if lat and lng are provided if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) { $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } // check if rider is set if (empty($req->request->get('rider'))) { $error_array['rider'] = 'No rider selected.'; } else { // get rider $rider = $em->getRepository(Rider::class)->find($req->request->get('rider')); if (empty($rider)) { $error_array['rider'] = 'Invalid rider specified.'; } } // get current user $user = $this->security->getUser(); if (empty($error_array)) { // coordinates $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); // set and save values $obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) ->setCoordinates($point) ->setAdvanceOrder($req->request->get('flag_advance') ?? false) ->setServiceType($req->request->get('service_type')) ->setWarrantyClass($req->request->get('warranty_class')) ->setSource($req->request->get('source')) ->setStatus(JOStatus::ASSIGNED) ->setDeliveryInstructions($req->request->get('delivery_instructions')) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setDateAssign(new DateTime()) ->setRider($rider); if ($user != null) { $obj->setAssignedBy($user); } // validate $errors = $this->validator->validate($obj); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } } if (empty($error_array)) { // the event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::RIDER_ASSIGN) ->setJobOrder($obj); if ($user != null) { $event->setUser($user); } $em->persist($event); // validated! save the entity $em->flush(); // call rider assignment handler's assignJobOrder $this->rah->assignJobOrder($obj, $rider); } return $error_array; } // fulfill job order public function fulfillJobOrder(Request $req, $id) { // initialize error list $error_array = []; // get object data $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); // make sure this object exists if (empty($obj)) throw new NotFoundHttpException('The item does not exist'); // check if lat and lng are provided if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) { $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } if (empty($error_array)) { // coordinates $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); // set and save values $obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) ->setCoordinates($point) ->setAdvanceOrder($req->request->get('flag_advance') ?? false) ->setServiceType($req->request->get('service_type')) ->setWarrantyClass($req->request->get('warranty_class')) ->setSource($req->request->get('source')) ->setDeliveryInstructions($req->request->get('delivery_instructions')) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')); // validate $errors = $this->validator->validate($obj); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } } $obj->fulfill(); if (empty($error_array)) { // the event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::FULFILL) ->setJobOrder($obj); // get current user $user = $this->security->getUser(); if ($user != null) { $event->setUser($user); } $event->setUser($user); $em->persist($event); // save to customer vehicle battery record $this->updateVehicleBattery($obj); // save serial to customer vehicle $cust_vehicle = $obj->getCustomerVehicle(); $cust_vehicle->setWarrantyCode($req->request->get('warranty_code')); $em->persist($cust_vehicle); // get rider $rider = $obj->getRider(); $image_url = $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/assets/images/user.gif'; if ($rider->getImageFile() != null) $image_url = $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/uploads/' . $rider->getImageFile(); // call rider assignment handler's fulfillJobOrder $this->rah->fulfillJobOrder($obj, $image_url, $rider); // create the warranty if new battery only if ($this->checkIfNewBattery($obj)) { if (empty($req->request->get('warranty_code'))) $serial = null; else $serial = $req->request->get('warranty_code'); $warranty_class = $obj->getWarrantyClass(); $first_name = $obj->getCustomer()->getFirstName(); $last_name = $obj->getCustomer()->getLastName(); $mobile_number = $obj->getCustomer()->getPhoneMobile(); // check if date fulfilled is null if ($obj->getDateFulfill() == null) $date_purchase = $obj->getDateCreate(); else $date_purchase = $obj->getDateFulfill(); // validate plate number // $plate_number = $this->wh->cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber()); $plate_number = Warranty::cleanPlateNumber($obj->getCustomerVehicle()->getPlateNumber()); if ($plate_number != false) { $batt_list = array(); $invoice = $obj->getInvoice(); if (!empty($invoice)) { // get battery $invoice_items = $invoice->getItems(); foreach ($invoice_items as $item) { $battery = $item->getBattery(); if ($battery != null) { $batt_list[] = $item->getBattery(); } } } $this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class); } } // validated! save the entity $em->flush(); } } // cancel job order public function cancelJobOrder(Request $req, int $id, MQTTClient $mclient) { // get object data $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); // make sure this object exists if (empty($obj)) throw new NotFoundHttpException('The item does not exist'); $cancel_reason = $req->request->get('cancel_reason'); $obj->cancel($cancel_reason); // the event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::CANCEL) ->setJobOrder($obj); // get current user $user = $this->security->getUser(); if ($user != null) { $event->setUser($user); } $event->setUser($user); $em->persist($event); // save $em->flush(); // send mobile app event $payload = [ 'event' => 'cancelled', 'reason' => $cancel_reason, 'jo_id' => $obj->getID(), ]; $mclient->sendEvent($obj, $payload); $mclient->sendRiderEvent($obj, $payload); } // set hub for job order public function setHub($req, $id, $mclient) { // get object data $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); $user = $this->security->getUser(); // initialize error list $error_array = []; // make sure this object exists if (empty($obj)) throw new NotFoundHttpException('The item does not exist'); // check if lat and lng are provided if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) { $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } // check if hub is set if (empty($req->request->get('hub'))) { $error_array['hub'] = 'No hub selected.'; } else { // get hub $hub = $em->getRepository(Hub::class)->find($req->request->get('hub')); if (empty($hub)) { $error_array['hub'] = 'Invalid hub specified.'; } } if (empty($error_array)) { // rider mqtt event // NOTE: need to send this before saving because rider will be cleared $rider_payload = [ 'event' => 'cancelled', 'reason' => 'Reassigned', 'jo_id' => $obj->getID(), ]; $mclient->sendRiderEvent($obj, $rider_payload); // coordinates $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); // set and save values $obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) ->setCoordinates($point) ->setAdvanceOrder($req->request->get('flag_advance') ?? false) ->setServiceType($req->request->get('service_type')) ->setWarrantyClass($req->request->get('warranty_class')) ->setSource($req->request->get('source')) ->setStatus(JOStatus::RIDER_ASSIGN) ->setDeliveryInstructions($req->request->get('delivery_instructions')) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setHub($hub) ->clearRider(); if ($user != null) { $obj->setProcessedBy($user); } $em->persist($obj); // validate $errors = $this->validator->validate($obj); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } } // check if any errors were found if (empty($error_array)) { // add event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::HUB_ASSIGN) ->setJobOrder($obj); if ($user != null) { $event->setUser($user); } $em->persist($event); // validated! save the entity $em->flush(); // user mqtt event $payload = [ 'event' => 'outlet_assign' ]; $mclient->sendEvent($obj, $payload); } return $error_array; } // reject hub for job order public function rejectHub($req, $id) { // get object data $em = $this->em; $jo = $em->getRepository(JobOrder::class)->find($id); $processor = $jo->getProcessedBy(); $user = $this->security->getUser(); // check if we're the one processing, return error otherwise if ($processor == null) throw new AccessDeniedHttpException('Not the processor'); if ($user != null) { if ($processor != null && $processor->getID() != $user->getID()) throw new AccessDeniedHttpException('Not the processor'); } // initialize error list $error_array = []; // make sure job order exists if (empty($jo)) throw new NotFoundHttpException('The item does not exist'); // check if hub is set if (empty($req->request->get('hub'))) { $error_array['hub'] = 'No hub selected.'; } else { // get hub $hub = $em->getRepository(Hub::class)->find($req->request->get('hub')); if (empty($hub)) { $error_array['hub'] = 'Invalid hub specified.'; } } // check if this hub has already been rejected on this job order $robj = $em->getRepository(JORejection::class)->findOneBy([ 'job_order' => $jo, 'hub' => $hub ]); if (!empty($robj)) $error_array['hub'] = 'This hub has already been rejected for the current job order.'; // check if reason is set if (empty($req->request->get('reason'))) $error_array['reason'] = 'No reason selected.'; else if (!JORejectionReason::validate($req->request->get('reason'))) $error_array['reason'] = 'Invalid reason specified.'; if (empty($error_array)) { // coordinates $obj = new JORejection(); // set and save values $obj->setDateCreate(new DateTime()) ->setHub($hub) ->setJobOrder($jo) ->setReason($req->request->get('reason')) ->setRemarks($req->request->get('remarks')) ->setContactPerson($req->request->get('contact_person')); if ($user != null) { $obj->setUser($user); } // validate $errors = $this->validator->validate($obj); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } } if (empty($error_array)) { // validated! save the entity $em->persist($obj); $em->flush(); } return $error_array; } // set rider for job order public function setRider($req, $id, $mclient) { // initialize error list $error_array = []; // get object data $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); $user = $this->security->getUser(); // make sure this object exists if (empty($obj)) throw new NotFoundHttpException('The item does not exist'); // check if lat and lng are provided if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) { $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } // check if rider is set if (empty($req->request->get('rider'))) { $error_array['rider'] = 'No rider selected.'; } else { // get rider $rider = $em->getRepository(Rider::class)->find($req->request->get('rider')); if (empty($rider)) { $error_array['rider'] = 'Invalid rider specified.'; } } if (empty($error_array)) { // rider mqtt event // NOTE: need to send this before saving because rider will be cleared $rider_payload = [ 'event' => 'cancelled', 'reason' => 'Reassigned', 'jo_id' => $obj->getID(), ]; $mclient->sendRiderEvent($obj, $rider_payload); // coordinates $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); // set and save values $obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) ->setCoordinates($point) ->setAdvanceOrder($req->request->get('flag_advance') ?? false) ->setServiceType($req->request->get('service_type')) ->setWarrantyClass($req->request->get('warranty_class')) ->setSource($req->request->get('source')) ->setStatus(JOStatus::ASSIGNED) ->setDeliveryInstructions($req->request->get('delivery_instructions')) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setDateAssign(new DateTime()) ->setRider($rider); if ($user != null) { $obj->setAssignedBy($user); } // validate $errors = $this->validator->validate($obj); $em->persist($obj); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } } // check if any errors were found if (empty($error_array)) { // add event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::RIDER_ASSIGN) ->setJobOrder($obj); if ($user != null) { $event->setUser($user); } $em->persist($event); // validated! save the entity $em->flush(); // send event to mobile app $payload = [ 'event' => 'driver_assigned' ]; $mclient->sendEvent($obj, $payload); $mclient->sendRiderEvent($obj, $payload); } return $error_array; } // unlock processor public function unlockProcessor($id) { // clear lock $em = $this->em; $jo = $em->getRepository(JobOrder::class)->find($id); if ($jo != null) { $jo->setProcessedBy(null); $em->flush(); } } // unlock assignor public function unlockAssignor($id) { // clear lock $em = $this->em; $jo = $em->getRepository(JobOrder::class)->find($id); if ($jo != null) { $jo->setAssignedBy(null); $em->flush(); } } // initialize incoming job order form public function initializeIncomingForm() { $params['obj'] = new JobOrder(); $params['mode'] = 'create'; $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display $params['template'] = $this->getTwigTemplate('jo_incoming_form'); // return params return $params; } public function initializeOneStepForm() { $params['obj'] = new JobOrder(); $params['mode'] = 'onestep'; $params['jo_service_charges'] = []; $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display $params['template'] = $this->getTwigTemplate('jo_onestep_form'); // return params return $params; } public function initializeOneStepEditForm($id, $map_tools) { $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); $params['obj'] = $obj; $params['mode'] = 'onestep-edit'; $params['cvid'] = $obj->getCustomerVehicle()->getID(); $params['vid'] = $obj->getCustomerVehicle()->getVehicle()->getID(); // get service charges $sc_array = []; $jo_service_charges = $obj->getMeta('service_charges'); if (!(empty($jo_service_charges))) { foreach ($jo_service_charges as $jo_sc_id) { // find service charge $sc_obj = $em->getRepository(ServiceCharge::class)->find($jo_sc_id); $sc_array[] = $sc_obj; } } $params['jo_service_charges'] = $sc_array; // get odometer $odometer = $obj->getMeta('odometer'); $params['odometer'] = $odometer; // get customer email used in JO $email = $obj->getMeta('customer_email'); $params['email'] = $email; // get images if any $jo_extra = $obj->getJOExtra(); $pic_array = []; if ($jo_extra != null) { $b_speed_img = $jo_extra->getBeforeSpeedImageFilename(); $a_speed_img = $jo_extra->getAfterSpeedImageFilename(); $b_plate_img = $jo_extra->getBeforePlateNumImageFilename(); $a_plate_img = $jo_extra->getAfterPlateNumImageFilename(); $b_batt_img = $jo_extra->getBeforeBattImageFilename(); $a_batt_img = $jo_extra->getAfterBattImageFilename(); $b_other_images = $jo_extra->getBeforeOtherImages(); $a_other_images = $jo_extra->getAfterOtherImages(); $cust_signature = $jo_extra->getCustomerSignature(); if ($b_speed_img != null) $pic_array['before_speed_img'] = $b_speed_img; if ($a_speed_img != null) $pic_array['after_speed_img'] = $a_speed_img; if ($b_plate_img != null) $pic_array['before_plate_img'] = $b_plate_img; if ($a_plate_img != null) $pic_array['after_plate_img'] = $a_plate_img; if ($b_batt_img != null) $pic_array['before_batt_img'] = $b_batt_img; if ($a_batt_img != null) $pic_array['after_batt_img'] = $a_batt_img; foreach($b_other_images as $b_img) { $pic_array['b_other_images'][] = $b_img; } foreach ($a_other_images as $a_img) { $pic_array['a_other_images'][] = $a_img; } $params['signature'] = $cust_signature; } $params['jo_pictures'] = $pic_array; $this->fillDropdownParameters($params); $this->fillFormTags($params); // get the hubs // TODO: move this snippet to a function $hubs = $map_tools->getClosestHubs($obj->getCoordinates(), 50, date("H:i:s")); $params['hubs'] = []; // format duration and distance into friendly time foreach ($hubs as $hub) { // duration $seconds = $hub['duration']; if (!empty($seconds) && $seconds > 0) { $hours = floor($seconds / 3600); $minutes = ceil(($seconds / 60) % 60); $hub['duration'] = ($hours > 0 ? number_format($hours) . " hr" . ($hours > 1 ? "s" : '') . ($minutes > 0 ? ", " : '') : '') . ($minutes > 0 ? number_format($minutes) . " min" . ($minutes > 1 ? "s" : '') : ''); } else { $hub['duration'] = false; } // distance $meters = $hub['distance']; if (!empty($meters) && $meters > 0) { $hub['distance'] = round($meters / 1000) . " km"; } else { $hub['distance'] = false; } // counters $hub['rider_count'] = count($hub['hub']->getAvailableRiders()); $hub['jo_count'] = count($hub['hub']->getForAssignmentJobOrders()); // check for rejection $hub['flag_rejected'] = false; $hub_id = $hub['hub']->getID(); $params['hubs'][] = $hub; } // get template to display $params['template'] = $this->getTwigTemplate('jo_onestep_edit_form'); return $params; } // initialize open edit job order form public function initializeOpenEditForm($id) { $em = $this->em; $jo = $em->getRepository(JobOrder::class)->find($id); $params['obj'] = $jo; $params['mode'] = 'open_edit'; $params['cvid'] = $jo->getCustomerVehicle()->getID(); $params['vid'] = $jo->getCustomerVehicle()->getVehicle()->getID(); $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display $params['template'] = $this->getTwigTemplate('jo_open_edit_form'); return $params; } // initialize incoming vehicle form public function initializeIncomingVehicleForm(int $cvid) { $params['mode'] = 'create_vehicle'; $params['cvid'] = $cvid; $em = $this->em; // get customer vehicle $cv = $em->getRepository(CustomerVehicle::class)->find($cvid); $params['vid'] = $cv->getVehicle()->getID(); // make sure this customer vehicle exists if (empty($cv)) { $em->getConnection()->rollback(); throw new NotFoundHttpException('The job order does not exist'); } $jo = new JobOrder(); $jo->setCustomerVehicle($cv) ->setCustomer($cv->getCustomer()); $params['obj'] = $jo; $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display $params['template'] = $this->getTwigTemplate('jo_incoming_vehicle_form'); return $params; } // initialize all job orders form for a specific job order id public function initializeAllForm($id) { $em = $this->em; $params['mode'] = 'view-all'; // get row data $obj = $em->getRepository(JobOrder::class)->find($id); // make sure this row exists if (empty($obj)) throw new NotFoundHttpException('The job order does not exist'); $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display // check transaction origin if walkin if ($obj->getSource() == TransactionOrigin::WALK_IN) $params['template'] = $this->getTwigTemplate('jo_walkin_form'); else $params['template'] = $this->getTwigTemplate('jo_onestep_form'); $params['obj'] = $obj; $params['status_cancelled'] = JOStatus::CANCELLED; // timeline stuff (descending by time) $params['timeline'] = [ [ 'date' => date("M j"), 'time' => date("g:i A"), 'event' => "Event 4", 'color' => "#f4516c" ], [ 'date' => date("M j"), 'time' => date("g:i A"), 'event' => "Event 3", 'color' => "#34bfa3" ], [ 'date' => date("M j"), 'time' => date("g:i A"), 'event' => "Event 2", 'color' => "#716aca" ], [ 'date' => date("M j"), 'time' => date("g:i A"), 'event' => "Event 1", 'color' => "#ffb822" ], ]; return $params; } // initialize dispatch/processing job order form public function initializeProcessingForm($id, $map_tools) { $em = $this->em; // manual transaction since we're locking $em->getConnection()->beginTransaction(); try { // lock and get data $obj = $em->getRepository(JobOrder::class)->find($id, LockMode::PESSIMISTIC_READ); // make sure this job order exists if (empty($obj)) { $em->getConnection()->rollback(); throw new NotFoundHttpException('The job order does not exist'); } // check status if ($obj->getStatus() != JOStatus::PENDING) { $em->getConnection()->rollback(); throw new NotFoundHttpException('The job order does not have a pending status'); } // check if we are the processor $processor = $obj->getProcessedBy(); // get current user $user = $this->security->getUser(); if ($user != null) { // TODO: go back to list page and display alert / flash that says they cannot access it because they // are not the processor if ($processor != null && $processor->getID() != $user->getID()) { $em->getConnection()->rollback(); throw new AccessDeniedHttpException('Not the processor'); } // make this user be the processor $obj->setProcessedBy($user); } $em->flush(); $em->getConnection()->commit(); } catch(PessimisticLockException $e) { throw new AccessDeniedHttpException('Not the processor'); } // NOTE: we are able to lock, everything should be fine now $params['mode'] = 'update-processing'; $params['status_cancelled'] = JOStatus::CANCELLED; $this->fillDropdownParameters($params); $this->fillFormTags($params); // get rejections $rejections = $obj->getHubRejections(); // get rejection reasons $params['rejection_reasons'] = JORejectionReason::getCollection(); // get closest hubs $hubs = $map_tools->getClosestHubs($obj->getCoordinates(), 50, date("H:i:s")); $params['hubs'] = []; // format duration and distance into friendly time foreach ($hubs as $hub) { // duration $seconds = $hub['duration']; if (!empty($seconds) && $seconds > 0) { $hours = floor($seconds / 3600); $minutes = ceil(($seconds / 60) % 60); $hub['duration'] = ($hours > 0 ? number_format($hours) . " hr" . ($hours > 1 ? "s" : '') . ($minutes > 0 ? ", " : '') : '') . ($minutes > 0 ? number_format($minutes) . " min" . ($minutes > 1 ? "s" : '') : ''); } else { $hub['duration'] = false; } // distance $meters = $hub['distance']; if (!empty($meters) && $meters > 0) { $hub['distance'] = round($meters / 1000) . " km"; } else { $hub['distance'] = false; } // counters $hub['rider_count'] = count($hub['hub']->getAvailableRiders()); $hub['jo_count'] = count($hub['hub']->getForAssignmentJobOrders()); // check for rejection $hub['flag_rejected'] = false; $hub_id = $hub['hub']->getID(); foreach ($rejections as $robj) { if ($robj->getHub()->getID() === $hub_id) { $hub['flag_rejected'] = true; break; } } $params['hubs'][] = $hub; } $params['obj'] = $obj; // get template to display $params['template'] = $this->getTwigTemplate('jo_processing_form'); return $params; } // initialize assign job order form public function initializeAssignForm($id) { $em = $this->em; // manual transaction since we're locking $em->getConnection()->beginTransaction(); $params['mode'] = 'update-assigning'; try { // get row data $obj = $em->getRepository(JobOrder::class)->find($id); // make sure this row exists if (empty($obj)) { $em->getConnection()->rollback(); throw new NotFoundHttpException('The job order does not exist'); } // check status if ($obj->getStatus() != JOStatus::RIDER_ASSIGN) { $em->getConnection()->rollback(); throw new NotFoundHttpException('The job order does not have an assigning status'); } // check if super user $user = $this->security->getUser(); if ($user != null) { if ($user->isSuperAdmin()) { // do nothing, just allow page to be accessed } else { // check if hub is assigned to current user $user_hubs = $user->getHubs(); if (!in_array($obj->getHub()->getID(), $user_hubs)) { $em->getConnection()->rollback(); throw new NotFoundHttpException('The job order is not on a hub assigned to this user'); } // check if we are the assignor $assignor = $obj->getAssignedBy(); if ($assignor != null && $assignor->getID() != $user->getID()) { $em->getConnection()->rollback(); throw new AccessDeniedHttpException('Not the assignor'); } // make this user be the assignor $obj->setAssignedBy($user); } } $em->flush(); $em->getConnection()->commit(); } catch (PessimisticLockException $e) { throw new AccessDeniedHttpException('Not the assignor'); } $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display $params['template'] = $this->getTwigTemplate('jo_assigning_form'); $params['obj'] = $obj; $params['status_cancelled'] = JOStatus::CANCELLED; return $params; } // initialize fulflll job order form public function initializeFulfillmentForm($id) { $em = $this->em; $params['mode'] = 'update-fulfillment'; // get row data $obj = $em->getRepository(JobOrder::class)->find($id); // make sure this row exists if (empty($obj)) { throw new NotFoundHttpException('The job order does not exist'); } // check status if (!in_array($obj->getStatus(), [JOStatus::ASSIGNED, JOStatus::IN_PROGRESS])) { throw new NotFoundHttpException('The job order does not have a fulfillment status'); } // get current user $user = $this->security->getUser(); // check if hub is assigned to current user $user_hubs = $user->getHubs(); if (!in_array($obj->getHub()->getID(), $user_hubs)) { throw new NotFoundHttpException('The job order is not on a hub assigned to this user'); } $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display $params['template'] = $this->getTwigTemplate('jo_fulfillment_form'); $params['obj'] = $obj; $params['status_cancelled'] = JOStatus::CANCELLED; return $params; } // initialize hub form public function initializeHubForm($id, $map_tools) { $em = $this->em; $params['mode'] = 'update-reassign-hub'; // get row data $obj = $em->getRepository(JobOrder::class)->find($id); // make sure this row exists if (empty($obj)) { throw new NotFoundHttpException('The job order does not exist'); } $this->fillDropdownParameters($params); $this->fillFormTags($params); // get rejections $rejections = $obj->getHubRejections(); // get rejection reasons $params['rejection_reasons'] = JORejectionReason::getCollection(); // get closest hubs $hubs = $map_tools->getClosestHubs($obj->getCoordinates(), 50, date("H:i:s")); $params['status_cancelled'] = JOStatus::CANCELLED; $params['hubs'] = []; // format duration and distance into friendly time foreach ($hubs as $hub) { // duration $seconds = $hub['duration']; if (!empty($seconds) && $seconds > 0) { $hours = floor($seconds / 3600); $minutes = ceil(($seconds / 60) % 60); $hub['duration'] = ($hours > 0 ? number_format($hours) . " hr" . ($hours > 1 ? "s" : '') . ($minutes > 0 ? ", " : '') : '') . ($minutes > 0 ? number_format($minutes) . " min" . ($minutes > 1 ? "s" : '') : ''); } else { $hub['duration'] = false; } // distance $meters = $hub['distance']; if (!empty($meters) && $meters > 0) { $hub['distance'] = round($meters / 1000) . " km"; } else { $hub['distance'] = false; } // counters $hub['rider_count'] = count($hub['hub']->getAvailableRiders()); $hub['jo_count'] = count($hub['hub']->getForAssignmentJobOrders()); // check for rejection $hub['flag_rejected'] = false; $hub_id = $hub['hub']->getID(); foreach ($rejections as $robj) { if ($robj->getHub()->getID() === $hub_id) { $hub['flag_rejected'] = true; break; } } $params['hubs'][] = $hub; } $params['obj'] = $obj; // get template to display $params['template'] = $this->getTwigTemplate('jo_open_hub_form'); return $params; } // initialize rider form public function initializeRiderForm($id) { $em = $this->em; $params['mode'] = 'update-reassign-rider'; // get row data $obj = $em->getRepository(JobOrder::class)->find($id); // make sure this row exists if (empty($obj)) { $em->getConnection()->rollback(); throw new NotFoundHttpException('The job order does not exist'); } // check status if ($obj->getStatus() == JOStatus::PENDING) { $em->getConnection()->rollback(); throw new NotFoundHttpException('The job order does not have an assigned hub'); } $this->fillDropdownParameters($params); $this->fillFormTags($params); $params['obj'] = $obj; $params['status_cancelled'] = JOStatus::CANCELLED; // get template to display $params['template'] = $this->getTwigTemplate('jo_open_rider_form'); return $params; } // generate pdf form for job order public function generatePDFForm($req, $id, $proj_path) { $em = $this->em; $translator = $this->translator; // get row data $obj = $em->getRepository(JobOrder::class)->find($id); // make sure this row exists if (empty($obj)) throw new NotFoundHttpException('The job order does not exist'); // set output filename $filename = 'job-order-' . $obj->getID() . '.pdf'; // translate the title and the logo for the pdf $translated_title = $translator->trans('jo_title_pdf'); $translated_logo = $translator->trans('image_jo_pdf'); $translated_delivery_instructions_label = $translator->trans('delivery_instructions_label'); // generate the pdf $pdf = new FPDF('P', 'mm', 'letter'); $pdf->AddPage(); $pdf->setTitle($translated_title . ' #' . $obj->getID()); $pdf->SetFillColor(211, 211, 211); // style defaults $margin = 10; $page_width = $pdf->GetPageWidth() - ($margin * 2); $table_col_width = $page_width / 12; $line_height = 5; $jo_line_height = 10; $table_line_height = 7; $font_face = 'Arial'; $body_font_size = 9; $header_font_size = 9; $jo_font_size = 16; $col1_x = $margin; $col2_x = 120; $label_width = 40; $val_width = 60; // insert the logo $image_path = $proj_path . $translated_logo; $pdf->Image($image_path, $col1_x, 10); // insert JO number $pdf->SetFont($font_face, 'B', $jo_font_size); $pdf->SetX($col2_x); $pdf->Cell($label_width, $jo_line_height, 'JO Number:'); $pdf->SetTextColor(9, 65, 150); $pdf->Cell(0, $jo_line_height, $obj->getID()); // insert customer info $customer = $obj->getCustomer(); $pdf->SetFont($font_face, '', $body_font_size); $pdf->SetTextColor(0, 0, 0); $pdf->Ln($line_height * 7); // get current Y $y = $pdf->GetY(); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'Customer Name:'); $pdf->MultiCell($val_width, $line_height, $customer ? $customer->getFirstName() . ' ' . $customer->getLastName() : '', 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->Cell($label_width, $line_height, 'Mobile Phone:'); $pdf->MultiCell(0, $line_height, $customer && $customer->getPhoneMobile() ? $this->country_code . $customer->getPhoneMobile() : '', 0, 'L'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'Delivery Date:'); $pdf->MultiCell($val_width, $line_height, $obj->getDateSchedule() ? $obj->getDateSchedule()->format("m/d/Y") : '', 0, 'left'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->Cell($label_width, $line_height, 'Landline:'); $pdf->MultiCell(0, $line_height, $customer && $customer->getPhoneLandline() ? $this->country_code . $customer->getPhoneLandline() : '', 0, 'L'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col2_x, $y); $pdf->Cell($label_width, $line_height, 'Office Phone:'); $pdf->MultiCell(0, $line_height, $customer && $customer->getPhoneOffice() ? $this->country_code . $customer->getPhoneOffice() : '', 0, 'L'); $pdf->SetX($col2_x); $pdf->Cell($label_width, $line_height, 'Fax:'); $pdf->MultiCell($val_width, $line_height, $customer && $customer->getPhoneFax() ? $this->country_code . $customer->getPhoneFax() : '', 0, 'L'); // insert vehicle info $cv = $obj->getCustomerVehicle(); $vehicle = $cv->getVehicle(); $pdf->Ln(); $pdf->SetFont($font_face, 'B', $header_font_size); $pdf->Cell($label_width, $line_height, 'Vehicle Details'); $pdf->Ln($line_height * 2); // get current Y $y = $pdf->GetY(); $pdf->SetFont($font_face, '', $body_font_size); $pdf->Cell($label_width, $line_height, 'Plate Number:'); $pdf->MultiCell($val_width, $line_height, $cv ? $cv->getPlateNumber() : '', 0, 'L'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'Brand:'); $pdf->MultiCell($val_width, $line_height, $vehicle && $vehicle->getManufacturer() ? $vehicle->getManufacturer()->getName() : '', 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->Cell($label_width, $line_height, 'Model / Year:'); $pdf->MultiCell(0, $line_height, $cv ? $cv->getModelYear() : '', 0, 'L'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'Make:'); $pdf->MultiCell($val_width, $line_height, $vehicle ? $vehicle->getMake() : '', 0, 'L'); // insert battery info $battery = $cv->getCurrentBattery(); $pdf->Ln(); $pdf->SetFont($font_face, 'B', $header_font_size); $pdf->Cell($label_width, $line_height, 'Battery Details'); $pdf->Ln($line_height * 2); $pdf->SetFont($font_face, '', $body_font_size); // get current Y $y = $pdf->GetY(); $pdf->Cell($label_width, $line_height, 'Current Battery:'); $pdf->MultiCell($val_width, $line_height, $battery && $battery->getManufacturer() && $battery->getModel() && $battery->getSize() ? $battery->getManufacturer()->getName() . ' ' . $battery->getModel()->getName() . ' ' . $battery->getSize()->getName() . ' (' . $battery->getProductCode() . ')' : '', 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->Cell($label_width, $line_height, 'Serial Number:'); $pdf->MultiCell(0, $line_height, $cv ? $cv->getWarrantyCode() : '', 0, 'L'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'Wty. Exp. Date:'); $pdf->MultiCell($val_width, $line_height, $cv && $cv->getWarrantyExpiration() ? $cv->getWarrantyExpiration()->format("d/m/Y") : '', 0, 'L'); // insert transaction details $pdf->Ln(); $pdf->SetFont($font_face, 'B', $header_font_size); $pdf->Cell($label_width, $line_height, 'Transaction Details'); $pdf->Ln($line_height * 2); $pdf->SetFont($font_face, '', $body_font_size); // get current Y $y = $pdf->GetY(); $pdf->Cell($label_width, $line_height, 'Warranty Class:'); $pdf->MultiCell($val_width, $line_height, CMBWarrantyClass::getName($obj->getWarrantyClass()), 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->Cell($label_width, $line_height, 'Mode of Payment:'); $pdf->MultiCell(0, $line_height, CMBModeOfPayment::getName($obj->getModeOfPayment()), 0, 'L'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->Cell($label_width, $line_height, 'Delivery Address:'); $pdf->MultiCell($val_width, $line_height, $obj->getDeliveryAddress(), 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->Cell($label_width, $line_height, 'Landmark:'); $pdf->MultiCell(0, $line_height, $obj->getLandMark(), 0, 'L'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'Dispatch Time:'); $pdf->MultiCell($val_width, $line_height, $obj->getDateSchedule() ? $obj->getDateSchedule()->format("g:i A") : '', 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->Cell($label_width, $line_height, 'Dispatched By:'); $pdf->MultiCell(0, $line_height, $obj->getProcessedBy() ? $obj->getProcessedBy()->getFullName() : '', 0, 'L'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); // insert delivery instructions $pdf->SetY($y); $pdf->Ln(); $pdf->SetFont($font_face, 'B', $header_font_size); $pdf->Cell(0, $line_height, $translated_delivery_instructions_label); $pdf->Ln(); $pdf->SetFont($font_face, '', $body_font_size); $pdf->MultiCell(0, $line_height, $obj->getDeliveryInstructions(), 1, 'L'); // insert invoice details $pdf->Ln(); $pdf->SetFont($font_face, 'B', $header_font_size); $pdf->Cell($label_width, $line_height, 'Invoice Details'); $pdf->Ln(); // invoice table headers $invoice = $obj->getInvoice(); $pdf->SetFont($font_face, 'B', $header_font_size); $pdf->Cell($table_col_width * 6, $table_line_height, 'Item', 1, 0, 'L', 1); $pdf->Cell($table_col_width * 2, $table_line_height, 'Quantity', 1, 0, 'R', 1); $pdf->Cell($table_col_width * 2, $table_line_height, 'Unit Price', 1, 0, 'R', 1); $pdf->Cell($table_col_width * 2, $table_line_height, 'Amount', 1, 1, 'R', 1); $pdf->SetFont($font_face, '', $body_font_size); // build invoice items table if ($invoice && !empty($invoice->getItems())) { foreach ($invoice->getItems() as $item) { $pdf->Cell($table_col_width * 6, $table_line_height, $item->getTitle(), 1); $pdf->Cell($table_col_width * 2, $table_line_height, number_format($item->getQuantity()), 1, 0, 'R'); $pdf->Cell($table_col_width * 2, $table_line_height, number_format($item->getPrice(), 2), 1, 0, 'R'); $pdf->Cell($table_col_width * 2, $table_line_height, number_format($item->getPrice() * $item->getQuantity(), 2), 1, 1, 'R'); } } else { $pdf->Cell($table_col_width * 12, 7, 'No items', 1, 1); } $pdf->Ln($line_height * 2); // get current Y $y = $pdf->GetY(); // insert invoice footer details $pdf->Cell($label_width, $line_height, 'Transaction Type:'); $pdf->MultiCell($val_width, $line_height, CMBServiceType::getName($obj->getServiceType()), 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->SetFont($font_face, 'B'); $pdf->Cell($label_width, $line_height, 'SUBTOTAL:'); $pdf->SetFont($font_face, ''); $pdf->MultiCell(0, $line_height, $invoice ? number_format($invoice->getVATExclusivePrice(), 2) : '', 0, 'R'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'OR Name:'); $pdf->MultiCell($val_width, $line_height, $obj->getORName(), 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->SetFont($font_face, 'B'); $pdf->Cell($label_width, $line_height, 'TAX:'); $pdf->SetFont($font_face, ''); $pdf->MultiCell(0, $line_height, $invoice ? number_format($invoice->getVAT(), 2) : '', 0, 'R'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'Emp. ID/Card No./Ref. By:'); $pdf->MultiCell($val_width, $line_height, $obj->getPromoDetail(), 0, 'L'); // get Y after left cell $y1 = $pdf->GetY(); $pdf->SetXY($col2_x, $y); $pdf->SetFont($font_face, 'B'); $pdf->Cell($label_width, $line_height, 'DISCOUNT:'); $pdf->SetFont($font_face, ''); $pdf->MultiCell(0, $line_height, $invoice ? number_format($invoice->getDiscount(), 2) : '', 0, 'R'); // get Y after right cell $y2 = $pdf->GetY(); // get row height $y = max($y1, $y2); $pdf->SetXY($col1_x, $y); $pdf->Cell($label_width, $line_height, 'Discount Type:'); $pdf->MultiCell($val_width, $line_height, $invoice && $invoice->getPromo() ? $invoice->getPromo()->getName() : '', 0, 'L'); $pdf->SetXY($col2_x, $y); $pdf->SetFont($font_face, 'B'); $pdf->Cell($label_width, $line_height, 'FINAL AMOUNT:'); $pdf->MultiCell(0, $line_height, $invoice ? number_format($invoice->getTotalPrice(), 2) : '', 0, 'R'); $pdf->SetFont($font_face, ''); $params['obj'] = $pdf; $params['filename'] = $filename; return $params; } public function getTwigTemplate($id) { if (isset($this->template_hash[$id])) { return $this->template_hash[$id]; } return null; } public function updateVehicleBattery(JobOrder $jo) { // check if new battery if (!($this->checkIfNewBattery($jo))) return; // customer vehicle $cv = $jo->getCustomerVehicle(); if ($cv == null) return; // invoice $invoice = $jo->getInvoice(); if ($invoice == null) return; // invoice items $items = $invoice->getItems(); if (count($items) <= 0) return; // get first battery from invoice $battery = null; foreach ($items as $item) { $battery = $item->getBattery(); if ($battery != null) break; } // no battery in order if ($battery == null) return; // warranty expiration // use GetWarrantyPrivate for passenger warranty $warr_months = 0; $warr = $jo->getWarrantyClass(); if ($warr == CMBWarrantyClass::WTY_PASSENGER) $warr_months = $battery->getWarrantyPrivate(); else if ($warr == CMBWarrantyClass::WTY_COMMERCIAL) $warr_months = $battery->getWarrantyCommercial(); $warr_date = new DateTime(); $warr_date->add(new DateInterval('P' . $warr_months . 'M')); // update customer vehicle battery $cv->setCurrentBattery($battery) ->setHasMotoliteBattery(true) ->setWarrantyExpiration($warr_date); } public function getOtherParameters() { // get riders for dropdown $params['riders'] = $this->em->getRepository(Rider::class)->findAll(); return $params; } public function checkIfNewBattery(JobOrder $jo) { if ($jo->getServiceType() == CMBServiceType::BATTERY_REPLACEMENT_NEW) return true; return false; } public function initializeWalkinForm() { $params['obj'] = new JobOrder(); $params['mode'] = 'walk-in'; $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display $params['template'] = $this->getTwigTemplate('jo_walkin_form'); // return params return $params; } public function processWalkinJobOrder(Request $req, $id) { // initialize error list $error_array = []; $em = $this->em; $jo = $em->getRepository(JobOrder::class)->find($id); if (empty($jo)) { // new job order $jo = new JobOrder(); } // check if new customer if ($req->request->get('new_customer', false)) { // validate mobile phone $valid_mobile = $this->cust_handler->validateMobileNumber($req->request->get('phone_mobile')); if (!($valid_mobile)) $error_array['phone_mobile'] = 'Invalid mobile phone number.'; // check if plate number is in request if (empty(trim($req->request->get('plate_number')))) $error_array['plate_number'] = 'Plate number is required.'; // find the vehicle using vid $new_vehicle = $em->getRepository(Vehicle::class)->find($req->request->get('vid')); if (empty($new_vehicle)) { $error_array['cv_mfg'] = 'Invalid manufacturer specified.'; $error_array['cv_make'] = 'Invalid make specified.'; } if (empty($error_array)) { $new_cust = new Customer(); $new_cv = new CustomerVehicle(); $new_cust->setLastName($req->request->get('last_name')) ->setFirstName($req->request->get('first_name')) ->setPhoneMobile($req->request->get('phone_mobile')) ->setPhoneLandline($req->request->get('phone_landline')) ->setPhoneOffice($req->request->get('phone_office')) ->setPhoneFax($req->request->get('phone_fax')) ->setCustomerNotes($req->request->get('customer_notes')); $new_cv->setCustomer($new_cust) ->setVehicle($new_vehicle) ->setPlateNumber(trim($req->request->get('plate_number'))) ->setModelYear($req->request->get('cv_year')) ->setColor('') ->setStatusCondition('') ->setFuelType('') ->setActive() ->setWarrantyCode($req->request->get('warranty_code')); if (($req->request->get('service_type')) == CMBServiceType::BATTERY_REPLACEMENT_NEW) { $new_cv->setHasMotoliteBattery(true); } else { $new_cv->setHasMotoliteBattery(false); } // link JO to new customer $jo->setCustomer($new_cust); $jo->setCustomerVehicle($new_cv); $em->persist($new_cust); $em->persist($new_cv); } } else { // check if customer vehicle is set if (empty($req->request->get('customer_vehicle'))) { $error_array['customer_vehicle'] = 'No vehicle selected.'; } else { // get customer vehicle $cust_vehicle = $em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle')); if (empty($cust_vehicle)) { $error_array['customer_vehicle'] = 'Invalid vehicle specified.'; } else { $jo->setCustomerVehicle($cust_vehicle); $jo->setCustomer($cust_vehicle->getCustomer()); // save serial into cv $cust_vehicle->setWarrantyCode($req->request->get('warranty_code')); $em->persist($cust_vehicle); } } } // check if hub is selected if (empty($req->request->get('hub_id'))) $error_array['hub'] = 'No hub selected.'; else { // get hub $hub = $em->getRepository(Hub::class)->find($req->request->get('hub_id')); if (empty($hub)) $error_array['hub'] = 'Invalid hub specified.'; // get hub coordinates $hub_coordinates = $hub->getCoordinates(); } // get discount and set to meta $discount = $req->request->get('invoice_discount'); // check if discount is greater than 50 or negative number if (($discount > 50) || ($discount < 0)) $error_array['invoice_discount'] = 'Invalid discount specified'; if (empty($error_array)) { // get current user $user = $this->security->getUser(); $stype = $req->request->get('service_type'); // set and save values $jo->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) ->setAdvanceOrder($req->request->get('flag_advance') ?? false) ->setServiceType($stype) ->setWarrantyClass($req->request->get('warranty_class')) ->setSource($req->request->get('source')) ->setStatus(JOStatus::FULFILLED) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) ->setORName($req->request->get('or_name')) ->setPromoDetail($req->request->get('promo_detail')) ->setModeOfPayment($req->request->get('mode_of_payment')) ->setLandmark($req->request->get('landmark')) ->setDeliveryAddress('Walk-in') ->setLandmark('Walk-in') ->setCoordinates($hub_coordinates) ->setHub($hub); $jo->addMeta('discount', $discount); // check if user is null, meaning call to create came from API if ($user != null) { $jo->setCreatedBy($user); } // check if reference JO is set and validate if (!empty($req->request->get('ref_jo'))) { // get reference JO $ref_jo = $em->getRepository(JobOrder::class)->find($req->request->get('ref_jo')); if (empty($ref_jo)) { $error_array['ref_jo'] = 'Invalid reference job order specified.'; } else { $jo->setReferenceJO($ref_jo); } } // call service to generate job order and invoice $invoice_items = $req->request->get('invoice_items', []); $discount = $req->request->get('invoice_discount'); $invoice_change = $req->request->get('invoice_change', 0); // check if invoice changed if ($invoice_change) { $this->ic->generateInvoiceCriteria($jo, $discount, $invoice_items, $error_array); } // validate $errors = $this->validator->validate($jo); // add errors to list foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } // check if errors are found if (empty($error_array)) { // validated, no error. save the job order $em->persist($jo); // the event $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::CREATE) ->setJobOrder($jo); if ($user != null) { $event->setUser($user); } $em->persist($event); // save to customer vehicle battery record $this->updateVehicleBattery($jo); // save serial to customer vehicle $cust_vehicle = $jo->getCustomerVehicle(); $cust_vehicle->setWarrantyCode($req->request->get('warranty_code')); $em->persist($cust_vehicle); // create the warranty if new battery only if ($this->checkIfNewBattery($jo)) { if (empty($req->request->get('warranty_code'))) $serial = null; else $serial = $req->request->get('warranty_code'); $warranty_class = $jo->getWarrantyClass(); $first_name = $jo->getCustomer()->getFirstName(); $last_name = $jo->getCustomer()->getLastName(); $mobile_number = $jo->getCustomer()->getPhoneMobile(); // check if date fulfilled is null if ($jo->getDateFulfill() == null) $date_purchase = $jo->getDateCreate(); else $date_purchase = $jo->getDateFulfill(); // validate plate number // $plate_number = $this->wh->cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber()); $plate_number = Warranty::cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber()); if ($plate_number != false) { $batt_list = array(); $invoice = $jo->getInvoice(); if (!empty($invoice)) { // get battery $invoice_items = $invoice->getItems(); foreach ($invoice_items as $item) { $battery = $item->getBattery(); if ($battery != null) { $batt_list[] = $item->getBattery(); } } } $this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class); } } $em->flush(); } } return $error_array; } public function initializeWalkinEditForm($id) { $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); $params['obj'] = $obj; $params['mode'] = 'walk-in-edit'; $params['cvid'] = $obj->getCustomerVehicle()->getID(); $params['vid'] = $obj->getCustomerVehicle()->getVehicle()->getID(); $this->fillDropdownParameters($params); $this->fillFormTags($params); // get template to display $params['template'] = $this->getTwigTemplate('jo_walkin_edit_form'); // return params return $params; } protected function fillDropdownParameters(&$params) { $em = $this->em; // db loaded $params['bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll(); $params['promos'] = $em->getRepository(Promo::class)->findAll(); $params['service_charges'] = $em->getRepository(ServiceCharge::class)->findAll(); // list of hubs $hubs = $em->getRepository(Hub::class)->findBy([], ['name' => 'ASC']); $fac_hubs = []; foreach ($hubs as $hub) { $fac_hubs[$hub->getID()] = $hub->getName() . ' - ' . $hub->getBranch(); } // name values $params['service_types'] = CMBServiceType::getCollection(); $params['warranty_classes'] = CMBWarrantyClass::getCollection(); $params['modes_of_payment'] = CMBModeOfPayment::getCollection(); $params['statuses'] = JOStatus::getCollection(); $params['discount_apply'] = DiscountApply::getCollection(); $params['trade_in_types'] = CMBTradeInType::getCollection(); $params['facilitated_types'] = FacilitatedType::getCollection(); $params['facilitated_hubs'] = $fac_hubs; $params['sources'] = TransactionOrigin::getCollection(); } protected function initFormTags(&$params) { // default to editing, as we have more forms editing than creating $params['ftags'] = [ 'title' => 'Job Order Form', 'vehicle_dropdown' => false, 'invoice_edit' => false, 'set_map_coordinate' => true, 'preset_vehicle' => false, 'ticket_table' => true, 'cancel_button' => true, ]; } protected function fillFormTags(&$params) { $this->initFormTags($params); switch ($params['mode']) { case 'create': $params['ftags']['vehicle_dropdown'] = true; $params['ftags']['set_map_coordinate'] = false; $params['ftags']['invoice_edit'] = true; $params['ftags']['ticket_table'] = false; $params['ftags']['cancel_button'] = false; break; case 'create_vehicle': $params['ftags']['set_map_coordinate'] = false; $params['ftags']['invoice_edit'] = true; $params['ftags']['preset_vehicle'] = true; $params['ftags']['ticket_table'] = false; $params['ftags']['cancel_button'] = false; break; case 'open_edit': $params['ftags']['invoice_edit'] = true; $params['ftags']['preset_vehicle'] = true; break; case 'onestep': $params['ftags']['vehicle_dropdown'] = true; $params['ftags']['set_map_coordinate'] = false; $params['ftags']['invoice_edit'] = true; $params['ftags']['ticket_table'] = false; $params['ftags']['cancel_button'] = false; break; case 'onestep-edit': $params['ftags']['invoice_edit'] = true; $params['ftags']['preset_vehicle'] = true; break; case 'walk-in': $params['ftags']['vehicle_dropdown'] = true; $params['ftags']['set_map_coordinate'] = false; $params['ftags']['invoice_edit'] = true; $params['ftags']['ticket_table'] = false; $params['ftags']['cancel_button'] = false; break; case 'walk-in-edit': $params['ftags']['invoice_edit'] = true; $params['ftags']['preset_vehicle'] = true; break; } } protected function loadTemplates() { $this->template_hash = []; // add all twig templates for job order to hash // TODO: put this in an array declaration // $this->template_hash = [ // 'blah' => 'blah', // ]; $this->template_hash['jo_incoming_vehicle_form'] = 'job-order/cmb.form.html.twig'; $this->template_hash['jo_fulfillment_form'] = 'job-order/cmb.form.onestep.html.twig'; $this->template_hash['jo_all_form'] = 'job-order/cmb.form.onestep.html.twig'; $this->template_hash['jo_list_fulfillment'] = 'job-order/cmb.list.fulfillment.html.twig'; $this->template_hash['jo_list_open'] = 'job-order/cmb.list.open.html.twig'; $this->template_hash['jo_list_all'] = 'job-order/cmb.list.all.html.twig'; $this->template_hash['jo_onestep_form'] = 'job-order/cmb.form.onestep.html.twig'; $this->template_hash['jo_onestep_edit_form'] = 'job-order/cmb.form.onestep.html.twig'; $this->template_hash['jo_walkin_form'] = 'job-order/cmb.form.walkin.html.twig'; $this->template_hash['jo_walkin_edit_form'] = 'job-order/cmb.form.walkin.html.twig'; $this->template_hash['jo_popup'] = 'job-order/cmb.popup.html.twig'; } protected function checkTier($tier) { // check specified tier switch ($tier) { case 'proc': $tier_key = 'jo_proc'; $tier_name = 'Dispatch'; $rows_route = 'jo_proc_rows'; $edit_route = 'jo_proc_form'; $unlock_route = 'jo_proc_unlock'; $jo_status = JOStatus::PENDING; break; case 'assign': $tier_key = 'jo_assign'; $tier_name = 'Assigning'; $rows_route = 'jo_assign_rows'; $edit_route = 'jo_assign_form'; $unlock_route = 'jo_assign_unlock'; $jo_status = JOStatus::RIDER_ASSIGN; break; case 'fulfill': $tier_key = 'jo_fulfill'; $tier_name = 'Fullfillment'; $rows_route = 'jo_fulfill_rows'; $edit_route = 'jo_fulfill_form'; $unlock_route = ''; $jo_status = [ JOStatus::ASSIGNED, JOStatus::IN_PROGRESS ]; break; case 'open': $tier_key = 'jo_open'; $tier_name = 'Open'; $rows_route = 'jo_open_rows'; $edit_route = ''; $unlock_route = ''; $jo_status = [ JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS, JOStatus::IN_TRANSIT, ]; break; case 'all': $tier_key = 'jo_open'; $tier_name = 'Open'; $rows_route = 'jo_open_rows'; $edit_route = 'jo_all_form'; $unlock_route = ''; $jo_status = ''; break; default: throw new AccessDeniedHttpException('No access.'); } // check acl if (!($this->security->isGranted($tier_key . '.list'))) throw new AccessDeniedHttpException('No access.'); // return params if allowed access return [ 'key' => $tier_key, 'name' => $tier_name, 'rows_route' => $rows_route, 'edit_route' => $edit_route, 'unlock_route' => $unlock_route, 'jo_status' => $jo_status ]; } // TODO: re-enable search, figure out how to group the orWhere filters into one, so can execute that plus the pending filter // check if datatable filter is present and append to query protected function setQueryFilters($datatable, &$query, $qb, $hubs, $tier, $status) { switch ($tier) { case 'fulfill': if (isset($datatable['query']['data-rows-search'])) { $query->innerJoin('q.cus_vehicle', 'cv') ->innerJoin('q.customer', 'c') ->where('cv.plate_number like :filter') ->orWhere('c.phone_mobile like :filter') ->orWhere('c.first_name like :filter or c.last_name like :filter') ->setParameter('filter', $datatable['query']['data-rows-search'] . '%'); } if (isset($datatable['query']['rider'])) { $query->innerJoin('q.rider', 'r') ->andWhere('r.id = :rider_id') ->setParameter('rider_id', $datatable['query']['rider']); } if (isset($datatable['query']['schedule_date'])) { $start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00'; $end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00'; $date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start); $date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end); $query->andWhere('q.date_schedule >= :date_start') ->andWhere('q.date_schedule <= :date_end') ->setParameter('date_start', $date_start) ->setParameter('date_end', $date_end); } $query->andWhere('q.status IN (:statuses)') ->andWhere('q.hub IN (:hubs)') ->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY) ->setParameter('hubs', $hubs, Connection::PARAM_STR_ARRAY); break; case 'assign': $query->where('q.status = :status') ->andWhere('q.hub IN (:hubs)') ->setParameter('status', $status) ->setParameter('hubs', $hubs, Connection::PARAM_STR_ARRAY); break; case 'open': if (isset($datatable['query']['data-rows-search'])) { $query->innerJoin('q.cus_vehicle', 'cv') ->innerJoin('q.customer', 'c') ->where('q.status IN (:statuses)') ->andWhere('cv.plate_number like :filter or c.first_name like :filter or c.last_name like :filter or c.phone_mobile like :filter') ->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY) ->setParameter('filter', $datatable['query']['data-rows-search'] . '%'); } else { $query->where('q.status IN (:statuses)') ->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY); } if (isset($datatable['query']['rider'])) { $query->innerJoin('q.rider', 'r') ->andWhere('r.id = :rider_id') ->setParameter('rider_id', $datatable['query']['rider']); } if (isset($datatable['query']['schedule_date'])) { $start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00'; $end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00'; $date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start); $date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end); $query->andWhere('q.date_schedule >= :date_start') ->andWhere('q.date_schedule <= :date_end') ->setParameter('date_start', $date_start) ->setParameter('date_end', $date_end); } break; case 'all': if (isset($datatable['query']['data-rows-search'])) { $query->innerJoin('q.cus_vehicle', 'cv') ->innerJoin('q.customer', 'c') ->where('cv.plate_number like :filter') ->orWhere('c.phone_mobile like :filter') ->orWhere('c.first_name like :filter or c.last_name like :filter') ->setParameter('filter', $datatable['query']['data-rows-search'] . '%'); } if (isset($datatable['query']['rider'])) { $query->innerJoin('q.rider', 'r') ->andWhere('r.id = :rider_id') ->setParameter('rider_id', $datatable['query']['rider']); } if (isset($datatable['query']['schedule_date'])) { $start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00'; $end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00'; $date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start); $date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end); $query->andWhere('q.date_schedule >= :date_start') ->andWhere('q.date_schedule <= :date_end') ->setParameter('date_start', $date_start) ->setParameter('date_end', $date_end); } break; default: $query->where('q.status = :status') ->setParameter('status', $status); } } public function getEditRoute($jo_id, $tier = null) { $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); if (empty($jo)) throw new NotFoundHttpException('The item does not exist'); // check transaction origin if ($jo->getSource() == TransactionOrigin::WALK_IN) return 'jo_walkin_edit_form'; else return 'jo_onestep_edit_form'; } }