diff --git a/config/acl.yaml b/config/acl.yaml
index 9edbb5cf..ce8eb67a 100644
--- a/config/acl.yaml
+++ b/config/acl.yaml
@@ -189,6 +189,8 @@ access_keys:
label: Processing
- id: jo_assign.list
label: Assigning
+ - id: jo_fulfill.list
+ label: Fulfillment
- id: support
label: Customer Support Access
diff --git a/config/menu.yaml b/config/menu.yaml
index 7c72fbc9..aa4f7a67 100644
--- a/config/menu.yaml
+++ b/config/menu.yaml
@@ -93,6 +93,10 @@ main_menu:
acl: jo_assign.list
label: Assigning
parent: joborder
+ - id: jo_fulfill
+ acl: jo_fulfill.list
+ label: Fulfillment
+ parent: joborder
- id: support
acl: support.menu
diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml
index 78a6204d..1563f374 100644
--- a/config/routes/job_order.yaml
+++ b/config/routes/job_order.yaml
@@ -58,3 +58,25 @@ jo_gen_invoice:
path: /job-order/generate-invoice
controller: App\Controller\JobOrderController::generateInvoice
methods: [POST]
+
+jo_fulfill:
+ path: /job-order/fulfillment
+ controller: App\Controller\JobOrderController::listFulfillment
+ methods: [GET]
+
+jo_fulfill_rows:
+ path: /job-order/fulfillment-rows
+ controller: App\Controller\JobOrderController::getRows
+ methods: [POST]
+ defaults:
+ tier: "fulfill"
+
+jo_fulfill_form:
+ path: /job-order/fulfillment/{id}
+ controller: App\Controller\JobOrderController::fulfillmentForm
+ methods: [GET]
+
+jo_fulfill_submit:
+ path: /job-order/fulfillment/{id}
+ controller: App\Controller\JobOrderController::fulfillmentSubmit
+ methods: [POST]
diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php
index 330164aa..7a2d4e1c 100644
--- a/src/Controller/JobOrderController.php
+++ b/src/Controller/JobOrderController.php
@@ -194,6 +194,16 @@ class JobOrderController extends BaseController
$edit_route = 'jo_assign_form';
$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';
+ $jo_status = [
+ JOStatus::ASSIGNED,
+ JOStatus::IN_PROGRESS
+ ];
+ break;
default:
$exception = $this->createAccessDeniedException('No access.');
throw $exception;
@@ -221,6 +231,15 @@ class JobOrderController extends BaseController
return $this->render('job-order/list.assigning.html.twig', $params);
}
+ public function listFulfillment()
+ {
+ $params = $this->initParameters('jo_fulfill');
+
+ $params['table_refresh_rate'] = $this->container->getParameter('job_order_refresh_interval');
+
+ return $this->render('job-order/list.fulfillment.html.twig', $params);
+ }
+
public function listRows($tier)
{
// check which job order tier is being called for and confirm access
@@ -597,37 +616,6 @@ class JobOrderController extends BaseController
$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_assign_submit', ['id' => $obj->getID()]);
$params['return_url'] = $this->generateUrl('jo_assign');
@@ -715,19 +703,155 @@ class JobOrderController extends BaseController
}
+
+
+
+
+
+
+ public function fulfillmentForm(MapTools $map_tools, $id)
+ {
+ $this->denyAccessUnlessGranted('jo_assign.list', null, 'No access.');
+
+ $em = $this->getDoctrine()->getManager();
+
+ $params = $this->initParameters('jo_assign');
+ $params['mode'] = 'update-fulfillment';
+
+ // 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');
+ }
+
+ // check status
+ if (!in_array($obj->getStatus(), [JOStatus::ASSIGNED, JOStatus::IN_PROGRESS]))
+ {
+ throw $this->createNotFoundException('The job order does not have a fulfillment status');
+ }
+
+ // check if hub is assigned to current user
+ $user_hubs = $this->getUser()->getHubs();
+ if (!in_array($obj->getHub()->getID(), $user_hubs))
+ {
+ throw $this->createNotFoundException('The job order is not on a hub assigned to this user');
+ }
+
+ // 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_fulfill_submit', ['id' => $obj->getID()]);
+ $params['return_url'] = $this->generateUrl('jo_fulfill');
+
+ // response
+ return $this->render('job-order/form.html.twig', $params);
+ }
+
+ public function fulfillmentSubmit(Request $req, ValidatorInterface $validator, $id)
+ {
+ $this->denyAccessUnlessGranted('jo_assign.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.';
+ }
+
+ 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::FULFILLED)
+ ->setDeliveryInstructions($req->request->get('delivery_instructions'))
+ ->setAgentNotes($req->request->get('agent_notes'))
+ ->setDeliveryAddress($req->request->get('delivery_address'));
+
+ // 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!'
+ ]);
+ }
+
+
+
+
+
+
+
+
+
+
// 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)
{
- $query->where('q.status = :status')
- ->setParameter('status', $status);
-
- // on assigning, filter by assigned hub
- if ($tier == 'assign')
+ switch ($tier)
{
- $query->andWhere('q.hub IN (:hubs)')
- ->setParameter('hubs', $hubs, Connection::PARAM_STR_ARRAY);
+ case 'fulfill':
+ $query->where('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;
+ default:
+ $query->where('q.status = :status')
+ ->setParameter('status', $status);
}
// get only pending rows
diff --git a/templates/job-order/form.html.twig b/templates/job-order/form.html.twig
index bc91ef46..e66a6433 100644
--- a/templates/job-order/form.html.twig
+++ b/templates/job-order/form.html.twig
@@ -425,7 +425,7 @@
{% endif %}
- {% if mode == 'update-assigning' %}
+ {% if mode in ['update-assigning', 'update-fulfillment'] %}
{% if obj.getHub %}
@@ -469,56 +469,100 @@
{% endif %}
-
-