diff --git a/.env.dist b/.env.dist index bd82f980..9fc0a002 100644 --- a/.env.dist +++ b/.env.dist @@ -74,3 +74,6 @@ MAPTILER_API_KEY=map_tiler_api_key # API version API_VERSION=insert_api_version_here + +#SSL_ENABLE for websockets +SSL_ENABLE=set_to_true_or_false diff --git a/config/acl.yaml b/config/acl.yaml index abd73118..81b49726 100644 --- a/config/acl.yaml +++ b/config/acl.yaml @@ -270,6 +270,8 @@ access_keys: label: Autoassign Test - id: jo_hub.list label: Hub View + - id: jo_cancel.fulfill + label: Fulfill Cancelled JO - id: support label: Customer Support Access diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index 1c848030..ade96bcc 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -8,3 +8,4 @@ twig: mqtt_host: "%env(MQTT_WS_HOST)%" mqtt_port: "%env(MQTT_WS_PORT)%" dashboard_enable: "%env(DASHBOARD_ENABLE)%" + ssl_enable: "%env(SSL_ENABLE)%" diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml index 586ed81f..621195b1 100644 --- a/config/routes/job_order.yaml +++ b/config/routes/job_order.yaml @@ -253,3 +253,12 @@ jo_hub_view_form: controller: App\Controller\JobOrderController::hubViewForm methods: [GET] +jo_fulfill_cancel_submit: + path: /job-order/fulfillcancel/{id} + controller: App\Controller\JobOrderController::fulfillCancelSubmit + methods: [POST] + +jo_cancel_reasons: + path: /ajax/jo_cancel_reasons + controller: App\Controller\JobOrderController::cancelReasons + methods: [GET] diff --git a/public/assets/js/map_mqtt.js b/public/assets/js/map_mqtt.js index 0e6bdb31..250e00c6 100644 --- a/public/assets/js/map_mqtt.js +++ b/public/assets/js/map_mqtt.js @@ -1,7 +1,8 @@ class MapEventHandler { - constructor(options, dashmap) { + constructor(options, dashmap, ssl) { this.options = options; this.dashmap = dashmap; + this.ssl = ssl; } connect(user_id, host, port) { @@ -11,7 +12,7 @@ class MapEventHandler { this.mqtt = new Paho.MQTT.Client(host, port, client_id); var options = { - // useSSL: true, + useSSL: this.ssl, timeout: 3, invocationContext: this, onSuccess: this.onConnect.bind(this), diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index f23fc73c..11446515 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -6,6 +6,7 @@ use App\Ramcar\JOStatus; use App\Ramcar\InvoiceCriteria; use App\Ramcar\CMBServiceType; use App\Ramcar\ServiceType; +use App\Ramcar\JOCancelReasons; use App\Entity\CustomerVehicle; use App\Entity\Promo; @@ -631,7 +632,6 @@ class JobOrderController extends Controller $params['vmfgs'] = $em->getRepository(VehicleManufacturer::class)->findAll(); $params['vmakes'] = $em->getRepository(Vehicle::class)->findAll(); $params['return_url'] = $this->generateUrl('jo_all'); - $params['submit_url'] = ''; $params['map_js_file'] = $gis->getJSJOFile(); $template = $params['template']; @@ -1153,6 +1153,38 @@ class JobOrderController extends Controller return $this->render($template, $params); } + public function fulfillCancelSubmit(Request $req, JobOrderHandlerInterface $jo_handler, $id) + { + $this->denyAccessUnlessGranted('jo_cancel.fulfill', null, 'No access.'); + + // TODO: make the service function to fulfill the cancelled JO + $error_array = []; + $result = $jo_handler->fulfillCancelledJobOrder($req, $id); + + $error_array = $result['error_array']; + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + // ajax call + public function cancelReasons() + { + return $this->json([ + 'cancel_reasons' => JOCancelReasons::getCollection(), + ]); + } /** * @Menu(selected="jo_autoassign") diff --git a/src/Entity/JobOrder.php b/src/Entity/JobOrder.php index 8bcdbb77..6309dc6d 100644 --- a/src/Entity/JobOrder.php +++ b/src/Entity/JobOrder.php @@ -334,6 +334,18 @@ class JobOrder */ protected $phone_mobile; + // flag if customer is willing to wait + /** + * @ORM\Column(type="boolean", nullable=true) + */ + protected $flag_will_wait; + + // reason for not willing to wait + /** + * @ORM\Column(type="string", length=80, nullable=true) + */ + protected $reasons_not_waiting; + public function __construct() { $this->date_create = new DateTime(); @@ -356,6 +368,8 @@ class JobOrder $this->meta = []; $this->phone_mobile = ''; + + $this->flag_will_wait = true; } public function getID() @@ -962,5 +976,25 @@ class JobOrder return $this->phone_mobile; } + public function setWillingToWait($flag = true) + { + $this->flag_will_wait = $flag; + return $this; + } + public function isWillingToWait() + { + return $this->flag_will_wait; + } + + public function setReasonsNotWait($reasons) + { + $this->reasons_not_waiting = $reasons; + return $this; + } + + public function getReasonsNotWait() + { + return $this->reasons_not_waiting; + } } diff --git a/src/Ramcar/CustomerNotWaitReason.php b/src/Ramcar/CustomerNotWaitReason.php new file mode 100644 index 00000000..142723b2 --- /dev/null +++ b/src/Ramcar/CustomerNotWaitReason.php @@ -0,0 +1,16 @@ + 'Emergency', + 'use_vehicle_now' => 'Need to Use Vehicle Now', + 'with_appointment' => 'With Appointment', + ]; +} diff --git a/src/Ramcar/JOCancelReasons.php b/src/Ramcar/JOCancelReasons.php new file mode 100644 index 00000000..b5a7a99c --- /dev/null +++ b/src/Ramcar/JOCancelReasons.php @@ -0,0 +1,26 @@ + 'Wrong Battery', + 'customer_no_show' => 'Customer No Show', + 'reschedule' => 'Reschedule', + 'location_change' => 'Change Location', + 'battery_working' => 'Battery is Already Working', + 'late_delivery' => 'Late Delivery', + 'customer_bought_new_battery' => 'Customer Already Bought New Battery from Nearby Outlet', + 'battery_no_stock' => 'No Stock of Battery', + ]; +} diff --git a/src/Ramcar/TransactionOrigin.php b/src/Ramcar/TransactionOrigin.php index c4d69ad2..8d4c12aa 100644 --- a/src/Ramcar/TransactionOrigin.php +++ b/src/Ramcar/TransactionOrigin.php @@ -10,6 +10,7 @@ class TransactionOrigin extends NameValue const VIP = 'vip'; const MOBILE_APP = 'mobile_app'; const WALK_IN = 'walk_in'; + const LAZADA = 'lazada'; // TODO: for now, resq also gets the walk-in option const COLLECTION = [ @@ -19,5 +20,6 @@ class TransactionOrigin extends NameValue 'vip' => 'VIP', 'mobile_app' => 'Mobile App', 'walk_in' => 'Walk-in', + 'lazada' => 'Lazada', ]; } diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index e6cc6ec8..5e8eb2be 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -37,6 +37,7 @@ use App\Ramcar\ModeOfPayment; use App\Ramcar\TransactionOrigin; use App\Ramcar\FacilitatedType; use App\Ramcar\JORejectionReason; +use App\Ramcar\CustomerNotWaitReason; use App\Service\InvoiceGeneratorInterface; use App\Service\JobOrderHandlerInterface; @@ -183,6 +184,12 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface else $row['assignor'] = $orow->getAssignedBy()->getFullName(); + $hub_facilitated = $orow->getFacilitatedBy(); + if ($hub_facilitated == null) + $row['hub_facilitated'] = ''; + else + $row['hub_facilitated'] = $orow->getFacilitatedBy()->getName(); + $rows[] = $row; } @@ -314,6 +321,17 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } } + // check if landmark is set + if (empty($req->request->get('landmark'))) + $error_array['landmark'] = 'Landmark is required.'; + + // check if customer is not willing to wait + $will_wait = $req->request->get('flag_will_wait'); + if ($will_wait) + $reason = ''; + else + $reason = $req->request->get('no_wait_reason'); + // TODO: check status before saving since JO might already // have a status that needs to be retained @@ -343,7 +361,9 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setORName($req->request->get('or_name')) ->setPromoDetail($req->request->get('promo_detail')) ->setModeOfPayment($req->request->get('mode_of_payment')) - ->setLandmark($req->request->get('landmark')); + ->setLandmark($req->request->get('landmark')) + ->setWillingToWait($req->request->get('flag_will_wait', false)) + ->setReasonsNotWait($reason); // check if user is null, meaning call to create came from API if ($user != null) @@ -434,6 +454,17 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } + // check if landmark is set + if (empty($req->request->get('landmark'))) + $error_array['landmark'] = 'Landmark is required.'; + + // check if customer is not willing to wait + $will_wait = $req->request->get('flag_will_wait'); + if ($will_wait) + $reason = ''; + else + $reason = $req->request->get('no_wait_reason'); + if (empty($error_array)) { // get current user @@ -458,7 +489,9 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setORName($req->request->get('or_name')) ->setPromoDetail($req->request->get('promo_detail')) ->setModeOfPayment($req->request->get('mode_of_payment')) - ->setLandmark($req->request->get('landmark')); + ->setLandmark($req->request->get('landmark')) + ->setWillingToWait($req->request->get('flag_will_wait', false)) + ->setReasonsNotWait($reason); // did they change invoice? $invoice_items = $req->request->get('invoice_items', []); @@ -492,6 +525,8 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setTypeID(JOEventType::OPEN_EDIT) ->setJobOrder($obj); + error_log('open edit?'); + if ($user != null) { $event->setUser($user); @@ -587,6 +622,17 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $fac_by = null; } + // check if landmark is set + if (empty($req->request->get('landmark'))) + $error_array['landmark'] = 'Landmark is required.'; + + // check if customer is not willing to wait + $will_wait = $req->request->get('flag_will_wait'); + if ($will_wait) + $reason = ''; + else + $reason = $req->request->get('no_wait_reason'); + if (empty($error_array)) { // coordinates @@ -606,7 +652,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setDeliveryAddress($req->request->get('delivery_address')) ->setFacilitatedType($fac_type) ->setFacilitatedBy($fac_by) - ->setHub($hub); + ->setHub($hub) + ->setLandmark($req->request->get('landmark')) + ->setWillingToWait($req->request->get('flag_will_wait', false)) + ->setReasonsNotWait($reason); // validate $errors = $this->validator->validate($obj); @@ -680,6 +729,17 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } } + // check if landmark is set + if (empty($req->request->get('landmark'))) + $error_array['landmark'] = 'Landmark is required.'; + + // check if customer is not willing to wait + $will_wait = $req->request->get('flag_will_wait'); + if ($will_wait) + $reason = ''; + else + $reason = $req->request->get('no_wait_reason'); + // get current user $user = $this->security->getUser(); @@ -700,7 +760,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setDateAssign(new DateTime()) - ->setRider($rider); + ->setRider($rider) + ->setLandmark($req->request->get('landmark')) + ->setWillingToWait($req->request->get('flag_will_wait', false)) + ->setReasonsNotWait($reason); if ($user != null) { @@ -763,6 +826,17 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; } + // check if landmark is set + if (empty($req->request->get('landmark'))) + $error_array['landmark'] = 'Landmark is required.'; + + // check if customer is not willing to wait + $will_wait = $req->request->get('flag_will_wait'); + if ($will_wait) + $reason = ''; + else + $reason = $req->request->get('no_wait_reason'); + if (empty($error_array)) { // coordinates $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); @@ -777,7 +851,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setDeliveryInstructions($req->request->get('delivery_instructions')) ->setTier1Notes($req->request->get('tier1_notes')) ->setTier2Notes($req->request->get('tier2_notes')) - ->setDeliveryAddress($req->request->get('delivery_address')); + ->setDeliveryAddress($req->request->get('delivery_address')) + ->setLandmark($req->request->get('landmark')) + ->setWillingToWait($req->request->get('flag_will_wait', false)) + ->setReasonsNotWait($reason); // validate $errors = $this->validator->validate($obj); @@ -883,6 +960,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface throw new NotFoundHttpException('The item does not exist'); $cancel_reason = $req->request->get('cancel_reason'); + error_log($cancel_reason); $obj->cancel($cancel_reason); // the event @@ -947,6 +1025,19 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } } + // check if landmark is set + if (empty($req->request->get('landmark'))) + $error_array['landmark'] = 'Landmark is required.'; + + error_log($req->request->get('landmark')); + + // check if customer is not willing to wait + $will_wait = $req->request->get('flag_will_wait'); + if ($will_wait) + $reason = ''; + else + $reason = $req->request->get('no_wait_reason'); + if (empty($error_array)) { // rider mqtt event @@ -974,6 +1065,9 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setHub($hub) + ->setLandmark($req->request->get('landmark')) + ->setWillingToWait($req->request->get('flag_will_wait', false)) + ->setReasonsNotWait($reason) ->clearRider(); if ($user != null) @@ -1148,6 +1242,17 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } } + // check if landmark is set + if (empty($req->request->get('landmark'))) + $error_array['landmark'] = 'Landmark is required.'; + + // check if customer is not willing to wait + $will_wait = $req->request->get('flag_will_wait'); + if ($will_wait) + $reason = ''; + else + $reason = $req->request->get('no_wait_reason'); + if (empty($error_array)) { // rider mqtt event // NOTE: need to send this before saving because rider will be cleared @@ -1174,7 +1279,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setTier2Notes($req->request->get('tier2_notes')) ->setDeliveryAddress($req->request->get('delivery_address')) ->setDateAssign(new DateTime()) - ->setRider($rider); + ->setRider($rider) + ->setLandmark($req->request->get('landmark')) + ->setWillingToWait($req->request->get('flag_will_wait', false)) + ->setReasonsNotWait($reason); if ($user != null) { @@ -1562,6 +1670,18 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $this->fillDropdownParameters($params); $this->fillFormTags($params); + // check JO status to determine the mode and submit_url to return + if ($obj->getStatus() == JOStatus::CANCELLED) + { + $params['mode'] = 'fulfill-cancel'; + $params['submit_url'] = 'jo_fulfill_cancel_submit'; + } + else + { + $params['mode'] = 'update-all'; + $params['submit_url'] = ''; + } + // get template to display $params['template'] = $this->getTwigTemplate('jo_all_form'); @@ -2434,6 +2554,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $params['facilitated_types'] = FacilitatedType::getCollection(); $params['facilitated_hubs'] = $fac_hubs; $params['sources'] = TransactionOrigin::getCollection(); + $params['no_wait_reasons'] = CustomerNotWaitReason::getCollection(); } protected function initFormTags(&$params) @@ -2765,20 +2886,35 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->andWhere('h.flag_hub_view = :flag_hub_view') ->setParameter('flag_hub_view', true); } + 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); + } + else + { + $c_date = new DateTime(); + $start_curr_date = $c_date->format('Y-m-d') . ' ' . '00:00:00'; + $end_curr_date = $c_date->format('Y-m-d') . ' ' . '23:59:00'; - $c_date = new DateTime(); - $start_curr_date = $c_date->format('Y-m-d') . ' ' . '00:00:00'; - $end_curr_date = $c_date->format('Y-m-d') . ' ' . '23:59:00'; + $start_current_date = DateTime::createFromFormat('Y-m-d H:i:s', $start_curr_date); + $end_current_date = DateTime::createFromFormat('Y-m-d H:i:s', $end_curr_date); - $start_current_date = DateTime::createFromFormat('Y-m-d H:i:s', $start_curr_date); - $end_current_date = DateTime::createFromFormat('Y-m-d H:i:s', $end_curr_date); - - $query->andWhere('q.date_schedule >= :start_current_date') - ->andWhere('q.date_schedule <= :end_current_date') - ->andWhere('q.status IN (:statuses)') - ->setParameter('start_current_date', $start_current_date) - ->setParameter('end_current_date', $end_current_date) - ->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY); + $query->andWhere('q.date_schedule >= :start_current_date') + ->andWhere('q.date_schedule <= :end_current_date') + ->andWhere('q.status IN (:statuses)') + ->setParameter('start_current_date', $start_current_date) + ->setParameter('end_current_date', $end_current_date) + ->setParameter('statuses', $status, Connection::PARAM_STR_ARRAY); + } break; default: $query->where('q.status = :status') @@ -2954,4 +3090,37 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface return $params; } + public function fulfillCancelledJobOrder(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'); + + $obj->fulfill(); + + // 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); + $em->flush(); + } + } diff --git a/templates/home.html.twig b/templates/home.html.twig index 69cb59c0..ebe8cff3 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -45,7 +45,7 @@ function initMap(r_markers, c_markers, icons) { return dashmap; } -function initEventHandler(dashmap) { +function initEventHandler(dashmap, icons, ssl) { var options = { 'track_jo': true, 'track_rider': true, @@ -58,7 +58,7 @@ function initEventHandler(dashmap) { }, }; - var event_handler = new MapEventHandler(options, dashmap); + var event_handler = new MapEventHandler(options, dashmap, ssl); event_handler.connect('{{ app.user.getID }}', '{{ mqtt_host }}', {{ mqtt_port }}); } @@ -94,8 +94,13 @@ var icons = { var r_markers = {}; var c_markers = {}; + var ssl = false; + {% if ssl_enable == 'true' %} + ssl = true; + {% endif %} + var dashmap = initMap(r_markers, c_markers, icons); - initEventHandler(dashmap, icons); + initEventHandler(dashmap, icons, ssl); {% endif %} diff --git a/templates/job-order/form.html.twig b/templates/job-order/form.html.twig index bba70b39..95dae4e0 100644 --- a/templates/job-order/form.html.twig +++ b/templates/job-order/form.html.twig @@ -292,6 +292,22 @@
+