From 08050416bbc048de989f50ae0fe2fd415e68a014 Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Mon, 20 Jun 2022 06:50:06 +0000 Subject: [PATCH] Add route for job order creation using third party API. #686 --- config/routes/tapi.yaml | 6 + src/Controller/APIUserController.php | 3 +- src/Controller/TAPI/JobOrderController.php | 409 +++++++++++++-------- src/Ramcar/TransactionOrigin.php | 2 + 4 files changed, 269 insertions(+), 151 deletions(-) create mode 100644 config/routes/tapi.yaml diff --git a/config/routes/tapi.yaml b/config/routes/tapi.yaml new file mode 100644 index 00000000..fad92ed7 --- /dev/null +++ b/config/routes/tapi.yaml @@ -0,0 +1,6 @@ +# third party api + +tapi_jo_request: + path: /tapi/job_order + controller: App\Controller\TAPI\JobOrderController::requestJobOrder + methods: [POST] diff --git a/src/Controller/APIUserController.php b/src/Controller/APIUserController.php index 06f128e9..302e97dd 100644 --- a/src/Controller/APIUserController.php +++ b/src/Controller/APIUserController.php @@ -160,7 +160,8 @@ class APIUserController extends Controller $meta = ['rider_id' => $rider_id]; // set api user in rider - $rider->setAPIUser($obj); + if ($rider != null) + $rider->setAPIUser($obj); // set and save values $obj->setName($req->request->get('name')) diff --git a/src/Controller/TAPI/JobOrderController.php b/src/Controller/TAPI/JobOrderController.php index 55f65d46..a34f5a28 100644 --- a/src/Controller/TAPI/JobOrderController.php +++ b/src/Controller/TAPI/JobOrderController.php @@ -48,6 +48,7 @@ use App\Entity\JOEvent; use App\Entity\Customer; use App\Entity\Hub; use App\Entity\Invoice; +use App\Entity\Vehicle; use DateTime; use DateInterval; @@ -70,6 +71,7 @@ class JobOrderController extends APIController HubSelector $hub_select, HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger, HubFilteringGeoChecker $hub_geofence, EntityManagerInterface $em) { + // TODO: fix all the responses $this->denyAccessUnlessGranted('tapi_jo.request', null, 'No access.'); // check required parameters and api key @@ -82,21 +84,19 @@ class JobOrderController extends APIController 'first_name', 'last_name', 'mobile_number', - 'vehicle_manufacturer', - 'vehicle_model', + 'vehicle_manufacturer_id', + 'vehicle_model_id', 'plate_number' ]; - $res = $this->checkParamsAndKey($req, $em, $required_params); - if ($res->isError()) - return $res->getReturnResponse(); + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); // get data from request - $data = []; - $data = $this->getJobOrderRequestInfo($req, $data); - - // process customer and vehicle information - $this->processCustomerAndVehicleInformation($data, $em); + $status = $this->getJobOrderRequestInfo($req, $data); + if ($status != null) + return new APIResponse(false, $status); $is_covered = false; // geofence @@ -105,72 +105,43 @@ class JobOrderController extends APIController 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(); + $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 new APIResponse(false, $msg); } $jo = new JobOrder(); - $jo->setSource(TransactionOrigin::MOBILE_APP) + $jo->setSource($data['source']) ->setStatus(JOStatus::PENDING) - ->setDeliveryInstructions('') + ->setDeliveryInstructions($data['instructions']) ->setTier1Notes('') ->setTier2Notes('') - ->setDeliveryAddress($address) - ->setTradeInType($trade_in) - ->setDeliveryInstructions($instructions) - // TODO: error check for valid mode of payment + ->setDeliveryAddress($data['address']) + ->setTradeInType($data['trade_in_type']) + ->setDeliveryInstructions($data['instructions']) ->setModeOfPayment($data['payment_mode']) ->setAdvanceOrder($data['is_advance_order']) ->setStatusAutoAssign(AutoAssignStatus::NOT_ASSIGNED) - ->setLandmark($landmark); + ->setLandmark($data['landmark']); - $jo->setCustomer($cust); + $jo->setCustomer($data['customer']); // set coordinates - $point = new Point($long, $lat); + $point = new Point($data['long'], $data['lat']); $jo->setCoordinates($point); // make invoice criteria $icrit = new InvoiceCriteria(); - $icrit->setServiceType($stype); + $icrit->setServiceType($data['service_type']); // TODO add promo to criteria if any // check promo // put in criteria - $icrit->addPromo($promo); + $icrit->addPromo($data['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); + $icrit->setCustomerVehicle($data['customer_vehicle']); + $jo->setCustomerVehicle($data['customer_vehicle']); - // check if customer owns vehicle - if ($cust->getID() != $cv->getCustomer()->getID()) - { - $res->setError(true) - ->setErrorMessage('Customer does not own vehicle'); - return $res->getReturnResponse(); - } - - switch ($trade_in) - { - case TradeInType::MOTOLITE: - case TradeInType::OTHER: - break; - - default: - $trade_in = ''; - break; - } - - $icrit->addEntry($batt, $trade_in, 1); + $icrit->addEntry($data['batt'], $data['trade_in_type'], 1); // send to invoice generator $invoice = $ic->generateInvoice($icrit); @@ -200,7 +171,7 @@ class JobOrderController extends APIController { // error_log('hub filter is enabled'); // check if customer location is in hub filter area - if ($hub_geofence->isCovered($long, $lat)) + if ($hub_geofence->isCovered($data['long'], $data['lat'])) { // if true, set other values for HubCriteria // TODO: set this properly, since the other flags @@ -213,6 +184,7 @@ class JobOrderController extends APIController } // check if batt is null + $batt = $data['batt']; if ($batt != null) { // add battery to items @@ -222,6 +194,7 @@ class JobOrderController extends APIController } // get customer id. No JO id at this point + $cust = $data['customer']; $customer_id = $cust->getID(); $hub_criteria->setCustomerId($customer_id); @@ -326,7 +299,7 @@ class JobOrderController extends APIController $jo->setStatusAutoAssign(AutoAssignStatus::HUB_ASSIGNED); if ($date_schedule != null) - $jo->setDateSchedule($date_schedule); + $jo->setDateSchedule($data['date_schedule']); // update redis hub_jo_count for hub $hub_dist->incrementJoCountForHub($hub); @@ -414,41 +387,10 @@ class JobOrderController extends APIController 'invoice' => $invoice_data ]; - // need to check for customer tag/promo - // 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 = $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); + $message = 'Job order created.'; - return $res->getReturnResponse(); + return new APIResponse(true, $message, $data); } public function getEstimate(Request $req, InvoiceGeneratorInterface $ic, EntityManagerInterface $em) @@ -1528,14 +1470,27 @@ class JobOrderController extends APIController protected function getJobOrderRequestInfo(Request $req, EntityManagerInterface $em, &$data) { + $error = $this->validateRequest($req); + if ($error != null) + { + // there is a validation error + return $error; + } + $r = $req->request; + // at this point, the request data has been validated // trade-in type $trade_in_type = $this->cleanText($r->get('trade_in_type')); - if (!TradeInType::validate($trade_in_type)) + switch ($trade_in_type) { - $message = 'Invalid trade in type'; - return $message; + case TradeInType::MOTOLITE: + case TradeInType::OTHER: + break; + + default: + $trade_in_type = ''; + break; } // address @@ -1551,22 +1506,11 @@ class JobOrderController extends APIController $long = $r->get('longitude'); $lat = $r->get('latitude'); - // validate service type + // get service type $stype = $this->cleanText($r->get('service_type')); - if (!ServiceType::validate($stype)) - { - $message = 'Invalid service type'; - return $message; - } // get mode of payment - // TODO: do we need to validate this? $payment_mode = $this->clean($r->get('mode_of_payment')); - if (!ModeOfPayment::validate($payment_mode)) - { - $message = 'Invalid mode of payment'; - return $message; - } $advance_order = $r->get('flag_advance_order'); // check for 'false' text @@ -1596,6 +1540,97 @@ class JobOrderController extends APIController } } + // get promo + $promo_id = $r->get('promo_id'); + $promo = $em->getRepository(Promo::class)->find($promo_id); + + // check battery + $batt_id = $req->request->get('batt_id'); + $batt = $em->getRepository(Battery::class)->find($batt_id); + + // get customer and vehicle info + $fname = trim($r->get('first_name', '')); + $lname = trim($r->get('last_name', '')); + $mobile = $r->get('mobile_number', ''); + + // validate mobile number + $clean_mobile = $this->cleanPhoneNumber($mobile); + + $vmanu_id = $r->get('vehicle_manufacturer'); + // find vehicle manufacturer + $vmanu = $em->getRepository(VehicleManufacturer::class)->find($vmanu_id); + + $vmodel_id = $r->get('vehicle_model'); + // find vehicle + $vehicle = $em->getRepository(Vehicle::class)->find($vmodel_id); + + $plate_number = $r->get('plate_number'); + + // clean plate number + $clean_plate = $this->cleanPlateNumber($plate_number); + + $c_data = [ + 'first_name' => $fname, + 'last_name' => $lname, + 'mobile' => $clean_mobile, + 'vmanu' => $vmanu, + 'vehicle' => $vehicle, + 'plate_number' => $clean_plate, + ]; + + // process customer and vehicle information + $cust_data = $this->processCustomerAndVehicleInformation($c_data, $em); + + $data = [ + 'trade_in_type' => $trade_in_type, + 'service_type' => $stype, + 'long' => $long, + 'lat' => $lat, + 'payment_mode' => $payment_mode, + 'address' => $address, + 'instructions' => $instructions, + 'landmark' => $landmark, + 'is_advance_order' => $flag_advance_order, + 'hub' => $hub, + 'date_schedule' => $date_schedule, + 'promo' => $promo, + 'batt' => $batt, + 'customer' => $cust_data['customer'], + 'customer_vehicle' => $cust_data['customer_vehicle'], + 'source' => TransactionOrigin::THIRD_PARTY, + ]; + + return null; + } + + protected function validateRequest(Request $req, EntityManagerInterface $em) + { + $r = $req->request; + + // validate trade-in type + $trade_in_type = $this->cleanText($r->get('trade_in_type')); + if (!TradeInType::validate($trade_in_type)) + { + $message = 'Invalid trade in type'; + return $message; + } + + // validate service type + $stype = $this->cleanText($r->get('service_type')); + if (!ServiceType::validate($stype)) + { + $message = 'Invalid service type'; + return $message; + } + + // validate mode of payment + $payment_mode = $this->clean($r->get('mode_of_payment')); + if (!ModeOfPayment::validate($payment_mode)) + { + $message = 'Invalid mode of payment'; + return $message; + } + // check promo $promo = null; $promo_id = $r->get('promo_id'); @@ -1603,7 +1638,7 @@ class JobOrderController extends APIController { $promo = $em->getRepository(Promo::class)->find($promo_id); if ($promo == null) - { + { $message = 'Invalid promo id'; return $message; } @@ -1612,7 +1647,7 @@ class JobOrderController extends APIController // check battery $batt = null; $batt_id = $req->request->get('batt_id'); - if ($batt_id != null) + if ($empty($batt_id)) { $batt = $em->getRepository(Battery::class)->find($batt_id); if ($batt == null) @@ -1622,12 +1657,8 @@ class JobOrderController extends APIController } } - // get customer and vehicle info - $fname = $r->get('first_name', ''); - $lname = $r->get('last_name', ''); - $mobile = $r->get('mobile_number', ''); - // validate mobile number + $mobile = $r->get('mobile_number', ''); $clean_mobile = $this->cleanPhoneNumber($mobile); if ($clean_mobile == false) { @@ -1635,66 +1666,144 @@ class JobOrderController extends APIController return $message; } - $vmanu = $r->get('vehicle_manufacturer', ''); - $vmodel = $r->get('vehicle_model', ''); - $plate_number = $r->get('plate_number'); + $vmanu = null; + $vmanu_id = $r->get('vehicle_manufacturer'); + // validate the vehicle manufacturer id + // find vehicle manufacturer + $vmanu = $em->getRepository(VehicleManufacturer::class)->find($vmanu_id); + if ($vmanu == null) + { + $message = 'Invalid vehicle manufacturer id.'; + return $message; + } - // clean plate number - $clean_plate = $this->cleanPlateNumber($plate_number); + $vmodel = null; + $vmodel_id = $r->get('vehicle_model'); + // validate the vehicle model id + // find vehicle + $vmodel = $em->getRepository(Vehicle::class)->find($vmodel_id); + if ($vmodel = null) + { + $message = 'Invalid vehicle model id.'; + return $message; + } - $data = [ - 'trade_in_type' => $trade_in_type, - 'service_type' => $stype, - 'long' => $long, - 'lat' => $lat, - 'payment_mode' => $payment_mode, - 'first_name' => $fname, - 'last_name' => $lname, - 'mobile' => $clean_mobile, - 'vmanu' => $vmanu, - 'vmodel' => $model, - 'plate_number' => $clean_plate, - 'address' => $address, - 'instructions' => $instructions, - 'landmark' => $landmark, - 'is_advance_order' => $flag_advance_order, - 'hub' => $hub, - 'date_schedule' => $date_schedule, - 'promo' => $promo, - 'batt' => $batt, - ]; + // confirm that vehicle model's manufacturer is the same as the one in vehicle + if ($vmodel->getManufacturer()->getID != $vmanu_id) + { + $message = 'Invalid vehicle manufacturer id for vehicle model.'; + return $message; + } return null; } protected function processCustomerAndVehicleInformation($data, EntityManagerInterface $em) { + $c_data = []; + // retrieve customer info $fname = $data['first_name']; $lname = $data['last_name']; $vmanu = $data['vmanu']; - $vmodel = $data['vmodel']; + $vehicle = $data['vehicle']; $plate_number = $data['plate_number']; $mobile = $data['mobile']; - - // find customer given phone number - $cust = $em->getRepository(Customer::class)->findOneBy(['phone_mobile' => $mobile]); + + $cust = null; + $cust_vehicle = null; - if ($cust == null) + // find customer + customer vehicle combo + $cv = $this->findCustomerAndCustomerVehicle($data, $em); + if ($cv == null) { - // create new customer and customer vehicle - } - else - { - // find customer vehicle using plate number - $cv = $em->getRepository(CustomerVehicle::class)->findOneBy(['plate_number' => $plate_number]); - if ($cv == null) + // find customer given phone number + $cust = $em->getRepository(Customer::class)->findOneBy(['phone_mobile' => $mobile]); + + if ($cust == null) + { + // get the api_user that made the call so that it gets added to the source + // source becomes TAPI_USER_ + $user_id = $_SERVER['HTTP_X_CATA_API_KEY']; + $source = 'TAPI_USER'; + $username = ''; + + if (!empty($user_id)) + { + $api_user = $this->getUser(); + if ($api_user != null) + { + $username = $api_user->getName(); + $source = $source . '_' . $username; + } + } + + // create new customer and customer vehicle + $cust = new Customer(); + + $cust->setFirstName($fname) + ->setLastName($lname) + ->setPhoneMobile($mobile) + ->setCreateSource($source); + + $em->persist($cust); + + // add customer vehicle + $cust_vehicle = $this->createCustomerVehicle($em, $cust, $vehicle); + + } + else { // create customer vehicle + $cust_vehicle = $this->createCustomerVehicle($em, $cust, $vehicle); } + + $em->flush(); } - return $cust; + $c_data = [ + 'customer' => $cust, + 'customer_vehicle' => $cust_vehicle, + ]; + + return $c_data; + } + + protected function createCustomerVehicle(EntityManagerInterface $em, Customer $cust, Vehicle $vehicle) + { + // add customer vehicle + $cust_vehicle = new CustomerVehicle(); + + $cust_vehicle->setCustomer($cust) + ->setPlateNumber($plate_number) + ->setVehicle($vehicle); + + $em->persist($cust_vehicle); + + return $cust_vehicle; + } + + protected function findCustomerAndCustomerVehicle($data, EntityManagerInterface $em) + { + $plate_number = $data['plate_number']; + $mobile = $data['mobile']; + + $query = $em->createQuery('SELECT cv, c FROM App\Entity\CustomerVehicle cv + JOIN cv.customer c + WHERE cv.plate_number = :plate_number + AND c.phone_mobile = :phone_mobile'); + $query->setParameter('plate_number', $plate_number) + ->setParameter('phone_mobile', $mobile); + + $cust_results = $query->iterate(); + + $cust_vehicle = null; + foreach ($cust_results as $row) + { + $cust_vehicle = $row[0]; + } + + return $cust_vehicle; } protected function cleanPhoneNumber($mobile) diff --git a/src/Ramcar/TransactionOrigin.php b/src/Ramcar/TransactionOrigin.php index 8d4c12aa..80f0d029 100644 --- a/src/Ramcar/TransactionOrigin.php +++ b/src/Ramcar/TransactionOrigin.php @@ -11,6 +11,7 @@ class TransactionOrigin extends NameValue const MOBILE_APP = 'mobile_app'; const WALK_IN = 'walk_in'; const LAZADA = 'lazada'; + const THIRD_PARTY = 'third_party'; // TODO: for now, resq also gets the walk-in option const COLLECTION = [ @@ -21,5 +22,6 @@ class TransactionOrigin extends NameValue 'mobile_app' => 'Mobile App', 'walk_in' => 'Walk-in', 'lazada' => 'Lazada', + 'third_party' => 'Third Party', ]; }