diff --git a/config/acl.yaml b/config/acl.yaml index bfd6c6cc..6574648c 100644 --- a/config/acl.yaml +++ b/config/acl.yaml @@ -326,6 +326,10 @@ access_keys: label: Vehicle Battery Compatibility Report - id: report.warranty.details label: Warranty Details Report + - id: report.jo.details + label: Job Order Details Report + - id: report.jo_events + label: Job Order Events Report - id: service label: Other Services diff --git a/config/routes/report.yaml b/config/routes/report.yaml index 85ad83ee..57fc52d0 100644 --- a/config/routes/report.yaml +++ b/config/routes/report.yaml @@ -77,3 +77,23 @@ rep_warranty_details_export_csv: path: /report/warranty_details_report controller: App\Controller\ReportController::warrantyDetailsExportCSV methods: [POST] + +rep_jo_details_form: + path: /report/jo_details_report + controller: App\Controller\ReportController::jobOrderDetailsForm + methods: [GET] + +rep_jo_details_submit: + path: /report/jo_details_report + controller: App\Controller\ReportController::jobOrderDetailsSubmit + methods: [POST] + +rep_jo_events_form: + path: /report/jo_events_report + controller: App\Controller\ReportController::jobOrderEventsForm + methods: [GET] + +rep_jo_events_submit: + path: /report/jo_events_report + controller: App\Controller\ReportController::jobOrderEventsSubmit + methods: [POST] diff --git a/public/assets/js/dashboard_map.js b/public/assets/js/dashboard_map.js index 6cba7d68..16f8a78d 100644 --- a/public/assets/js/dashboard_map.js +++ b/public/assets/js/dashboard_map.js @@ -1,14 +1,16 @@ class DashboardMap { - constructor(options, rider_markers, cust_markers) { + constructor(options, rider_markers, cust_markers, mc_markers) { this.options = options; this.rider_markers = rider_markers; this.cust_markers = cust_markers; + this.mobile_cust_markers = mc_markers; // layer groups this.layer_groups = { 'rider_available': L.layerGroup(), 'rider_active_jo': L.layerGroup(), - 'customer': L.layerGroup() + 'customer': L.layerGroup(), + 'mobile_customer': L.layerGroup(), }; } @@ -31,6 +33,7 @@ class DashboardMap { this.layer_groups.rider_available.addTo(this.map); this.layer_groups.rider_active_jo.addTo(this.map); this.layer_groups.customer.addTo(this.map); + this.layer_groups.mobile_customer.addTo(this.map); // base layer var baseMaps = { @@ -42,7 +45,8 @@ class DashboardMap { var overlayMaps = { 'Available Riders' : this.layer_groups.rider_available, 'JO Riders' : this.layer_groups.rider_active_jo, - 'Customers' : this.layer_groups.customer + 'Customers' : this.layer_groups.customer, + 'Mobile Customers': this.layer_groups.mobile_customer } L.control.layers(baseMaps, overlayMaps).addTo(this.map); @@ -133,6 +137,32 @@ class DashboardMap { layer_group.removeLayer(markers[id]); } + putMobileCustomerMarker(id, lat, lng) { + this.putMarker( + id, + lat, + lng, + this.mobile_cust_markers, + this.options.icons.mobile_customer, + this.layer_groups.mobile_customer, + this.options.cust_popup_url + ); + } + + removeMobileCustomerMarker(id) { + console.log('removing mobile customer marker for ' + id); + var layer_group = this.layer_groups.mobile_customer; + var markers = this.mobile_cust_markers; + + // no customer marker with that id + if (!markers.hasOwnProperty(id)) { + console.log('no such marker to remove'); + return; + } + + layer_group.removeLayer(markers[id]); + } + putRiderAvailableMarker(id, lat, lng) { this.putMarker( id, @@ -180,10 +210,12 @@ class DashboardMap { my.layer_groups.rider_available.clearLayers(); my.layer_groups.rider_active_jo.clearLayers(); my.layer_groups.customer.clearLayers(); + my.layer_groups.mobile_customer.clearLayers(); // get riders and job orders var riders = response.riders; var jos = response.jos; + var mobile_jos = response.mobile_jos; // job orders $.each(jos, function(id, data) { @@ -193,6 +225,14 @@ class DashboardMap { my.putCustomerMarker(id, lat, lng); }); + // mobile app job orders + $.each(mobile_jos, function(id, data) { + var lat = data.latitude; + var lng = data.longitude; + + my.putMobileCustomerMarker(id, lat, lng); + }); + // riders $.each(riders, function(id, data) { var lat = data.latitude; diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index 5ba473af..cbc4afa7 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -13,7 +13,9 @@ use App\Service\JobOrderCache; use App\Service\RiderCache; use App\Entity\Rider; +use App\Entity\JobOrder; +use App\Ramcar\TransactionOrigin; class HomeController extends Controller { @@ -63,6 +65,24 @@ class HomeController extends Controller $riders[$rider_id]['has_jo'] = true; } + // get JOs with transaction origin TransactionOrigin::MOBILE_APP from list of active_jos + $mobile_jos = []; + foreach ($active_jos as $jo_id => $jo_data) + { + $jo = $em->getRepository(JobOrder::class)->find($jo_id); + if ($jo == null) + { + unset($active_jos[$jo_id]); + continue; + } + + if ($jo->getSource() == TransactionOrigin::MOBILE_APP) + { + $mobile_jos[$jo_id] = $jo_data; + unset($active_jos[$jo_id]); + } + } + // get active riders from cache // get all riders /* @@ -120,6 +140,7 @@ class HomeController extends Controller return $this->json([ 'jos' => $active_jos, 'riders' => $riders, + 'mobile_jos' => $mobile_jos, ]); } diff --git a/src/Controller/ReportController.php b/src/Controller/ReportController.php index 2f72b3fa..94cede5f 100644 --- a/src/Controller/ReportController.php +++ b/src/Controller/ReportController.php @@ -6,10 +6,12 @@ use App\Ramcar\JORejectionReason; use App\Ramcar\ServiceType; use App\Ramcar\JOStatus; use App\Ramcar\InvalidPlateNumber; +use App\Ramcar\JOEventType; use App\Entity\JORejection; use App\Entity\Battery; use App\Entity\JobOrder; +use App\Entity\JOEvent; use App\Entity\Warranty; use App\Entity\CustomerVehicle; use App\Entity\MobileSession; @@ -725,6 +727,116 @@ class ReportController extends Controller } + /** + * @Menu(selected="outlet_list") + */ + public function jobOrderDetailsForm() + { + $this->denyAccessUnlessGranted('report.jo.details', null, 'No access.'); + + return $this->render('report/jo-details/form.html.twig'); + } + + public function jobOrderDetailsSubmit(Request $req, EntityManagerInterface $em) + { + $data = $this->getJobOrderDetails($req, $em); + + $resp = new StreamedResponse(); + $resp->setCallback(function() use ($data) { + // csv output + $csv_handle = fopen('php://output', 'w+'); + fputcsv($csv_handle, [ + 'Job Order ID', + 'Dispatcher', + 'Coordinate Latitude', + 'Coordinate Longitude', + 'Date and Time Hub Assigned for JO', + 'Date Hub Assigned for JO', + 'Time Hub Assigned for JO', + 'Date and Time Rider Assigned for JO', + 'Date Rider Assigned for JO', + 'Time Rider Assigned for JO', + 'Date and Time Created for Invoice', + 'Date and Time Created for JO', + 'Date Created for JO', + 'Time Created for JO', + 'Outlet Name', + 'Rider Name', + 'Service Type', + 'Source', + 'Status', + 'SKU', + + ]); + foreach ($data as $row) + { + fputcsv($csv_handle, $row); + } + + fclose($csv_handle); + }); + + $filename = 'job_order_details_report' . '.csv'; + + $resp->setStatusCode(200); + $resp->headers->set('Content-Type', 'text/csv; charset=utf-8'); + $resp->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '"'); + + return $resp; + } + + /** + * @Menu(selected="outlet_list") + */ + public function jobOrderEventsForm() + { + $this->denyAccessUnlessGranted('report.jo_events', null, 'No access.'); + + return $this->render('report/jo-events/form.html.twig'); + } + + public function jobOrderEventsSubmit(Request $req, EntityManagerInterface $em) + { + $hub_events = $this->getJobOrderEvents($req, JOEventType::HUB_ASSIGN); + $arrive_events = $this->getJobOrderEvents($req, JOEventType::RIDER_ARRIVE); + $accept_events = $this->getJobOrderEvents($req, JOEventType::RIDER_ACCEPT); + + $blanks = []; + array_push($blanks, array("\t","\t","\t","\t")); + + $data = array_merge($hub_events, $blanks, $arrive_events, $blanks, $accept_events); + + $resp = new StreamedResponse(); + $resp->setCallback(function() use ($data) { + // csv output + $csv_handle = fopen('php://output', 'w+'); + fputcsv($csv_handle, [ + 'Job Order ID', + 'Date and Time Created for JO', + 'Date Created for JO', + 'Time Created for JO', + 'Service Type', + 'Type ID', + + ]); + // write hub events + foreach ($data as $row) + { + fputcsv($csv_handle, $row); + } + + fclose($csv_handle); + }); + + $filename = 'job_order_events_report' . '.csv'; + + $resp->setStatusCode(200); + $resp->headers->set('Content-Type', 'text/csv; charset=utf-8'); + $resp->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '"'); + + return $resp; + } + protected function processPopappFile(UploadedFile $csv_file, EntityManagerInterface $em) { // attempt to open file @@ -1162,4 +1274,195 @@ class ReportController extends Controller return $bsize_hash; } + protected function getJobOrderDetails(Request $req, EntityManagerInterface $em) + { + // get query builder + $qb = $this->getDoctrine() + ->getRepository(JobOrder::class) + ->createQueryBuilder('r'); + + // get dates + $raw_date_start = $req->request->get('date_start'); + $raw_date_end = $req->request->get('date_end'); + + $date_start = DateTime::createFromFormat('m/d/Y', $raw_date_start); + $date_end = DateTime::createFromFormat('m/d/Y', $raw_date_end); + + // build query + $query = $qb->where('r.date_create >= :start') + ->andWhere('r.date_create <= :end') + ->setParameter('start', $date_start->format('Y-m-d') . ' 00:00:00') + ->setParameter('end', $date_end->format('Y-m-d') . ' 23:59:59') + ->getQuery(); + + // run query + $jos = $query->getResult(); + + $result = []; + foreach($jos as $jo) + { + // get dispatcher if any + $dispatcher_name = ''; + $dispatcher = $jo->getProcessedBy(); + if ($dispatcher != null) + $dispatcher_name = $dispatcher->getFullName(); + + $coord_long = $jo->getCoordinates()->getLongitude(); + $coord_lat = $jo->getCoordinates()->getLatitude(); + + // find date and time when JO was assigned a hub + $datetime_hub_assign_jo = ''; + $date_hub_assign_jo = ''; + $time_hub_assign_jo = ''; + $hub_assign_event_type = JOEventType::HUB_ASSIGN; + $hub_assign_events = $em->getRepository(JOEvent::class)->findBy(['job_order' => $jo, 'type_id'=> $hub_assign_event_type], ['date_happen' => 'DESC']); + if ($hub_assign_events != null) + { + // TODO: what happens if JO was reassigned a hub multiple times? + // right now, this gets the last time hub was assigned. + foreach ($hub_assign_events as $hub_assign_event) + { + $datetime_hub_assign_jo = $hub_assign_event->getDateHappen()->format('m-d-Y H:i'); + $date_hub_assign_jo = $hub_assign_event->getDateHappen()->format('m-d-Y'); + $time_hub_assign_jo = $hub_assign_event->getDateHappen()->format('H:i'); + } + } + + // find date and time when JO was assigned a rider + $datetime_rider_assign_jo = ''; + $date_rider_assign_jo = ''; + $time_rider_assign_jo = ''; + $rider_assign_event_type = JOEventType::RIDER_ASSIGN; + $rider_assign_events = $em->getRepository(JOEvent::class)->findBy(['job_order' => $jo, 'type_id'=> $rider_assign_event_type], ['date_happen' => 'DESC']); + if ($rider_assign_events != null) + { + foreach ($rider_assign_events as $rider_assign_event) + { + // TODO: what happens if JO was reassigned a rider multiple times? + // right now, this gets the last time rider was assigned. + $datetime_rider_assign_jo = $rider_assign_event->getDateHappen()->format('m-d-Y H:i'); + $date_rider_assign_jo = $rider_assign_event->getDateHappen()->format('m-d-Y'); + $time_rider_assign_jo = $rider_assign_event->getDateHappen()->format('H:i'); + } + } + + // get invoice date create and item sku if any + $datetime_create_invoice = ''; + $sku = ''; + $invoice = $jo->getInvoice(); + if ($invoice != null) + { + $invoice_create = $invoice->getDateCreate(); + $datetime_create_invoice = $invoice_create->format('m-d-Y H:i'); + + // get item sku + $invoice_items = $invoice->getItems(); + foreach ($invoice_items as $item) + { + $battery = $item->getBattery(); + if ($battery != null) + $sku = $battery->getModel()->getName() . ' ' . $battery->getSize()->getName(); + } + } + + // get jo date create + $datetime_create_jo = ''; + $date_create_jo = ''; + $time_create_jo = ''; + $jo_create = $jo->getDateCreate(); + + $datetime_create_jo = $jo_create->format('m-d-Y H:i'); + $date_create_jo = $jo_create->format('m-d-Y'); + $time_create_jo = $jo_create->format('H:i'); + + // get hub if any + $hub_name = ''; + $hub = $jo->getHub(); + if ($hub != null) + $hub_name = $hub->getName(); + + // get rider if any + $rider_name = ''; + $rider = $jo->getRider(); + if ($rider != null) + $rider_name = $rider->getFullName(); + + $result[] = [ + $jo->getID(), + $dispatcher_name, + $coord_lat, + $coord_long, + $datetime_hub_assign_jo, + $date_hub_assign_jo, + $time_hub_assign_jo, + $datetime_rider_assign_jo, + $date_rider_assign_jo, + $time_rider_assign_jo, + $datetime_create_invoice, + $datetime_create_jo, + $date_create_jo, + $time_create_jo, + $hub_name, + $rider_name, + $jo->getServiceType(), + $jo->getSource(), + $jo->getStatus(), + $sku, + ]; + } + + return $result; + } + + protected function getJobOrderEvents(Request $req, $event_type) + { + // get query builder + $qb = $this->getDoctrine() + ->getRepository(JOEvent::class) + ->createQueryBuilder('r'); + + // get dates + $raw_date_start = $req->request->get('date_start'); + $raw_date_end = $req->request->get('date_end'); + + $date_start = DateTime::createFromFormat('m/d/Y', $raw_date_start); + $date_end = DateTime::createFromFormat('m/d/Y', $raw_date_end); + + // build query + $query = $qb->where('r.date_create >= :start') + ->andWhere('r.date_create <= :end') + ->andWhere('r.type_id = :event_type') + ->setParameter('start', $date_start->format('Y-m-d') . ' 00:00:00') + ->setParameter('end', $date_end->format('Y-m-d') . ' 23:59:59') + ->setParameter('event_type', $event_type) + ->getQuery(); + + // run query + $jo_events = $query->getResult(); + + $result = []; + foreach ($jo_events as $jo_event) + { + $datetime_jo_create = ''; + $date_jo_create = ''; + $time_jo_create = ''; + + $jo = $jo_event->getJobOrder(); + $jo_date_create = $jo->getDateCreate(); + $datetime_jo_create = $jo_date_create->format('m-d-Y H:i'); + $date_jo_create = $jo_date_create->format('m-d-Y'); + $time_jo_create = $jo_date_create->format('H:i'); + + $result[] = [ + $jo->getID(), + $datetime_jo_create, + $date_jo_create, + $time_jo_create, + $jo->getServiceType(), + $jo_event->getTypeID(), + ]; + } + + return $result; + } } diff --git a/templates/base.html.twig b/templates/base.html.twig index ec13aa91..095df727 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -142,6 +142,26 @@ +