em = $em; $this->ic = $ic; $this->security = $security; $this->validator = $validator; } // 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', []); $promo_id = $req->request->get('invoice_promo'); $invoice_change = $req->request->get('invoice_change', 0); // check if invoice changed if ($invoice_change) { // TODO: move invoice processing to InvoiceGenerator $this->processInvoice($jo, $promo_id, $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; } // 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, MQTTCLient $mclient, APNSClient $aclient) { // 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)) { // set rider unavailable $rider->setAvailable(false); // 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(); // send event to mobile app $payload = [ 'event' => 'driver_assigned' ]; $mclient->sendEvent($obj, $payload); $mclient->sendRiderEvent($obj, $payload); // sned push notification $aclient->sendPush($obj, "A RESQ rider is on his way to you."); } return $error_array; } // fulfill job order public function fulfillJobOrder(Request $req, $id, MQTTClient $mclient) { // 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); // validated! save the entity $em->flush(); // 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(); // send to mqtt $payload = [ 'event' => 'fulfilled', 'jo_id' => $obj->getID(), 'driver_image' => $image_url, 'driver_name' => $rider->getFullName(), 'driver_id' => $rider->getID(), ]; $mclient->sendEvent($obj, $payload); $mclient->sendRiderEvent($obj, $payload); } } // 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); // return params 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); 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); return $params; } // initialize all job orders form for a specific job order id public function initializeAllForm($id) { $em = $this->em; $params['mode'] = 'update-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); $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 initializeDispatchForm($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; 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); $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); $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; 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; 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(); // 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'] = ServiceType::getCollection(); $params['warranty_classes'] = WarrantyClass::getCollection(); $params['modes_of_payment'] = ModeOfPayment::getCollection(); $params['statuses'] = JOStatus::getCollection(); $params['discount_apply'] = DiscountApply::getCollection(); $params['trade_in_types'] = TradeInType::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; } } protected function updateVehicleBattery(JobOrder $jo) { // check if new battery if ($jo->getServiceType() != ServiceType::BATTERY_REPLACEMENT_NEW) 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 $warr = $jo->getWarrantyClass(); if ($warr == WarrantyClass::WTY_PRIVATE) $warr_months = $battery->getWarrantyPrivate(); else if ($warr == WarrantyClass::WTY_COMMERCIAL) $warr_months = $battery->getWarrantyCommercial(); else if ($warr == WarrantyClass::WTY_TNV) $warr_months = 12; $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); } // TODO: move this to InvoiceGenerator protected function processInvoice($jo, $promo_id, $invoice_items, &$error_array) { // instantiate the invoice criteria $criteria = new InvoiceCriteria(); $criteria->setServiceType($jo->getServiceType()) ->setCustomerVehicle($jo->getCustomerVehicle()); $ierror = $this->invoicePromo($criteria, $promo_id); if (!$ierror && !empty($invoice_items)) { // check for trade-in so we can mark it for mobile app foreach ($invoice_items as $item) { // get first trade-in if (!empty($item['trade_in'])) { $jo->getTradeInType($item['trade_in']); break; } } $ierror = $this->invoiceBatteries($criteria, $invoice_items); } if ($ierror) { $error_array['invoice'] = $ierror; } else { // generate the invoice $iobj = $this->ic->generateInvoice($criteria); // validate $ierrors = $this->validator->validate($iobj); // add errors to list foreach ($ierrors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } // check if invoice already exists for JO $old_invoice = $jo->getInvoice(); if ($old_invoice != null) { // remove old invoice $this->em->remove($old_invoice); $this->em->flush(); } // add invoice to JO $jo->setInvoice($iobj); $this->em->persist($iobj); } } //TODO: move this to InvoiceGenerator protected function invoicePromo(InvoiceCriteria $criteria, $promo_id) { // return error if there's a problem, false otherwise // check service type $stype = $criteria->getServiceType(); if ($stype != ServiceType::BATTERY_REPLACEMENT_NEW) return null; if (empty($promo_id)) { return false; } // check if this is a valid promo $promo = $this->em->getRepository(Promo::class)->find($promo_id); if (empty($promo)) return 'Invalid promo specified.'; $criteria->addPromo($promo); return false; } // TODO: move this to InvoiceGenerator protected function invoiceBatteries(InvoiceCriteria $criteria, $items) { // check service type $stype = $criteria->getServiceType(); if ($stype != ServiceType::BATTERY_REPLACEMENT_NEW && $stype != ServiceType::BATTERY_REPLACEMENT_WARRANTY) return null; // return error if there's a problem, false otherwise if (!empty($items)) { foreach ($items as $item) { // check if this is a valid battery $battery = $this->em->getRepository(Battery::class)->find($item['battery']); if (empty($battery)) { $error = 'Invalid battery specified.'; return $error; } // quantity $qty = $item['quantity']; if ($qty < 1) continue; /* // add to criteria $criteria->addBattery($battery, $qty); */ // if this is a trade in, add trade in if (!empty($item['trade_in']) && TradeInType::validate($item['trade_in'])) $trade_in = $item['trade_in']; else $trade_in = null; $criteria->addEntry($battery, $trade_in, $qty); } } return null; } }