From bfc2ee089055d8e37aa6a92ba16096aa457ee737 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Fri, 9 Jul 2021 11:00:57 +0000 Subject: [PATCH] Modify JobOrderController. #591 --- config/api_acl.yaml | 5 + src/Controller/ResqAPI/JobOrderController.php | 622 +++++++++--------- 2 files changed, 298 insertions(+), 329 deletions(-) diff --git a/config/api_acl.yaml b/config/api_acl.yaml index 2499e003..9f280586 100644 --- a/config/api_acl.yaml +++ b/config/api_acl.yaml @@ -141,3 +141,8 @@ access_keys: label: Get Rider Status - id: mobile_rider.rating.add label: Add Rider Rating + - id: job_order + label: Mobile Job Order Access + acls: + - id: mobile_jo.request + label: Request Job Order diff --git a/src/Controller/ResqAPI/JobOrderController.php b/src/Controller/ResqAPI/JobOrderController.php index 1035c986..e22bd13b 100644 --- a/src/Controller/ResqAPI/JobOrderController.php +++ b/src/Controller/ResqAPI/JobOrderController.php @@ -11,10 +11,7 @@ use Doctrine\ORM\EntityManagerInterface; use CrEOF\Spatial\PHP\Types\Geometry\Point; use Catalyst\APIBundle\Controller\APIController; -// TODO: what do we use for response? APIResponse or APIResult? -// APIResult is what is used by APIController. APIResponse is what is used by CAPI use Catalyst\APIBundle\Response\APIResponse; -use App\Ramcar\APIResult; use App\Ramcar\WarrantyClass; use App\Ramcar\JOStatus; use App\Ramcar\AutoAssignStatus; @@ -37,6 +34,7 @@ use App\Service\HubDistributor; use App\Service\HubFilterLogger; use App\Service\HubFilteringGeoChecker; use App\Service\MapTools; +use App\Service\MobileAPIHandler; use App\Entity\JobOrder; use App\Entity\CustomerVehicle; @@ -55,15 +53,15 @@ class JobOrderController extends APIController $this->acl_gen = $acl_gen; } - // TODO: modify for MobileUser - // break this down into smaller functions - // do we still use this? public function requestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo, InventoryManager $im, MQTTClient $mclient, RiderAssignmentHandlerInterface $rah, PromoLogger $promo_logger, EntityManagerInterface $em, HubSelector $hub_select, - HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger, HubFilteringGeoChecker $hub_geofence) + HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger, HubFilteringGeoChecker $hub_geofence, + MobileAPIHandler $mah) { - // check required parameters and api key + $this->denyAccessUnlessGranted('mobile_jo.request', null, 'No access.'); + + // check required parameters $required_params = [ 'service_type', 'cv_id', @@ -74,267 +72,59 @@ class JobOrderController extends APIController 'warranty', 'mode_of_payment', ]; - $res = $this->checkParamsAndKey($req, $em, $required_params); - if ($res->isError()) - return $res->getReturnResponse(); - // trade in type - $trade_in = $req->request->get('trade_in'); + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); - // address - $address = $req->request->get('delivery_address', 'Set by mobile application'); + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); - // instructions - $instructions = $req->request->get('delivery_instructions', ''); + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); - // longitude and latitude - $long = $req->request->get('long'); - $lat = $req->request->get('lat'); + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); - // geofence - $is_covered = $geo->isCovered($long, $lat); - if (!$is_covered) - { - // TODO: put geofence error message in config file somewhere - $res->setError(true) - ->setErrorMessage('Oops! Our service is limited to some areas in Metro Manila, Laguna, and Baguio only. We will update you as soon as we are able to cover your area'); - return $res->getReturnResponse(); - } + // validate request + $msg = $this->validateJORequest($req); + if ($msg) + return new APIResponse(false, $msg); $jo = new JobOrder(); - $jo->setSource(TransactionOrigin::MOBILE_APP) - ->setStatus(JOStatus::PENDING) - ->setDeliveryInstructions('') - ->setTier1Notes('') - ->setTier2Notes('') - ->setDeliveryAddress($address) - ->setTradeInType($trade_in) - ->setDeliveryInstructions($instructions) - // TODO: error check for valid mode of payment - ->setModeOfPayment($req->request->get('mode_of_payment')); + $this->setJOObject($jo, $req); // customer - $cust = $this->session->getCustomer(); + $cust = $mobile_user->getCustomer(); if ($cust == null) - { - $res->setError(true) - ->setErrorMessage('No customer information found'); - return $res->getReturnResponse(); - } + return new APIResponse(false, 'No customer information found'); + $jo->setCustomer($cust); - // validate service type - $stype = $req->request->get('service_type'); - if (!ServiceType::validate($stype)) - { - $res->setError(true) - ->setErrorMessage('Invalid service type'); - return $res->getReturnResponse(); - } - $jo->setServiceType($stype); - - // validate warranty - $warr = $req->request->get('warranty'); - if (!WarrantyClass::validate($warr)) - { - $res->setError(true) - ->setErrorMessage('Invalid warranty class'); - return $res->getReturnResponse(); - } - $jo->setWarrantyClass($warr); - - // set coordinates - $point = new Point($long, $lat); - $jo->setCoordinates($point); - // make invoice criteria $icrit = new InvoiceCriteria(); - $icrit->setServiceType($stype); - - // check promo - $promo_id = $req->request->get('promo_id'); - if (!empty($promo_id)) - { - $promo = $em->getRepository(Promo::class)->find($promo_id); - if ($promo == null) - { - $res->setError(true) - ->setErrorMessage('Invalid promo id'); - return $res->getReturnResponse(); - } - - // put in criteria - $icrit->addPromo($promo); - } - - // check customer vehicle - $cv = $em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id')); - if ($cv == null) - { - $res->setError(true) - ->setErrorMessage('Invalid customer vehicle id'); - return $res->getReturnResponse(); - } - $icrit->setCustomerVehicle($cv); - $jo->setCustomerVehicle($cv); - - // check if customer owns vehicle - if ($cust->getID() != $cv->getCustomer()->getID()) - { - $res->setError(true) - ->setErrorMessage('Customer does not own vehicle'); - return $res->getReturnResponse(); - } - - // check battery - $batt_id = $req->request->get('batt_id'); - if ($batt_id != null) - { - $batt = $em->getRepository(Battery::class)->find($batt_id); - if ($batt == null) - { - $res->setError(true) - ->setErrorMessage('Invalid battery id'); - return $res->getReturnResponse(); - } - } - else - $batt = null; - - /* - // put battery in criteria - $icrit->addBattery($batt); - */ - - // check trade-in - // only allow motolite, other, none - switch ($trade_in) - { - case TradeInType::MOTOLITE: - case TradeInType::OTHER: - break; - - default: - $trade_in = ''; - break; - } - - $icrit->addEntry($batt, $trade_in, 1); + $msg = $this->setInvoiceCriteria($em, $icrit, $req, $cust, $jo); + if ($msg != null) + return new APIResponse(false, $msg); // send to invoice generator $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); - // set more hub criteria fields + // set hub criteria details $hub_criteria = new HubCriteria(); - $hub_criteria->setPoint($jo->getCoordinates()); - - if ($hub_geofence->isCovered($long, $lat)) - { - // TODO: set this properly, since the other flags - // are on default values. - // if true, set other values for HubCriteria - // error_log('Area is covered by hub filtering'); - $hub_criteria->setJoType($jo->getServiceType()) - ->setPaymentMethod($jo->getModeOfPayment()) - ->setRoundRobin(true); - } - - // add battery to items - $sku = $batt->getSAPCode(); - if (!empty($sku)) - $hub_criteria->addItem($batt->getSAPCode(), 1); + $this->setHubCriteria($em, $hub_geofence, $hub_criteria, $jo); // find nearest hubs $nearest_hubs = $hub_select->find($hub_criteria); if (!empty($nearest_hubs)) - { - // go through the hub list, find the nearest hub - // with an available rider - //error_log('found nearest hub ' . $nearest_hub->getID()); - foreach ($nearest_hubs as $nearest_hub) - { - $available_riders = $nearest_hub['hub']->getAvailableRiders(); - if (count($available_riders) >= 1) - { - $assigned_rider = null; - if (count($available_riders) == 1) - { - $assigned_rider = $available_riders[0]; - } - else - { - // TODO: the setting of riders into an array - // will no longer be necessary when the contents - // of randomizeRider changes - $riders = []; - foreach ($available_riders as $rider) - { - $riders[] = $rider; - } - - $assigned_rider = $this->randomizeRider($riders); - } - - $jo->setHub($nearest_hub['hub']); - $jo->setRider($assigned_rider); - $jo->setStatus(JOStatus::ASSIGNED); - $jo->setStatusAutoAssign(AutoAssignStatus::HUB_AND_RIDER_ASSIGNED); - - $assigned_rider->setAvailable(false); - - // update redis hub_jo_count for hub - $hub_dist->incrementJoCountForHub($nearest_hub['hub']); - - // break out of loop - break; - } - else - { - // log hub into hub_filter_log - $hub_filter_logger->logFilteredHub($nearest_hub['hub'], 'no_available_rider'); - // continue to go through list to find hub with an available rider - } - } - } + $this->assignRider($nearest_hubs, $jo, $hub_filter_logger); $em->persist($jo); $em->persist($invoice); - // add event log for JO - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::CREATE) - ->setJobOrder($jo); - $em->persist($event); - - // check JO status - if ($jo->getStatus() == JOStatus::ASSIGNED) - { - // add event logs for hub and rider assignments - $hub_assign_event = new JOEvent(); - $hub_assign_event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::HUB_ASSIGN) - ->setJobOrder($jo); - - $em->persist($hub_assign_event); - - $rider_assign_event = new JOEvent(); - $rider_assign_event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::RIDER_ASSIGN) - ->setJobOrder($jo); - - $em->persist($rider_assign_event); - - // user mqtt event - $payload = [ - 'event' => 'outlet_assign' - ]; - $mclient->sendEvent($jo, $payload); - - $rah->assignJobOrder($jo, $jo->getRider()); - } + $this->processEvents($em, $jo, $rah, $mclient); $em->flush(); @@ -364,40 +154,9 @@ class JobOrderController extends APIController 'invoice' => $invoice_data ]; - // check service type - if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) - { - $customer = $cv->getCustomer(); - $customer_tags = $customer->getCustomerTagObjects(); - if (!empty($customer_tags)) - { - foreach ($customer_tags as $customer_tag) - { - if ($customer_tag->getID() == $invoice->getUsedCustomerTagId()) - { - // remove associated entity - $customer->removeCustomerTag($customer_tag); + $this->removeCustomerTag($jo, $cv, $promo_logger, $mobile_user->getID()); - // log the availment of promo from customer - $created_by = $req->query->get('api_key');; - $cust_id = $jo->getCustomer()->getID(); - $cust_fname = $jo->getCustomer()->getFirstName(); - $cust_lname = $jo->getCustomer()->getLastName(); - $jo_id = $jo->getID(); - $invoice_id = $jo->getInvoice()->getID(); - // TODO: check if we store total price of invoice or just the discounted amount - $amount = $jo->getInvoice()->getTotalPrice(); - $promo_logger->logPromoInfo($created_by, $cust_id, $cust_fname, $cust_lname, $jo_id, - $invoice_id, $amount); - } - } - } - } - - // set data - $res->setData($data); - - return $res->getReturnResponse(); + return new APIResponse(true, 'Job order creatd', $data); } // TODO: modify for MobileUser @@ -1644,87 +1403,292 @@ class JobOrderController extends APIController return $time_selected; } - // TODO: since we broke the functions into separate files, we need - // to figure out how to make this accessible to all ResqAPI controllers - protected function checkParamsAndKey(Request $req, $em, $params) + protected function validateJORequest(Request $req) { - // TODO: depends on what we decide to return - // returns APIResult object - $res = new APIResult(); - - // check for api_key in query string - $api_key = $req->query->get('api_key'); - if (empty($api_key)) + // geofence + $is_covered = $geo->isCovered($long, $lat); + if (!$is_covered) { - $res->setError(true) - ->setErrorMessage('Missing API key'); - return $res; + // TODO: put geofence error message in config file somewhere + $msg = 'Oops! Our service is limited to some areas in Metro Manila, Laguna, and Baguio only. We will update you as soon as we are able to cover your area'; + return $msg; } - // check missing parameters - $missing = $this->checkMissingParameters($req, $params); - if (count($missing) > 0) + // validate service type + $stype = $req->request->get('service_type'); + if (!ServiceType::validate($stype)) { - $miss_string = implode(', ', $missing); - $res->setError(true) - ->setErrorMessage('Missing parameter(s): ' . $miss_string); - return $res; + $msg = 'Invalid service type'; + return $msg; } - // check api key - $mobile_user = $this->checkAPIKey($em, $req->query->get('api_key')); - if ($mobile_user == null) + // validate warranty + $warr = $req->request->get('warranty'); + if (!WarrantyClass::validate($warr)) { - $res->setError(true) - ->setErrorMessage('Invalid API Key'); - return $res; + $msg = 'Invalid warranty class'; + return $msg; } - // store session - $this->session = $sess; + // validate payment method + $payment_method = $req->request->get('mode_of_payment'); + if (!ModeOfPayment::validate($payment_method)) + { + $msg = 'Invalid payment method'; + return $msg; + } - return $res; + return null; } - // TODO: this might not be needed if we use APIController's checkRequiredParameters - // or we put this into a service? - protected function checkMissingParameters(Request $req, $params = []) + protected function setJOObject(JobOrder $jo, Request $req) { - $missing = []; + // longitude and latitude + $long = $req->request->get('long'); + $lat = $req->request->get('lat'); - // check if parameters are there - foreach ($params as $param) + $point = new Point($long, $lat); + + $jo->setSource(TransactionOrigin::MOBILE_APP) + ->setStatus(JOStatus::PENDING) + ->setDeliveryInstructions('') + ->setTier1Notes('') + ->setTier2Notes('') + ->setDeliveryAddress($req->request->get('delivery_address', 'Set by mobile application')) + ->setTradeInType($req->request->get('trade_in')) + ->setDeliveryInstructions($req->request->get('delivery_instructions', '')) + ->setModeOfPayment($req->request->get('mode_of_payment')) + ->setServiceType($req->request->get('service_type')) + ->setWarrantyClass($req->request->get('warranty')) + ->setCoordinates($point); + } + + protected function setInvoiceCriteria(EntityManagerInterface $em, InvoiceCriteria $icrit, Request $req, + Customer $cust, JobOrder $jo) + { + $icrit->setServiceType($req->request->get('service_type')); + + // check promo + $promo_id = $req->request->get('promo_id'); + if (!empty($promo_id)) { - if ($req->getMethod() == 'GET') + $promo = $em->getRepository(Promo::class)->find($promo_id); + if ($promo == null) { - $check = $req->query->get($param); - if (empty($check)) - $missing[] = $param; + $msg = 'Invalid promo id'; + return $msg; } - else if ($req->getMethod() == 'POST') + + // put in criteria + $icrit->addPromo($promo); + } + + // check customer vehicle + $cv = $em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id')); + if ($cv == null) + { + $msg = 'Invalid customer vehicle id'; + return $msg; + } + $icrit->setCustomerVehicle($cv); + $jo->setCustomerVehicle($cv); + + // check if customer owns vehicle + if ($cust->getID() != $cv->getCustomer()->getID()) + { + $msg = 'Customer does not own vehicle'; + return $msg; + } + + // check battery + $batt_id = $req->request->get('batt_id'); + if ($batt_id != null) + { + $batt = $em->getRepository(Battery::class)->find($batt_id); + if ($batt == null) { - $check = $req->request->get($param); - if (empty($check)) - $missing[] = $param; + $msg = 'Invalid battery id'); + return $msg; + } + } + else + $batt = null; + + /* + // put battery in criteria + $icrit->addBattery($batt); + */ + + // check trade-in + // only allow motolite, other, none + switch ($trade_in) + { + case TradeInType::MOTOLITE: + case TradeInType::OTHER: + break; + + default: + $trade_in = ''; + break; + } + + $icrit->addEntry($batt, $trade_in, 1); + + return null; + } + + protected function setHubCriteria(EntityManagerInterface $em, HubFilteringGeoChecker $hub_geofence, + HubCriteria $hub_criteria, JobOrder $jo) + { + $hub_criteria->setPoint($jo->getCoordinates()); + $long = $jo->getCoordinates()->getLongitude(); + $lat = $jo->getCoordinates()->getLatitude(); + + if ($hub_geofence->isCovered($long, $lat)) + { + // TODO: set this properly, since the other flags + // are on default values. + // if true, set other values for HubCriteria + // error_log('Area is covered by hub filtering'); + $hub_criteria->setJoType($jo->getServiceType()) + ->setPaymentMethod($jo->getModeOfPayment()) + ->setRoundRobin(true); + } + + $batt_id = $req->request->get('batt_id'); + if ($batt_id != null) + { + $batt = $em->getRepository(Battery::class)->find($batt_id); + if ($batt != null) + { + // add battery to items + $sku = $batt->getSAPCode(); + if (!empty($sku)) + $hub_criteria->addItem($batt->getSAPCode(), 1); + } + } + } + + protected function assignRider($nearest_hubs, JobOrder $jo, HubFilterLogger $hub_filter_logger) + { + // try to assin rider + // go through the hub list, find the nearest hub + // with an available rider + //error_log('found nearest hub ' . $nearest_hub->getID()); + foreach ($nearest_hubs as $nearest_hub) + { + $available_riders = $nearest_hub['hub']->getAvailableRiders(); + if (count($available_riders) >= 1) + { + $assigned_rider = null; + if (count($available_riders) == 1) + { + $assigned_rider = $available_riders[0]; + } + else + { + // TODO: the setting of riders into an array + // will no longer be necessary when the contents + // of randomizeRider changes + $riders = []; + foreach ($available_riders as $rider) + { + $riders[] = $rider; + } + + $assigned_rider = $this->randomizeRider($riders); + } + + $jo->setHub($nearest_hub['hub']); + $jo->setRider($assigned_rider); + $jo->setStatus(JOStatus::ASSIGNED); + $jo->setStatusAutoAssign(AutoAssignStatus::HUB_AND_RIDER_ASSIGNED); + + $assigned_rider->setAvailable(false); + + // update redis hub_jo_count for hub + $hub_dist->incrementJoCountForHub($nearest_hub['hub']); + + // break out of loop + break; } else - return $params; + { + // log hub into hub_filter_log + $hub_filter_logger->logFilteredHub($nearest_hub['hub'], 'no_available_rider'); + // continue to go through list to find hub with an available rider + } } - - return $missing; } - // TODO: type hint entity manager - // TODO: since we broke the functions into separate files, we need - // to figure out how to make this accessible to all ResqAPI controllers - protected function checkAPIKey($em, $api_key) + protected function processEvents(EntityManagerInterface $em, JobOrder $jo, + RiderAssignmentHandlerInterface $rah, MQTTClient $mclient) { - // find the api key (session id) - // TODO: user validation needs to be changed - $m_user = $em->getRepository(MobileUser::class)->find($api_key); - if ($m_user == null) - return null; + // add event log for JO + $event = new JOEvent(); + $event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::CREATE) + ->setJobOrder($jo); + $em->persist($event); - return $m_user; + // check JO status + if ($jo->getStatus() == JOStatus::ASSIGNED) + { + // add event logs for hub and rider assignments + $hub_assign_event = new JOEvent(); + $hub_assign_event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::HUB_ASSIGN) + ->setJobOrder($jo); + + $em->persist($hub_assign_event); + + $rider_assign_event = new JOEvent(); + $rider_assign_event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::RIDER_ASSIGN) + ->setJobOrder($jo); + + $em->persist($rider_assign_event); + + // user mqtt event + $payload = [ + 'event' => 'outlet_assign' + ]; + $mclient->sendEvent($jo, $payload); + + $rah->assignJobOrder($jo, $jo->getRider()); + } + } + + protected function removeCustomerTag(JobOrder $jo, CustomerVehicle $cv, PromoLogger $promo_logger, $mobile_user_id) + { + // check service type + if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) + { + $customer = $cv->getCustomer(); + $customer_tags = $customer->getCustomerTagObjects(); + if (!empty($customer_tags)) + { + foreach ($customer_tags as $customer_tag) + { + if ($customer_tag->getID() == $invoice->getUsedCustomerTagId()) + { + // remove associated entity + $customer->removeCustomerTag($customer_tag); + + // log the availment of promo from customer + $created_by = $mobile_user_id; + $cust_id = $jo->getCustomer()->getID(); + $cust_fname = $jo->getCustomer()->getFirstName(); + $cust_lname = $jo->getCustomer()->getLastName(); + $jo_id = $jo->getID(); + $invoice_id = $jo->getInvoice()->getID(); + // TODO: check if we store total price of invoice or just the discounted amount + $amount = $jo->getInvoice()->getTotalPrice(); + $promo_logger->logPromoInfo($created_by, $cust_id, $cust_fname, $cust_lname, $jo_id, + $invoice_id, $amount); + } + } + } + } } }