diff --git a/config/acl.yaml b/config/acl.yaml index ce8eb67a..ea3e486f 100644 --- a/config/acl.yaml +++ b/config/acl.yaml @@ -191,6 +191,10 @@ access_keys: label: Assigning - id: jo_fulfill.list label: Fulfillment + - id: jo_open.list + label: Open + - id: joborder.cancel + label: Cancel - id: support label: Customer Support Access diff --git a/config/menu.yaml b/config/menu.yaml index aa4f7a67..cdc72dc8 100644 --- a/config/menu.yaml +++ b/config/menu.yaml @@ -97,6 +97,10 @@ main_menu: acl: jo_fulfill.list label: Fulfillment parent: joborder + - id: jo_open + acl: jo_open.list + label: Open + parent: joborder - id: support acl: support.menu diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml index a3099fd0..db6a1b87 100644 --- a/config/routes/job_order.yaml +++ b/config/routes/job_order.yaml @@ -81,6 +81,43 @@ jo_fulfill_submit: controller: App\Controller\JobOrderController::fulfillmentSubmit methods: [POST] +jo_open: + path: /job-order/open + controller: App\Controller\JobOrderController::listOpen + methods: [GET] + +jo_open_rows: + path: /job-order/open-rows + controller: App\Controller\JobOrderController::getRows + methods: [POST] + defaults: + tier: "open" + +jo_open_hub_form: + path: /job-order/open/hub/{id} + controller: App\Controller\JobOrderController::openHubForm + methods: [GET] + +jo_open_hub_submit: + path: /job-order/open/hub/{id} + controller: App\Controller\JobOrderController::openHubSubmit + methods: [POST] + +jo_open_rider_form: + path: /job-order/open/rider/{id} + controller: App\Controller\JobOrderController::openRiderForm + methods: [GET] + +jo_open_rider_submit: + path: /job-order/open/rider/{id} + controller: App\Controller\JobOrderController::openRiderSubmit + methods: [POST] + +jo_cancel: + path: /job-order/{id} + controller: App\Controller\JobOrderController::cancel + methods: [DELETE] + jo_search: path: /job-order/search controller: App\Controller\JobOrderController::getJobOrders diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index 826088b8..4a9ca7df 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -297,6 +297,18 @@ class JobOrderController extends BaseController JOStatus::IN_PROGRESS ]; break; + case 'open': + $tier_key = 'jo_open'; + $tier_name = 'Open'; + $rows_route = 'jo_open_rows'; + $edit_route = false; + $jo_status = [ + JOStatus::PENDING, + JOStatus::RIDER_ASSIGN, + JOStatus::ASSIGNED, + JOStatus::IN_PROGRESS + ]; + break; default: $exception = $this->createAccessDeniedException('No access.'); throw $exception; @@ -333,6 +345,16 @@ class JobOrderController extends BaseController return $this->render('job-order/list.fulfillment.html.twig', $params); } + public function listOpen() + { + $params = $this->initParameters('jo_open'); + + $params['table_refresh_rate'] = $this->container->getParameter('job_order_refresh_interval'); + $params['statuses'] = JOStatus::getCollection(); + + return $this->render('job-order/list.open.html.twig', $params); + } + public function listRows($tier) { // check which job order tier is being called for and confirm access @@ -435,7 +457,15 @@ class JobOrderController extends BaseController $row['assignor'] = $orow->getAssignedBy()->getFullName(); // add crud urls - $row['meta']['update_url'] = $this->generateUrl($tier_params['edit_route'], ['id' => $row['id']]); + if ($tier == 'open') + { + $row['meta']['reassign_hub_url'] = $this->generateUrl('jo_open_hub_form', ['id' => $row['id']]); + $row['meta']['reassign_rider_url'] = $this->generateUrl('jo_open_rider_form', ['id' => $row['id']]); + } + else + { + $row['meta']['update_url'] = $this->generateUrl($tier_params['edit_route'], ['id' => $row['id']]); + } $rows[] = $row; } @@ -795,13 +825,6 @@ class JobOrderController extends BaseController ]); } - - - - - - - public function fulfillmentForm(MapTools $map_tools, $id) { $this->denyAccessUnlessGranted('jo_assign.list', null, 'No access.'); @@ -914,11 +937,279 @@ class JobOrderController extends BaseController ]); } + public function openHubForm(MapTools $map_tools, $id) + { + $this->denyAccessUnlessGranted('jo_open.list', null, 'No access.'); + + $em = $this->getDoctrine()->getManager(); + + $params = $this->initParameters('jo_open'); + $params['mode'] = 'update-reassign-hub'; + + // get row data + $obj = $em->getRepository(JobOrder::class)->find($id); + + // make sure this row exists + if (empty($obj)) + { + throw $this->createNotFoundException('The job order does not exist'); + } + + // get parent associations + $params['bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll(); + $params['customers'] = $em->getRepository(Customer::class)->findAll(); + $params['service_types'] = ServiceType::getCollection(); + $params['warranty_classes'] = WarrantyClass::getCollection(); + $params['statuses'] = JOStatus::getCollection(); + $params['promos'] = $em->getRepository(Promo::class)->findAll(); + $params['discount_apply'] = DiscountApply::getCollection(); + $params['trade_in_types'] = TradeInType::getCollection(); + + // get closest hubs + $hubs = $map_tools->getClosestHubs($obj->getCoordinates(), 10, 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; + } + + $params['hubs'][] = $hub; + } + + $params['obj'] = $obj; + $params['submit_url'] = $this->generateUrl('jo_open_hub_submit', ['id' => $obj->getID()]); + $params['return_url'] = $this->generateUrl('jo_open'); + + // response + return $this->render('job-order/form.html.twig', $params); + } + + public function openHubSubmit(Request $req, ValidatorInterface $validator, $id) + { + $this->denyAccessUnlessGranted('jo_open.list', null, 'No access.'); + + // get object data + $em = $this->getDoctrine()->getManager(); + $obj = $em->getRepository(JobOrder::class)->find($id); + $user = $this->getUser(); + + // initialize error list + $error_array = []; + + // make sure this object exists + if (empty($obj)) + throw $this->createNotFoundException('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)) { + // 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('web') + ->setStatus(JOStatus::RIDER_ASSIGN) + ->setDeliveryInstructions($req->request->get('delivery_instructions')) + ->setAgentNotes($req->request->get('agent_notes')) + ->setDeliveryAddress($req->request->get('delivery_address')) + ->setHub($hub) + ->clearRider(); + + // validate + $errors = $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)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + public function openRiderForm($id) + { + $this->denyAccessUnlessGranted('jo_open.list', null, 'No access.'); + + $em = $this->getDoctrine()->getManager(); + + $params = $this->initParameters('jo_open'); + $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 $this->createNotFoundException('The job order does not exist'); + } + + // check status + if ($obj->getStatus() == JOStatus::PENDING) + { + $em->getConnection()->rollback(); + throw $this->createNotFoundException('The job order does not have an assigned hub'); + } + + // get parent associations + $params['bmfgs'] = $em->getRepository(BatteryManufacturer::class)->findAll(); + $params['customers'] = $em->getRepository(Customer::class)->findAll(); + $params['service_types'] = ServiceType::getCollection(); + $params['warranty_classes'] = WarrantyClass::getCollection(); + $params['statuses'] = JOStatus::getCollection(); + $params['promos'] = $em->getRepository(Promo::class)->findAll(); + $params['discount_apply'] = DiscountApply::getCollection(); + $params['trade_in_types'] = TradeInType::getCollection(); + + $params['obj'] = $obj; + $params['submit_url'] = $this->generateUrl('jo_open_rider_submit', ['id' => $obj->getID()]); + $params['return_url'] = $this->generateUrl('jo_open'); + + // response + return $this->render('job-order/form.html.twig', $params); + } + + public function openRiderSubmit(Request $req, ValidatorInterface $validator, $id) + { + $this->denyAccessUnlessGranted('jo_open.list', null, 'No access.'); + + // initialize error list + $error_array = []; + + // get object data + $em = $this->getDoctrine()->getManager(); + $obj = $em->getRepository(JobOrder::class)->find($id); + + // make sure this object exists + if (empty($obj)) + throw $this->createNotFoundException('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)) { + // 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('web') + ->setStatus(JOStatus::ASSIGNED) + ->setDeliveryInstructions($req->request->get('delivery_instructions')) + ->setAgentNotes($req->request->get('agent_notes')) + ->setDeliveryAddress($req->request->get('delivery_address')) + ->setAssignedBy($this->getUser()) + ->setDateAssign(new DateTime()) + ->setRider($rider); + + // validate + $errors = $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)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + + @@ -942,6 +1233,10 @@ class JobOrderController extends BaseController ->setParameter('status', $status) ->setParameter('hubs', $hubs, Connection::PARAM_STR_ARRAY); break; + case 'open': + $query->where('q.status IN (:statuses)') + ->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY); + break; default: $query->where('q.status = :status') ->setParameter('status', $status); diff --git a/src/Entity/JobOrder.php b/src/Entity/JobOrder.php index fe3e16c2..5bec7ce1 100644 --- a/src/Entity/JobOrder.php +++ b/src/Entity/JobOrder.php @@ -360,6 +360,11 @@ class JobOrder return $this; } + public function clearHub() + { + return $this->hub = null; + } + public function getHub() { return $this->hub; @@ -371,6 +376,11 @@ class JobOrder return $this; } + public function clearRider() + { + return $this->rider = null; + } + public function getRider() { return $this->rider; diff --git a/templates/job-order/form.html.twig b/templates/job-order/form.html.twig index 3625f2a1..82f2ecfd 100644 --- a/templates/job-order/form.html.twig +++ b/templates/job-order/form.html.twig @@ -28,6 +28,12 @@ {% elseif mode == 'update-assigning' %} Assigning {{ obj.getID() }} + {% elseif mode == 'update-reassign-hub' %} + Re-assign Hub + {{ obj.getID() }} + {% elseif mode == 'update-reassign-rider' %} + Re-assign Rider + {{ obj.getID() }} {% else %} Incoming {% endif %} @@ -393,7 +399,7 @@ {% endif %} - {% if mode == 'update-processing' %} + {% if mode in ['update-processing', 'update-reassign-hub'] %}
@@ -448,7 +454,7 @@
{% endif %} - {% if mode in ['update-assigning', 'update-fulfillment'] %} + {% if mode in ['update-assigning', 'update-fulfillment', 'update-reassign-rider'] %}
{% if obj.getHub %}
@@ -495,7 +501,7 @@
- {% if mode == 'update-assigning' %} + {% if mode in ['update-assigning', 'update-reassign-rider'] %}

@@ -701,7 +707,7 @@ $(function() { $("[data-vehicle-field='1']").prop('placeholder', ''); {% endif %} - {% if mode == 'update-processing' %} + {% if mode in ['update-processing', 'update-reassign-hub'] %} // display hub map var hmap = new GMaps({ div: '#hub_map', @@ -743,12 +749,12 @@ $(function() { // add invoice items to data fields['invoice_items'] = invoiceItems; - {% if mode == 'update-processing' %} + {% if mode in ['update-processing', 'update-reassign-hub'] %} // add selected hub to data fields['hub'] = selectedHub; {% endif %} - {% if mode == 'update-assigning' %} + {% if mode in ['update-assigning', 'update-reassign-rider'] %} // add selected rider to data fields['rider'] = selectedRider; {% endif %} @@ -1128,7 +1134,7 @@ $(function() { }); */ - {% if mode == 'update-processing' %} + {% if mode in ['update-processing', 'update-reassign-hub'] %} var selectedHub = '{{ obj.getHub ? obj.getHub.getID : "" }}'; $("#hubs-table tbody tr").click(function() { @@ -1157,7 +1163,7 @@ $(function() { }); {% endif %} - {% if mode == 'update-assigning' %} + {% if mode in ['update-assigning', 'update-reassign-rider'] %} var selectedRider = '{{ obj.getRider ? obj.getRider.getID : "" }}'; $("#riders-table tbody tr").click(function() { diff --git a/templates/job-order/list.open.html.twig b/templates/job-order/list.open.html.twig new file mode 100644 index 00000000..fd7a667d --- /dev/null +++ b/templates/job-order/list.open.html.twig @@ -0,0 +1,133 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

+ Job Orders (Open) +

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+
+
+ +
+ +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %}