session = null; } protected function debugRequest(Request $req) { $all = $req->request->all(); error_log(print_r($all, true)); } protected function checkMissingParameters(Request $req, $params = []) { $missing = []; // check if parameters are there foreach ($params as $param) { if ($req->getMethod() == 'GET') { $check = $req->query->get($param); if (empty($check)) $missing[] = $param; } else if ($req->getMethod() == 'POST') { $check = $req->request->get($param); if (empty($check)) $missing[] = $param; } else return $params; } return $missing; } // TODO: type hint entity manager protected function checkAPIKey($em, $api_key) { // find the api key (session id) $session = $em->getRepository(MobileSession::class)->find($api_key); if ($session == null) return null; return $session; } protected function checkParamsAndKey(Request $req, $em, $params) { // returns APIResult object $res = new APIResult(); // check for api_key in query string $api_key = $req->query->get('api_key'); if (empty($api_key)) { $res->setError(true) ->setErrorMessage('Missing API key'); return $res; } // check missing parameters $missing = $this->checkMissingParameters($req, $params); if (count($missing) > 0) { $miss_string = implode(', ', $missing); $res->setError(true) ->setErrorMessage('Missing parameter(s): ' . $miss_string); return $res; } // check api key $sess = $this->checkAPIKey($em, $req->query->get('api_key')); if ($sess == null) { $res->setError(true) ->setErrorMessage('Invalid API Key'); return $res; } // store session $this->session = $sess; return $res; } public function register(Request $req) { $res = new APIResult(); // confirm parameters $required_params = [ 'phone_model', 'os_type', 'os_version', 'phone_id' ]; $missing = $this->checkMissingParameters($req, $required_params); if (count($missing) > 0) { $params = implode(', ', $missing); $res->setError(true) ->setErrorMessage('Missing parameter(s): ' . $params); return $res->getReturnResponse(); } $em = $this->getDoctrine()->getManager(); // retry until we get a unique id while (true) { try { // instantiate session $sess = new MobileSession(); $sess->setPhoneModel($req->request->get('phone_model')) ->setOSType($req->request->get('os_type')) ->setOSVersion($req->request->get('os_version')) ->setPhoneID($req->request->get('phone_id')); // reopen in case we get an exception if (!$em->isOpen()) { $em = $em->create( $em->getConnection(), $em->getConfiguration() ); } // save $em->persist($sess); $em->flush(); } catch (DBALException $e) { error_log($e->getMessage()); // delay one second and try again sleep(1); continue; } break; } // return data $data = [ 'session_id' => $sess->getID() ]; $res->setData($data); // response return $res->getReturnResponse(); } protected function generateConfirmCode() { return sprintf("%06d", mt_rand(100000, 999999)); } protected function sendConfirmationCode(RisingTideGateway $rt, $phone_number, $code) { // send sms to number $message = "Your Resq confirmation code is $code."; $rt->sendSMS($phone_number, 'MOTOLITE', $message); } public function confirmNumber(RisingTideGateway $rt, Request $req) { // check parameters $required_params = [ 'phone_number', ]; // check required parameters and api key $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // phone number $phone_number = $req->request->get('phone_number'); // get otp_mode from .env $dotenv = new Dotenv(); $dotenv->loadEnv(__DIR__.'/../../.env'); $otp_mode = $_ENV['OTP_MODE']; // check for hardcoded phone number for app store testing if ($phone_number == '639991112233') { $code = '123456'; $this->session->setConfirmCode($code) ->setPhoneNumber($phone_number); $em->flush(); return $res->getReturnResponse(); } // check if otp_mode is test if ($otp_mode == 'test') { $code = '123456'; $this->session->setConfirmCode($code) ->setPhoneNumber($phone_number); $em->flush(); return $res->getReturnResponse(); } // TODO: spam protection // TODO: validate phone number // generate code and save $code = $this->generateConfirmCode(); $this->session->setConfirmCode($code) ->setPhoneNumber($phone_number); $em->flush(); if ($otp_mode != 'test') { // send sms to number $this->sendConfirmationCode($rt, $phone_number, $code); } // response return $res->getReturnResponse(); } // TODO: find session customer by phone number protected function findNumberSession($number) { $em = $this->getDoctrine()->getManager(); $query = $em->getRepository(MobileSession::class)->createQueryBuilder('s') ->where('s.phone_number = :number') ->andWhere('s.customer is not null') ->andWhere('s.confirm_flag = 1') ->setParameter('number', $number) ->setMaxResults(1) ->getQuery(); // we just need one $res = $query->getOneOrNullResult(); return $res; } public function validateCode(Request $req) { // check parameters $required_params = [ 'code', ]; // check required parameters and api key $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // code is wrong $code = $req->request->get('code'); if ($this->session->getConfirmCode() != $code) { $res->setError(true) ->setErrorMessage('Wrong confirm code'); return $res->getReturnResponse(); } // set confirm date $date = new DateTime(); $this->session->setDateConfirmed($date) ->setConfirmed(); // TODO: check if we have the number registered before and merge $dupe_sess = $this->findNumberSession($this->session->getPhoneNumber()); if ($dupe_sess != null) { $dupe_cust = $dupe_sess->getCustomer(); $this->session->setCustomer($dupe_cust); } // TODO: check if mobile matches mobile of customer $customer = $this->findCustomerByNumber($this->session->getPhoneNumber()); if ($customer != null) { // TODO: if there is a dupe_sess, do we need to check if // dupe_cust is the same as the customer we found? $this->session->setCustomer($customer); } $em->flush(); // response return $res->getReturnResponse(); } public function getInfo(Request $req) { // check required parameters and api key $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // if no customer found $cust = $this->session->getCustomer(); if ($cust == null) { $data = [ 'first_name' => '', 'last_name' => '', 'priv_third_party' => (bool) false, 'priv_promo' => (bool) false, ]; $res->setData($data); return $res->getReturnResponse(); } // send back customer details $data = [ 'first_name' => $cust->getFirstName(), 'last_name' => $cust->getLastName(), 'priv_third_party' => (bool) $cust->getPrivacyThirdParty(), 'priv_promo' => (bool) $cust->getPrivacyPromo(), ]; $res->setData($data); return $res->getReturnResponse(); } public function updateInfo(Request $req) { // check required parameters and api key $required_params = [ 'first_name', 'last_name', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // create new customer if it's not there $cust = $this->session->getCustomer(); if ($cust == null) { $cust = new Customer(); $em->persist($cust); $this->session->setCustomer($cust); } $cust->setFirstName($req->request->get('first_name')) ->setLastName($req->request->get('last_name')) ->setConfirmed($this->session->isConfirmed()); // update mobile phone of customer $cust->setPhoneMobile(substr($this->session->getPhoneNumber(), 2)); // get privacy policy for mobile $dotenv = new Dotenv(); $dotenv->loadEnv(__DIR__.'/../../.env'); $policy_mobile_id = $_ENV['POLICY_MOBILE']; $mobile_policy = $em->getRepository(PrivacyPolicy::class)->find($policy_mobile_id); // set policy id if ($mobile_policy != null) { $cust->setPrivacyPolicyMobile($mobile_policy); } $em->flush(); return $res->getReturnResponse(); } public function getStatus(Request $req) { // check required parameters and api key $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // set data $data = []; if ($this->session->isConfirmed()) $data['status'] = 'confirmed'; else $data['status'] = 'unconfirmed'; $res->setData($data); return $res->getReturnResponse(); } public function listVehicleManufacturers(Request $req) { // check required parameters and api key $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get manufacturer list $mfgs = $em->getRepository(VehicleManufacturer::class)->findBy(['flag_mobile' => true], ['name' => 'asc']); $mfg_list = []; foreach ($mfgs as $mfg) { $mfg_list[] = [ 'id' => $mfg->getID(), 'name' => $mfg->getName(), ]; } $data = [ 'manufacturers' => $mfg_list ]; $res->setData($data); return $res->getReturnResponse(); } public function listVehicleMakes(Request $req, $mfg_id) { // check required parameters and api key $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get manufacturer $mfg = $em->getRepository(VehicleManufacturer::class)->find($mfg_id); if ($mfg == null) { $res->setError(true) ->setErrorMessage('Invalid vehicle manufacturer id'); return $res->getReturnResponse(); } // get makes $vehicles = $em->getRepository(Vehicle::class)->findBy( [ 'flag_mobile' => true, 'manufacturer' => $mfg_id, ], ['make' => 'asc'] ); // $vehicles = $mfg->getVehicles(); $vlist = []; foreach ($vehicles as $v) { $vlist[] = [ 'id' => $v->getID(), 'make' => trim($v->getMake() . ' ' . $v->getModelYearFormatted(false)), // 'make' => $v->getMake() . ' ' . $v->getModelYearFrom() . '-' . $v->getModelYearTo(), ]; } $data = [ 'manufacturer' => [ 'id' => $mfg->getID(), 'name' => $mfg->getName(), ], 'makes' => $vlist, ]; $res->setData($data); return $res->getReturnResponse(); } protected function checkVehicleRequirements(Request $req) { // check required parameters and api key $required_params = [ 'make_id', 'name', 'plate_num', 'model_year', 'color', 'condition', 'fuel_type', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res; // TODO: check valid plate number // TODO: check valid fuel type (gas / diesel) // TODO: check current battery id // TODO: check condition (brand new / second-hand) // TODO: check is_motolite and is_active (1 or 0) // TODO: check warranty expiration date (YYYYMMDD) // TODO: check model year coverage if it fits in between return $res; } protected function setCustomerVehicleObject(Request $req, APIResult $res, CustomerVehicle $cv) { $em = $this->getDoctrine()->getManager(); // check customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res; } // get vehicle $vehicle = $em->getRepository(Vehicle::class)->find($req->request->get('make_id')); if ($vehicle == null) { $res->setError(true) ->setErrorMessage('Invalid vehicle make id'); return $res; } $cv->setCustomer($cust) ->setVehicle($vehicle) ->setName($req->request->get('name')) ->setPlateNumber($req->request->get('plate_num')) ->setModelYear($req->request->get('model_year')) ->setColor($req->request->get('color')) ->setFuelType($req->request->get('fuel_type')) ->setStatusCondition($req->request->get('condition')); // set warranty code and expiration // TODO: check warranty requirements if (!empty($req->request->get('wty_code'))) $cv->setWarrantyCode($req->request->get('wty_code')); if (!empty($req->request->get('wty_expire'))) $cv->setWarrantyExpiration(new DateTime($req->request->get('wty_expire'))); // TODO: get current battery // is motolite if ($req->request->get('is_motolite') == 0) $cv->setHasMotoliteBattery(false); else $cv->setHasMotoliteBattery(true); // is active if ($req->request->get('is_active') == 0) $cv->setActive(false); else $cv->setActive(true); // save $em->persist($cv); $em->flush(); // data $data = [ 'cv_id' => $cv->getID() ]; $res->setData($data); return $res; } public function addVehicle(Request $req) { // check requirements $res = $this->checkVehicleRequirements($req); if ($res->isError()) return $res->getReturnResponse(); // customer vehicle $cv = new CustomerVehicle(); $res = $this->setCustomerVehicleObject($req, $res, $cv); return $res->getReturnResponse(); } public function updateVehicle(Request $req, $id) { // check requirements $res = $this->checkVehicleRequirements($req); if ($res->isError()) return $res->getReturnResponse(); // get customer vehicle $em = $this->getDoctrine()->getManager(); $cv = $em->getRepository(CustomerVehicle::class)->find($id); // check if it exists if ($cv == null) { $res->setError(true) ->setErrorMessage('Vehicle does not exist'); return $res->getReturnResponse(); } // check if it's owned by customer if ($cv->getCustomer()->getID() != $this->session->getCustomer()->getID()) { $res->setError(true) ->setErrorMessage('Invalid vehicle'); return $res->getReturnResponse(); } $res = $this->setCustomerVehicleObject($req, $res, $cv); return $res->getReturnResponse(); } protected function getBatteryImageURL($req, $batt) { // TODO: workaround for now, we get static image of battery based on model name $filename = trim(strtolower($batt->getModel()->getName())) . '_mobile.jpg'; $file_path = $req->getSchemeAndHttpHost() . $this->generateUrl('static_battery_image') . '/' . $filename; return $file_path; } public function listVehicles(Request $req) { // check required parameters and api key $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } // vehicles $cv_list = []; $cvs = $cust->getVehicles(); foreach ($cvs as $cv) { $battery_id = null; if ($cv->getCurrentBattery() != null) $battery_id = $cv->getCurrentBattery()->getID(); $wty_ex = null; if ($cv->getWarrantyExpiration() != null) $wty_ex = $cv->getWarrantyExpiration()->format('Y-m-d'); $warranty = $this->findWarranty($cv->getPlateNumber()); $cv_list[] = [ 'cv_id' => $cv->getID(), 'mfg_id' => $cv->getVehicle()->getManufacturer()->getID(), 'make_id' => $cv->getVehicle()->getID(), 'name' => $cv->getName(), 'plate_num' => $cv->getPlateNumber(), 'model_year' => $cv->getModelYear(), 'color' => $cv->getColor(), 'condition' => $cv->getStatusCondition(), 'fuel_type' => $cv->getFuelType(), 'wty_code' => $cv->getWarrantyCode(), 'wty_expire' => $wty_ex, 'curr_batt_id' => $battery_id, 'is_motolite' => $cv->hasMotoliteBattery() ? 1 : 0, 'is_active' => $cv->isActive() ? 1 : 0, 'warranty' => $warranty, ]; } // data $data = [ 'vehicles' => $cv_list ]; $res->setData($data); return $res->getReturnResponse(); } public function listPromos(Request $req) { // check required parameters and api key $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); return $res->getReturnResponse(); } public function getCompatibleBatteries(Request $req, $vid) { // check required parameters and api key $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get vehicle $vehicle = $em->getRepository(Vehicle::class)->find($vid); if ($vehicle == null) { $res->setError(true) ->setErrorMessage('Invalid vehicle'); return $res->getReturnResponse(); } // batteries $batt_list = []; $batts = $vehicle->getBatteries(); foreach ($batts as $batt) { // TODO: Add warranty_tnv to battery information $batt_list[] = [ 'id' => $batt->getID(), 'mfg_id' => $batt->getManufacturer()->getID(), 'mfg_name' => $batt->getManufacturer()->getName(), 'model_id' => $batt->getModel()->getID(), 'model_name' => $batt->getModel()->getName(), 'size_id' => $batt->getSize()->getID(), 'size_name' => $batt->getSize()->getName(), 'price' => $batt->getSellingPrice(), 'wty_private' => $batt->getWarrantyPrivate(), 'wty_commercial' => $batt->getWarrantyCommercial(), 'image_url' => $this->getBatteryImageURL($req, $batt), ]; } // data $data = [ 'vehicle' => [ 'id' => $vehicle->getID(), 'mfg_id' => $vehicle->getManufacturer()->getID(), 'mfg_name' => $vehicle->getManufacturer()->getName(), 'make' => $vehicle->getMake(), 'model_year_from' => $vehicle->getModelYearFrom(), 'model_year_to' => $vehicle->getModelYearTo(), ], 'batteries' => $batt_list, ]; $res->setData($data); return $res->getReturnResponse(); } public function requestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo, MapTools $map_tools, InventoryManager $im, MQTTClient $mclient, RiderAssignmentHandlerInterface $rah) { // check required parameters and api key $required_params = [ 'service_type', 'cv_id', // 'batt_id', 'trade_in', 'long', 'lat', 'warranty', 'mode_of_payment', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // trade in type $trade_in = $req->request->get('trade_in'); // address $address = $req->request->get('delivery_address', 'Set by mobile application'); // instructions $instructions = $req->request->get('delivery_instructions', ''); // longitude and latitude $long = $req->request->get('long'); $lat = $req->request->get('lat'); // 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(); } $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')); // customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } $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); // send to invoice generator $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); // assign hub and rider if (($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) || ($jo->getServicetype() == ServiceType::BATTERY_REPLACEMENT_WARRANTY)) { // get nearest hub // $nearest_hub = $this->findNearestHubWithInventory($jo, $batt, $em, $map_tools, $im); $nearest_hub = $this->findNearestHub($jo, $em, $map_tools); } else { $nearest_hub = $this->findNearestHub($jo, $em, $map_tools); } if (!empty($nearest_hub)) { //error_log('found nearest hub ' . $nearest_hub->getID()); // assign rider $available_riders = $nearest_hub->getAvailableRiders(); if (count($available_riders) > 0) { $assigned_rider = null; if (count($available_riders) > 1) { // 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); } else $assigned_rider = $available_riders[0]; //error_log('found rider ' . $assigned_rider->getID()); $jo->setHub($nearest_hub); $jo->setRider($assigned_rider); $jo->setStatus(JOStatus::ASSIGNED); $assigned_rider->setAvailable(false); } } $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()); } $em->flush(); // make invoice json data $invoice_data = [ 'total_price' => $invoice->getTotalPrice(), 'vat_ex_price' => $invoice->getVATExclusivePrice(), 'vat' => $invoice->getVAT(), 'discount' => $invoice->getDiscount(), 'trade_in' => $invoice->getTradeIn(), ]; $items = $invoice->getItems(); $items_data = []; foreach ($items as $item) { $items_data[] = [ 'title' => $item->getTitle(), 'qty' => $item->getQuantity() + 0, 'price' => $item->getPrice() + 0.0, ]; } $invoice_data['items'] = $items_data; // make job order data $data = [ 'jo_id' => $jo->getID(), 'invoice' => $invoice_data ]; // set data $res->setData($data); return $res->getReturnResponse(); } public function getEstimate(Request $req, InvoiceGeneratorInterface $ic) { // $this->debugRequest($req); // check required parameters and api key $required_params = [ 'service_type', 'cv_id', // 'batt_id', 'trade_in', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } // make invoice criteria $icrit = new InvoiceCriteria(); $icrit->setServiceType($req->request->get('service_type')); // 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); // 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 $trade_in = $req->request->get('trade_in'); switch ($trade_in) { case TradeInType::MOTOLITE: case TradeInType::OTHER: break; default: $trade_in = ''; break; } $icrit->addEntry($batt, $trade_in, 1); // send to invoice generator $invoice = $ic->generateInvoice($icrit); // make invoice json data $data = [ 'total_price' => (float) $invoice->getTotalPrice(), 'vat_ex_price' => (float) $invoice->getVATExclusivePrice(), 'vat' => (float) $invoice->getVAT(), 'discount' => (float) $invoice->getDiscount(), 'trade_in' => (float) $invoice->getTradeIn(), ]; $items = $invoice->getItems(); $items_data = []; foreach ($items as $item) { $my_data = [ 'title' => $item->getTitle(), 'qty' => (int) $item->getQuantity() + 0, 'price' => (float) $item->getPrice() + 0.0, ]; $item_batt = $item->getBattery(); if ($item_batt != null) { $my_data['image_url'] = $this->getBatteryImageURL($req, $item_batt); } $items_data[] = $my_data; } $data['items'] = $items_data; // error_log(print_r($data, true)); // set data $res->setData($data); return $res->getReturnResponse(); } public function getRiderStatus(Request $req, RiderTracker $rt) { $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } // check if we have an ongoing job order /* $ongoing_jos = $em->getRepository(JobOrder::class)->findBy([ 'customer' => $cust, 'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS], ]); */ $ongoing_jos = $this->getOngoingJobOrders($cust); // $res->setData(['here' => count($ongoing_jos)]); // return $res->getReturnResponse(); if (count($ongoing_jos) <= 0) { try { // check if the latest fulfilled jo they have needs rider rating $query = $em->createQuery('select jo from App\Entity\JobOrder jo where jo.customer = :cust and jo.status = :status order by jo.date_fulfill desc'); $fulfill_jo = $query->setParameters([ 'cust' => $cust, 'status' => JOStatus::FULFILLED, ]) ->setMaxResults(1) ->getSingleResult(); } catch (Exception $e) { // no pending $res->setData([ 'status' => APIRiderStatus::NO_PENDING_JO ]); return $res->getReturnResponse(); } // we got a recently fulfilled job order if ($fulfill_jo) { // check if the rider has been rated if (!$fulfill_jo->hasRiderRating()) { $dest = $fulfill_jo->getCoordinates(); $data = [ 'jo_id' => $fulfill_jo->getID(), 'service_type' => $fulfill_jo->getServiceType(), 'destination' => [ 'long' => $dest->getLongitude(), 'lat' => $dest->getLatitude(), ], 'delivery_address' => $fulfill_jo->getDeliveryAddress(), 'delivery_instructions' => $fulfill_jo->getDeliveryInstructions(), ]; $rider = $fulfill_jo->getRider(); // default image url $url_prefix = $req->getSchemeAndHttpHost(); $image_url = $url_prefix . '/assets/images/user.gif'; if ($rider->getImageFile() != null) $image_url = $url_prefix . '/uploads/' . $rider->getImageFile(); $data['status'] = APIRiderStatus::RIDER_RATING; // default rider location to hub $data['rider'] = [ 'id' => $rider->getID(), 'name' => $rider->getFullName(), 'plate_num' => $rider->getPlateNumber(), 'contact_num' => $rider->getContactNumber(), 'image_url' => $image_url, ]; $res->setData($data); return $res->getReturnResponse(); } } // no pending $res->setData([ 'status' => APIRiderStatus::NO_PENDING_JO ]); return $res->getReturnResponse(); } // get first jo that's pending $jo = $ongoing_jos[0]; $dest = $jo->getCoordinates(); $data = [ 'jo_id' => $jo->getID(), 'service_type' => $jo->getServiceType(), 'destination' => [ 'long' => $dest->getLongitude(), 'lat' => $dest->getLatitude(), ], 'delivery_address' => $jo->getDeliveryAddress(), 'delivery_instructions' => $jo->getDeliveryInstructions(), ]; switch ($jo->getStatus()) { case JOStatus::PENDING: $data['status'] = APIRiderStatus::OUTLET_ASSIGN; $res->setData($data); return $res->getReturnResponse(); case JOStatus::RIDER_ASSIGN: $data['status'] = APIRiderStatus::RIDER_ASSIGN; $res->setData($data); return $res->getReturnResponse(); case JOStatus::ASSIGNED: case JOStatus::IN_TRANSIT: case JOStatus::IN_PROGRESS: $rider = $jo->getRider(); // get rider coordinates from redis $coord = $rt->getRiderLocation($rider->getID()); // default image url $url_prefix = $req->getSchemeAndHttpHost(); $image_url = $url_prefix . '/assets/images/user.gif'; if ($rider->getImageFile() != null) $image_url = $url_prefix . '/uploads/' . $rider->getImageFile(); $data['status'] = APIRiderStatus::RIDER_PICK_UP; // TODO: fix this to actual location of rider // default rider location to hub $data['rider'] = [ 'id' => $rider->getID(), 'name' => $rider->getFullName(), 'plate_num' => $rider->getPlateNumber(), 'contact_num' => $rider->getContactNumber(), 'image_url' => $image_url, 'location' => [ 'long' => $coord->getLongitude(), 'lat' => $coord->getLatitude() ] ]; $res->setData($data); return $res->getReturnResponse(); } $res->setData($data); return $res->getReturnResponse(); } protected function getOngoingJobOrders($cust) { $em = $this->getDoctrine()->getManager(); $ongoing_jos = $em->getRepository(JobOrder::class)->findBy([ 'customer' => $cust, 'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS], ]); return $ongoing_jos; } public function getOngoing(Request $req) { $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } /* // check if we have an ongoing job order $ongoing_jos = $em->getRepository(JobOrder::class)->findBy([ 'customer' => $cust, 'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS], ]); */ $ongoing_jos = $this->getOngoingJobOrders($cust); // initialize data $data = []; // no ongoing if (count($ongoing_jos) <= 0) { $data = [ 'has_ongoing' => false, ]; } else { $data = [ 'has_ongoing' => true, ]; } $res->setData($data); return $res->getReturnResponse(); } public function addRiderRating(Request $req) { $required_params = [ 'jo_id', 'rating', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } // get job order $jo_id = $req->request->get('jo_id'); $jo = $em->getRepository(JobOrder::class)->find($jo_id); if ($jo == null) { $res->setError(true) ->setErrorMessage('No job order found'); return $res->getReturnResponse(); } // get rider $rider = $jo->getRider(); if ($rider == null) { $res->setError(true) ->setErrorMessage('No rider found'); return $res->getReturnResponse(); } // check that the customer owns the job order $jo_cust = $jo->getCustomer(); if ($jo_cust->getID() != $cust->getID()) { $res->setError(true) ->setErrorMessage('Job order was not initiated by customer'); return $res->getReturnResponse(); } // TODO: check job order status, if it's complete // add rider rating $rating_num = $req->request->get('rating', -1); // if rating is -1 if ($rating_num == -1) { $jo->setHasRiderRating(); $em->flush(); $res->setData([]); return $res->getReturnResponse(); } $rating = new RiderRating(); $rating->setRider($rider) ->setCustomer($cust) ->setJobOrder($jo) ->setRating($rating_num); // rider rating comment $comment = $req->request->get('comment'); if (!empty($comment)) $rating->setComment($comment); // mark jo as rider rated already $jo->setHasRiderRating(); $em->persist($rating); $em->flush(); // TODO: set average rating in rider entity $res->setData([]); return $res->getReturnResponse(); } public function getJOInvoice(Request $req) { $required_params = [ 'jo_id', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get job order $jo_id = $req->query->get('jo_id'); $jo = $em->getRepository(JobOrder::class)->find($jo_id); if ($jo == null) { $res->setError(true) ->setErrorMessage('No job order found'); return $res->getReturnResponse(); } // get customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } // check that the customer owns the job order $jo_cust = $jo->getCustomer(); if ($jo_cust->getID() != $cust->getID()) { $res->setError(true) ->setErrorMessage('Job order was not initiated by customer'); return $res->getReturnResponse(); } $invoice = $jo->getInvoice(); // make invoice json data $data = [ 'total_price' => (float) $invoice->getTotalPrice(), 'vat_ex_price' => (float) $invoice->getVATExclusivePrice(), 'vat' => (float) $invoice->getVAT(), 'discount' => (float) $invoice->getDiscount(), 'trade_in' => (float) $invoice->getTradeIn(), ]; $items = $invoice->getItems(); $items_data = []; foreach ($items as $item) { $my_data = [ 'title' => $item->getTitle(), 'qty' => (int) $item->getQuantity() + 0, 'price' => (float) $item->getPrice() + 0.0, ]; $item_batt = $item->getBattery(); if ($item_batt != null) { $my_data['image_url'] = $this->getBatteryImageURL($req, $item_batt); } $items_data[] = $my_data; } $data['items'] = $items_data; // set data $res->setData($data); /* // invoice items $inv_items = []; foreach ($inv->getItems() as $item) { $item_batt = $item->getBattery(); if ($item_batt == null) $batt_id = null; else $batt_id = $item_batt->getID(); $inv_items[] = [ 'id' => $item->getID(), 'title' => $item->getTitle(), 'qty' => $item->getQuantity(), 'price' => $item->getPrice(), 'batt_id' => $batt_id, ]; } $data = [ 'invoice' => [ 'discount' => $inv->getDiscount(), 'trade_in' => $inv->getTradeIn(), 'total_price' => $inv->getTotalPrice(), 'vat' => $inv->getVat(), 'items' => $inv_items, ], ]; $res->setData($data); */ return $res->getReturnResponse(); } public function cancelJobOrder(Request $req, MQTTClient $mclient) { $required_params = [ 'jo_id', 'reason' ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get job order $jo_id = $req->request->get('jo_id'); $jo = $em->getRepository(JobOrder::class)->find($jo_id); if ($jo == null) { $res->setError(true) ->setErrorMessage('No job order found'); return $res->getReturnResponse(); } // get customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } // check that the customer owns the job order $jo_cust = $jo->getCustomer(); if ($jo_cust->getID() != $cust->getID()) { $res->setError(true) ->setErrorMessage('Job order was not initiated by customer'); return $res->getReturnResponse(); } // TODO: check job order status, if it's cancellable $cancel_reason = $req->request->get('reason'); $jo->cancel($cancel_reason); // add event log $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::CANCEL) ->setJobOrder($jo); $em->persist($event); $em->flush(); // send mobile app event $payload = [ 'event' => 'cancelled', 'reason' => $cancel_reason, 'jo_id' => $jo->getID(), ]; // $mclient->sendEvent($jo, $payload); $mclient->sendRiderEvent($jo, $payload); $res->setData([]); return $res->getReturnResponse(); } public function getJOHistory(Request $req) { $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, []); if ($res->isError()) return $res->getReturnResponse(); // get customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } // get job orders $all_jo_data = []; $jos = $cust->getJobOrders(); foreach ($jos as $jo) { $status = $jo->getStatus(); $jo_data = [ 'id' => $jo->getID(), 'date_create' => $jo->getDateCreate()->format('M d, Y'), 'service_type' => $jo->getServiceType(), 'status' => $status, ]; // customer vehicle and warranty $cv = $jo->getCustomerVehicle(); // get latest warranty using plate number $warranty = $this->findWarranty($cv->getPlateNumber()); $jo_data['customer_vehicle'] = [ 'id' => $cv->getID(), 'plate_number' => $cv->getPlateNumber(), 'warranty' => $warranty, ]; // rider $rider = $jo->getRider(); if ($rider != null) { $jo_data['rider'] = $rider->getFullName(); } // invoice items $items = []; $jo_items = $jo->getInvoice()->getItems(); foreach ($jo_items as $item) { $items[] = [ 'id' => $item->getID(), 'title' => $item->getTitle(), 'qty' => $item->getQuantity(), 'price' => $item->getPrice(), ]; } $jo_data['items'] = $items; // dates depending on status switch ($status) { case JOStatus::FULFILLED: if ($jo->getDateFulfill() == null) $jo_data['date_fulfilled'] = ''; else $jo_data['date_fulfilled'] = $jo->getDateFulfill()->format('M d, Y'); break; case JOStatus::CANCELLED: $date_cancel = $jo->getDateCancel(); if ($date_cancel == null) $date_cancel = new DateTime(); $jo_data['date_cancelled'] = $date_cancel->format('M d, Y'); break; } $all_jo_data[] = $jo_data; } // return data $data = [ 'job_orders' => $all_jo_data ]; $res->setData($data); // response return $res->getReturnResponse(); } public function updateDeviceID(Request $req) { $required_params = [ 'device_id', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); $device_id = $req->request->get('device_id'); $this->session->setDevicePushID($device_id); $em->flush(); // response return $res->getReturnResponse(); } public function resendCode(Request $req, RisingTideGateway $rt) { $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // already confirmed if ($this->session->isConfirmed()) { $res->setError(true) ->setErrorMessage('User is already confirmed.'); return $res->getReturnResponse(); } // have sent code before if ($this->session->getDateCodeSent() != null) { $res->setError(true) ->setErrorMessage('Can only send confirm code every 5 mins.'); return $res->getReturnResponse(); } // TODO: send via sms $phone_number = $this->session->getPhoneNumber(); $code = $this->session->getConfirmCode(); $this->sendConfirmationCode($rt, $phone_number, $code); return $res->getReturnResponse(); } public function privacySettings(Request $req) { $required_params = [ 'priv_third_party', // 'priv_promo', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } // set privacy settings $priv_promo = $req->request->get('priv_promo', false); $priv_third_party = $req->request->get('priv_third_party'); $cust->setPrivacyThirdParty($priv_third_party) ->setPrivacyPromo($priv_promo); // get the policy ids from .env $dotenv = new Dotenv(); $dotenv->loadEnv(__DIR__.'/../../.env'); $policy_promo_id = $_ENV['POLICY_PROMO']; $policy_third_party_id = $_ENV['POLICY_THIRD_PARTY']; // check if privacy settings are true // if true, set the private policy for the customer if ($priv_promo) { // find the promo policy $policy = $em->getRepository(PrivacyPolicy::class)->find($policy_promo_id); // set policy id if ($policy != null) { $cust->setPrivacyPolicyPromo($policy); } } if ($priv_third_party) { // find the third party policy $policy = $em->getRepository(PrivacyPolicy::class)->find($policy_third_party_id); // set policy id if ($policy != null) { $cust->setPrivacyPolicyThirdParty($policy); } } $em->flush(); return $res->getReturnResponse(); } public function locationSupport(Request $req) { $required_params = [ 'longitude', 'latitude', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); $data = [ 'longitude' => $req->query->get('longitude'), 'latitude' => $req->query->get('latitude'), 'supported' => true, ]; $res->setData($data); return $res->getReturnResponse(); } public function activateWarranty(Request $req) { $required_params = ['plate_number']; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); $plate_number = $req->request->get('plate_number'); // find warranty using plate number $warranty_results = $em->getRepository(Warranty::class)->findBy(['plate_number' => $plate_number], ['date_create' => 'desc']); // check if warranty_results is empty if (empty($warranty_results)) { $res->setError(true) ->setErrorMessage('No warranty found for plate number'); return $res->getReturnResponse(); } // activate all entries foreach ($warranty_results as $warranty) { $warranty->setActivated(); } $em->flush(); return $res->getReturnResponse(); } protected function findWarranty($plate_number) { $em = $this->getDoctrine()->getManager(); // NOTE: Modify the search for the latest warranty. This seems hacky. // get latest warranty using plate number $warranty_results = $em->getRepository(Warranty::class)->findBy(['plate_number' => $plate_number], ['date_create' => 'desc']); $warr = []; // check if warranty_results is empty if (empty($warranty_results)) { /* $res->setError(true) ->setErrorMessage('No warranty found for plate number'); return $res->getReturnResponse(); */ return $warr; } // get first entry $warranty = current($warranty_results); // check for null values for battery and date claim and date expire $batt_model = ''; $batt_size = ''; $sap_batt = ''; $claim_date = ''; $expiry_date = ''; if (!(is_null($warranty->getBatteryModel()))) { $batt_model = $warranty->getBatteryModel()->getName(); } if (!(is_null($warranty->getBatterySize()))) { $batt_size = $warranty->getBatterySize()->getName(); } if (!(is_null($warranty->getSAPBattery()))) { $sap_batt = $warranty->getSAPBattery()->getID(); } if (!(is_null($warranty->getDateClaim()))) { $claim_date = $warranty->getDateClaim()->format("d M Y"); } if (!(is_null($warranty->getDateExpire()))) { $expiry_date = $warranty->getDateExpire()->format("d M Y"); } $warr[] = [ 'id' => $warranty->getID(), 'serial' => $warranty->getSerial(), 'warranty_class' => $warranty->getWarrantyClass(), 'plate_number' => $warranty->getPlateNumber(), 'first_name' => $warranty->getFirstName(), 'last_name' => $warranty->getLastName(), 'mobile_number' => $warranty->getMobileNumber(), 'battery_model' => $batt_model, 'battery_size' => $batt_size, 'sap_battery' => $sap_batt, 'status' => $warranty->getStatus(), 'date_create' => $warranty->getDateCreate()->format("d M Y g:i A"), 'date_purchase' => $warranty->getDatePurchase()->format("d M Y"), 'date_expire' => $expiry_date, 'date_claim' => $claim_date, 'claim_from' => $warranty->getClaimedFrom(), 'is_activated' => $warranty->isActivated() ? 1 : 0, ]; return $warr; } public function listServices(Request $req) { $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // services $results = $em->getRepository(Service::class)->findAll(); if (empty($results)) { $res->setError(true) ->setErrorMessage('No services available.'); return $res->getReturnResponse(); } $services = []; foreach ($results as $result) { /* // get partners $partners = []; $service_partners = $result->getPartners(); foreach($service_partners as $sp) { $partners[] = [ 'id' => $sp->getID(), 'name' => $sp->getName(), 'branch' => $sp->getBranch(), 'address' => $sp->getAddress(), 'contact_nums' => $sp->getContactNumbers(), 'time_open' => $sp->getTimeOpen()->format("g:i A"), 'time_close' => $sp->getTimeClose()->format("g:i A"), ]; } */ $services[] = [ 'id' => $result->getID(), 'name' => $result->getName(), // 'partners' => $partners, ]; } $data['services'] = $services; $res->setData($data); return $res->getReturnResponse(); } public function getPartnerInformation(Request $req, $pid) { $required_params = []; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // get partner $partner = $em->getRepository(Partner::class)->findOneBy(['id' => $pid]); if ($partner == null) { $res->setError(true) ->setErrorMessage('No partner found.'); return $res->getReturnResponse(); } // get reviews for partner $reviews = $em->getRepository(Review::class)->findBy(['partner' => $partner]); // get average rating for all reviews $average_rating = 0; if (!empty($reviews)) { $rating = 0; foreach($reviews as $review) { $rating = $rating + $review->getRating(); } $average_rating = $rating / sizeof($reviews); } $data['partner'] = [ 'id' => $partner->getID(), 'name' => $partner->getName(), 'branch' => $partner->getBranch(), 'address' => $partner->getAddress(), 'contact_nums' => $partner->getContactNumbers(), 'time_open' => $partner->getTimeOpen()->format("g:i A"), 'time_close' => $partner->getTimeClose()->format("g:i A"), 'longitude' => $partner->getCoordinates()->getLongitude(), 'latitude' => $partner->getCoordinates()->getLatitude(), 'average_rating' => $average_rating, ]; $res->setData($data); return $res->getReturnResponse(); } public function getClosestPartners(Request $req) { $required_params = [ 'longitude', 'latitude', 'service_id', 'limit', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); $long = $req->query->get('longitude'); $lat = $req->query->get('latitude'); $service_id = $req->query->get('service_id'); $limit = $req->query->get('limit'); // get partners within range $query = $em->createQuery('SELECT p, st_distance(p.coordinates, point(:lng, :lat)) as dist FROM App\Entity\Partner p JOIN App\Entity\Service s where s.id = :service_id ORDER BY dist') ->setParameter('lat', $lat) ->setParameter('lng', $long) ->setParameter('service_id', $service_id); $query->setMaxResults($limit); $result = $query->getResult(); $data = []; $partners = []; foreach($result as $row) { $partners[] = [ 'id' => $row[0]->getID(), 'name' => $row[0]->getName(), 'branch' => $row[0]->getBranch(), 'address' => $row[0]->getAddress(), 'contact_nums' => $row[0]->getContactNumbers(), 'time_open' => $row[0]->getTimeOpen()->format("g:i A"), 'time_close' => $row[0]->getTimeClose()->format("g:i A"), 'longitude' => $row[0]->getCoordinates()->getLongitude(), 'latitude' => $row[0]->getCoordinates()->getLatitude(), 'db_distance' => $row['dist'], ]; } $data['partners'] = $partners; $res->setData($data); return $res->getReturnResponse(); } public function reviewPartner($pid, Request $req, EntityManagerInterface $em) { $required_params = [ 'rating', 'message', ]; $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); $rating = $req->request->get('rating'); $msg = $req->request->get('message'); // TODO: check rating if 1 - 5 // check if partner exists $partner = $em->getRepository(Partner::class)->find($pid); if ($partner == null) { $res->setError(true) ->setErrorMessage('No partner found.'); return $res->getReturnResponse(); } $rev = new Review(); $rev->setRating($rating) ->setMessage($msg) ->setPartner($partner) ->setMobileSession($this->session); // save to db $em->persist($rev); $em->flush(); $data = []; $res->setData($data); return $res->getReturnResponse(); } public function getNearestHubAndSlots(Request $req, EntityManagerInterface $em, MapTools $map_tools) { $required_params = [ 'longitude', 'latitude', ]; $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); $coordinates = new Point($req->query->get('longitude'), $req->query->get('latitude')); $nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $em, $map_tools); if (empty($nearest_hub_slots['hub'])) { $res->setError(true) ->setErrorMessage('No hub found.'); return $res->getReturnResponse(); } // make hub data $data = [ 'hub_id' => $nearest_hub_slots['hub']->getID(), 'hub_slots' => $nearest_hub_slots['slots'], ]; $res->setData($data); return $res->getReturnResponse(); } public function newRequestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo, MapTools $map_tools, InventoryManager $im, MQTTClient $mclient, RiderAssignmentHandlerInterface $rah) { // check required parameters and api key $required_params = [ 'service_type', 'cv_id', 'trade_in', 'long', 'lat', 'warranty', 'mode_of_payment', ]; $em = $this->getDoctrine()->getManager(); $res = $this->checkParamsAndKey($req, $em, $required_params); if ($res->isError()) return $res->getReturnResponse(); // trade in type $trade_in = $req->request->get('trade_in'); // address $address = $req->request->get('delivery_address', 'Set by mobile application'); // instructions $instructions = $req->request->get('delivery_instructions', ''); // longitude and latitude $long = $req->request->get('long'); $lat = $req->request->get('lat'); // 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(); } $hub = null; $hub_id = $req->request->get('hub_id'); if (strlen($hub_id) > 0) $hub = $em->getRepository(Hub::class)->find($hub_id); $schedule_date = $req->request->get('date_schedule'); $slot_id = $req->request->get('slot_id'); $advance_order = $req->request->get('flag_advance_order'); $flag_advance_order = ($advance_order === 'true') ? true : false; $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')) ->setAdvanceOrder($flag_advance_order); // customer $cust = $this->session->getCustomer(); if ($cust == null) { $res->setError(true) ->setErrorMessage('No customer information found'); return $res->getReturnResponse(); } $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); // send to invoice generator $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); // assign hub and rider // check if hub is null if ($hub == null) { // find nearest hub if (($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) || ($jo->getServicetype() == ServiceType::BATTERY_REPLACEMENT_WARRANTY)) { // get nearest hub // $nearest_hub = $this->findNearestHubWithInventory($jo, $batt, $em, $map_tools, $im); $nearest_hub = $this->findNearestHub($jo, $em, $map_tools); } else { $nearest_hub = $this->findNearestHub($jo, $em, $map_tools); } if (!empty($nearest_hub)) { //error_log('found nearest hub ' . $nearest_hub->getID()); // assign rider $available_riders = $nearest_hub->getAvailableRiders(); if (count($available_riders) > 0) { $assigned_rider = null; if (count($available_riders) > 1) { // 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); } else $assigned_rider = $available_riders[0]; //error_log('found rider ' . $assigned_rider->getID()); $jo->setHub($nearest_hub); $jo->setRider($assigned_rider); $jo->setStatus(JOStatus::ASSIGNED); $assigned_rider->setAvailable(false); } } } else { $date_schedule = null; if ((strlen($schedule_date) > 0) && (strlen($slot_id) > 0)) { $time_schedule = $this->getTimeFromSlot($slot_id); if (!empty($time_schedule)) { $s_date = $schedule_date . ' ' . $time_schedule; $date_schedule = DateTime::createFromFormat('Y-m-d H:i', $s_date); //error_log($date_schedule->format('Y-m-d H:i')); } } $jo->setHub($hub); $jo->setStatus(JOStatus::RIDER_ASSIGN); if ($date_schedule != null) $jo->setDateSchedule($date_schedule); } $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()); } if ($jo->getStatus() == JOStatus::RIDER_ASSIGN) { // add event logs for hub assignments $hub_assign_event = new JOEvent(); $hub_assign_event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::HUB_ASSIGN) ->setJobOrder($jo); $em->persist($hub_assign_event); // user mqtt event $payload = [ 'event' => 'outlet_assign' ]; $mclient->sendEvent($jo, $payload); } $em->flush(); // make invoice json data $invoice_data = [ 'total_price' => $invoice->getTotalPrice(), 'vat_ex_price' => $invoice->getVATExclusivePrice(), 'vat' => $invoice->getVAT(), 'discount' => $invoice->getDiscount(), 'trade_in' => $invoice->getTradeIn(), ]; $items = $invoice->getItems(); $items_data = []; foreach ($items as $item) { $items_data[] = [ 'title' => $item->getTitle(), 'qty' => $item->getQuantity() + 0, 'price' => $item->getPrice() + 0.0, ]; } $invoice_data['items'] = $items_data; // make job order data $data = [ 'jo_id' => $jo->getID(), 'invoice' => $invoice_data ]; // set data $res->setData($data); return $res->getReturnResponse(); } public function versionCheck(Request $req) { $res = new APIResult(); $required_params = [ 'version', ]; $missing = $this->checkMissingParameters($req, $required_params); if (count($missing) > 0) { $params = implode(', ', $missing); $res->setError(true) ->setErrorMessage('Missing parameter(s): ' . $params); return $res->getReturnResponse(); } $need_update = false; $api_version = $this->getParameter('api_version'); $app_version = $req->query->get('version'); $api_v = explode('.', $api_version); $app_v = explode('.', $app_version); if ($api_v[0] < $app_v[0]) { $res->setError(true) ->setErrorMessage('Invalid application version: ' . $app_version); return $res->getReturnResponse(); } if ($api_v[0] > $app_v[0]) $need_update = true; $data = [ 'need_update' => $need_update, ]; $res->setData($data); return $res->getReturnResponse(); } protected function findCustomerByNumber($number) { $em = $this->getDoctrine()->getManager(); $customers = $em->getRepository(Customer::class)->findBy(['phone_mobile' => $number]); // find the customer with the most number of cars $car_count = 0; $cust = null; foreach($customers as $customer) { $vehicles = $customer->getVehicles(); if (count($vehicles) > $car_count) { $car_count = count($vehicles); // "save" customer object $cust = $customer; } } return $cust; } protected function findNearestHub($jo, EntityManagerInterface $em, MapTools $map_tools) { // get the nearest 10 hubs $selected_hub = null; $hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10, date("H:i:s")); $nearest_hubs_with_distance = []; $nearest_branch_codes = []; foreach ($hubs as $hub) { $nearest_hubs_with_distance[] = $hub; //if (!empty($hub['hub']->getBranchCode())) // $nearest_branch_codes[] = $hub['hub']->getBranchCode(); } // check if nearest hubs have branch codes //if (count($nearest_branch_codes) == 0) // return $selected_hub; // assume all 10 have stock // find the nearest hub with available riders $nearest = null; foreach ($nearest_hubs_with_distance as $nhd) { // get number of available riders $count_riders = count($nhd['hub']->getAvailableRiders()); // get number of advance orders in the next 3 hours $time_now = new DateTime(); $date_end = new DateTime(); $date_end->add(new DateInterval('PT2H')); // NOTE: get advance orders via query // get JOs assigned to hub that are advance orders and scheduled within X hours with // for rider assignment status $query = $em->createQuery('select count(jo) from App\Entity\JobOrder jo where jo.hub = :hub and jo.flag_advance = true and jo.date_schedule <= :date_end and jo.status = :status'); $count_advance_orders = $query->setParameters([ 'hub' => $nhd['hub'], 'date_end' => $date_end, 'status' => JOStatus::RIDER_ASSIGN, ]) ->setMaxResults(1) ->getSingleScalarResult(); error_log('HUB - ' . $nhd['hub']->getID()); error_log('RIDER COUNT - ' . $count_riders); error_log('ADVANCE ORDER COUNT - ' . $count_advance_orders); // if (count($nhd['hub']->getAvailableRiders()) > 0) // if we have more riders than we have advance orders if ($count_riders - $count_advance_orders > 0) { if (empty($nearest)) $nearest = $nhd; else { if ($nhd['distance'] < $nearest['distance']) $nearest = $nhd; } } } $selected_hub = $nearest['hub']; return $selected_hub; } protected function findNearestHubWithInventory($jo, Battery $batt, EntityManagerInterface $em, MapTools $map_tools, InventoryManager $im) { // get the nearest 10 hubs $selected_hub = null; $hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10, date("H:i:s")); $nearest_hubs_with_distance = []; $nearest_branch_codes = []; foreach ($hubs as $hub) { $nearest_hubs_with_distance[] = $hub; //if (!empty($hub['hub']->getBranchCode())) // $nearest_branch_codes[] = $hub['hub']->getBranchCode(); } // check if nearest hubs have branch codes //if (count($nearest_branch_codes) == 0) // return $selected_hub; // assume all 10 have stock // find the nearest hub with available riders $nearest = null; foreach ($nearest_hubs_with_distance as $nhd) { if (count($nhd['hub']->getAvailableRiders()) > 0) { if (empty($nearest)) $nearest = $nhd; else { if ($nhd['distance'] < $nearest['distance']) $nearest = $nhd; } } } $selected_hub = $nearest['hub']; // uncomment this snippet when inventory check becomes active // get battery sku /* if ($batt != null) { $skus[] = $batt->getSAPCode(); // api call to check inventory // pass the list of branch codes of nearest hubs and the skus // go through returned list of branch codes // bypass inventory check for now // $hubs_with_inventory = $im->getBranchesInventory($nearest_branch_codes, $skus); if (!empty($hubs_with_inventory)) { $nearest = []; $flag_hub_found = false; foreach ($hubs_with_inventory as $hub_with_inventory) { // find hub according to branch code $found_hub = $em->getRepository(Hub::class)->findOneBy(['branch_code' => $hub_with_inventory['BranchCode']]); if ($found_hub != null) { // check rider availability if (count($found_hub->getAvailableRiders()) > 0) { // check against nearest hubs with distance foreach ($nearest_hubs_with_distance as $nhd) { // get distance of hub from location, compare with $nearest. if less, replace nearest if ($found_hub->getID() == $nhd['hub']->getID()) { if (empty($nearest)) { $nearest = $nhd; $flag_hub_found = true; } else { if ($nhd['distance'] < $nearest['distance']) { $nearest = $nhd; $flag_hub_found = true; } } } } } } } $selected_hub = $nearest['hub']; } } */ return $selected_hub; } protected function randomizeRider($riders) { // TODO: get redis to track the sales per rider per day // check the time they came in // for now, randomize the rider $selected_index = array_rand($riders); $selected_rider = $riders[$selected_index]; return $selected_rider; } protected function findAdvanceNearestHubAndSlots(Point $coordinates, EntityManagerInterface $em, MapTools $map_tools) { // get the nearest 10 hubs $hub_data = []; $hubs = $map_tools->getClosestOpenHubs($coordinates, 10); foreach ($hubs as $hub) { $nearest_hubs_with_distance[] = $hub; // TODO: insert checking for branch code here when inventory manager is up } $nearest = null; $slot_found = false; // find the nearest hub foreach ($nearest_hubs_with_distance as $nhd) { if (empty($nearest)) $nearest = $nhd; else { if ($nhd['distance'] < $nearest['distance']) $nearest = $nhd; } } // get slots of nearest hub if ($nearest != null) { $hub_slots = $this->getHubRiderSlots($nearest['hub'], $em); $hub_data = [ 'hub' => $nearest['hub'], 'slots' => $hub_slots, ]; } return $hub_data; } protected function getHubRiderSlots(Hub $hub, EntityManagerInterface $em) { // check hub's advance orders for the day // get number of advance orders for the next day if request came in before midnight // or for current day if request came in after midnight // check request_time $request_time = time(); $midnight = strtotime('00:00'); $start_date = new DateTime(); $end_date = new DateTime(); if ($request_time < $midnight) { // add +1 to start date to get the next day // add +3 to date to end date to get the advance orders for the next three days $start_date->add(new DateInterval('P1D')); } $end_date->add(new DateInterval('P2D')); // set time bounds for the start and end date $start_date->setTime(0, 1); $end_date->setTime(23, 59); $str_request_time = date('Y-m-d H:i:s', $request_time); $time_of_request = DateTime::createFromFormat('Y-m-d H:i:s', $str_request_time); // NOTE: get advance orders via query // get JOs assigned to hub that are advance orders and scheduled for the next three days with // for hub assignment status $query = $em->createQuery('select jo from App\Entity\JobOrder jo where jo.hub = :hub and jo.flag_advance = true and jo.date_schedule >= :date_start and jo.date_schedule <= :date_end and jo.status != :status_cancelled and jo.status != :status_fulfilled'); $jos_advance_orders = $query->setParameters([ 'hub' => $hub, 'date_start' => $start_date, 'date_end' => $end_date, 'status_cancelled' => JOStatus::CANCELLED, 'status_fulfilled' => JOStatus::FULFILLED, ]) ->getResult(); // check each JO's date_schedule, decrement rider_slots if date schedule falls in that slot // index - equivalent time // 0 - 8-9 // 1 - 9-10 // 2 - 10-11 // 3 - 11-12 // 4 - 12 -13 // 5 - 13-14 // 6 - 14-15 // 7 - 15-16 // 8 - 16-17 $hub_slots = []; // get the dates for the next three days $first_date = $start_date->format('Y-m-d'); // 2nd day $second_date = $start_date->add(new DateInterval('P1D')); $sec_date = $second_date->format('Y-m-d'); // third day $third_date = $end_date->format('Y-m-d'); // array of # of riders that can handle JOs in a timeslot $hub_rider_slots = []; // populate the array with the hub's rider slots per day for ($i = 0; $i <= self::RIDER_SLOT_ARRAY_LENGTH; $i++) { $hub_rider_slots[$first_date][$i] = $hub->getRiderSlots(); } for ($i = 0; $i <= self::RIDER_SLOT_ARRAY_LENGTH; $i++) { $hub_rider_slots[$sec_date][$i] = $hub->getRiderSlots(); } for ($i = 0; $i <= self::RIDER_SLOT_ARRAY_LENGTH; $i++) { $hub_rider_slots[$third_date][$i] = $hub->getRiderSlots(); } // no advance orders if (empty($jos_advance_orders)) { // set hub slots for the next three days $slot_status = true; $hub_slots[$first_date] = $this->setAllHubSlots($slot_status); $hub_slots[$sec_date] = $this->setAllHubSlots($slot_status); $hub_slots[$third_date] = $this->setAllHubSlots($slot_status); } else { foreach ($jos_advance_orders as $advance_jo) { // get date schedules of JO $jo_date_schedule = $advance_jo->getDateSchedule(); $date_schedule = $jo_date_schedule->format('Y-m-d'); $time_schedule = $jo_date_schedule->format('H:i'); $hour_schedule = $jo_date_schedule->format('H'); $minute_schedule = $jo_date_schedule->format('i'); // check which of the three dates does date schedule fall // and if date is already in hub slots if (($first_date == $date_schedule) && (!isset($hub_slots[$first_date]))) $hub_slots[$first_date] = []; if (($sec_date == $date_schedule) && (!isset($hub_slots[$sec_date]))) $hub_slots[$sec_date] = []; if (($third_date == $date_schedule) && (!isset($hub_slots[$third_date]))) $hub_slots[$third_date] = []; // decrement number of hub rider slots then // set hub slots if ($date_schedule == $first_date) $this->decrementRiderSlots($hub_rider_slots, $first_date, $hour_schedule, $minute_schedule); if ($date_schedule == $sec_date) $this->decrementRiderSlots($hub_rider_slots, $sec_date, $hour_schedule, $minute_schedule); if ($date_schedule == $third_date) $this->decrementRiderSlots($hub_rider_slots, $third_date, $hour_schedule, $minute_schedule); $hub_slots[$first_date] = $this->setHubSlots($hub_rider_slots, $first_date); $hub_slots[$sec_date] = $this->setHubSlots($hub_rider_slots, $sec_date); $hub_slots[$third_date] = $this->setHubSlots($hub_rider_slots, $third_date); } } return $hub_slots; } protected function setAllHubSlots($status) { $data = []; // set all slots to true $data = [ [ 'id' => '08_09', 'label' => '8:00 AM', 'available' => $status, ], [ 'id' => '09_10', 'label' => '9:00 AM', 'available' => $status, ], [ 'id' => '10_11', 'label' => '10:00 AM', 'available' => $status, ], [ 'id' =>'11_12', 'label' => '11:00 AM', 'available' => $status, ], [ 'id' => '12_13', 'label' => '12:00 PM', 'available' => $status, ], [ 'id' => '13_14', 'label' => '1:00 PM', 'available' => $status, ], [ 'id' => '14_15', 'label' => '2:00 PM', 'available' => $status, ], [ 'id' => '15_16', 'label' => '3:00 PM', 'available' => $status, ], [ 'id' => '16_17', 'label' => '4:00 PM', 'available' => $status, ], ]; return $data; } protected function setHubSlots($hub_rider_slots, $schedule_date) { $data = []; // check values of hub_rider_slot to mark if available or not for ($i = 0; $i <= self::RIDER_SLOT_ARRAY_LENGTH; $i++) { if ($hub_rider_slots[$schedule_date][$i] > 0) { // set the time label switch($i) { case '0': $data[] = [ 'id' => '08_09', 'label' => '8:00 AM', 'available' => true, ]; break; case '1': $data[] = [ 'id' => '09_10', 'label' => '9:00 AM', 'available' => true, ]; break; case '2': $data[] = [ 'id' => '10_11', 'label' => '10:00 AM', 'available' => true, ]; break; case '3': $data[] = [ 'id' => '11_12', 'label' => '11:00 AM', 'available' => true, ]; break; case '4': $data[] = [ 'id' => '12_13', 'label' => '12:00 PM', 'available' => true, ]; break; case '5': $data[] = [ 'id' => '13_14', 'label' => '1:00 PM', 'available' => true, ]; break; case '6': $data[] = [ 'id' => '14_15', 'label' => '2:00 PM', 'available' => true, ]; break; case '7': $data[] = [ 'id' => '15_16', 'label' => '3:00 PM', 'available' => true, ]; break; case '8': $data[] = [ 'id' => '16_17', 'label' => '4:00 PM', 'available' => true, ]; break; default: error_log('Invalid rider slot index.'); } } else { // set the time label switch($i) { case '0': $data[] = [ 'id' => '08_09', 'label' => '8:00 AM', 'available' => false, ]; break; case '1': $data[] = [ 'id' => '09_10', 'label' => '9:00 AM', 'available' => false, ]; break; case '2': $data[] = [ 'id' => '10_11', 'label' => '10:00 AM', 'available' => false, ]; break; case '3': $data[] = [ 'id' => '11_12', 'label' => '11:00 AM', 'available' => false, ]; break; case '4': $data[] = [ 'id' => '12_13', 'label' => '12:00 PM', 'available' => false, ]; break; case '5': $data[] = [ 'id' => '13_14', 'label' => '1:00 PM', 'available' => false, ]; break; case '6': $data[] = [ 'id' => '14_15', 'label' => '2:00 PM', 'available' => false, ]; break; case '7': $data[] = [ 'id' => '15_16', 'label' => '3:00 PM', 'available' => false, ]; break; case '8': $data[] = [ 'id' => '16_17', 'label' => '4:00 PM', 'available' => false, ]; break; default: error_log('Invalid rider slot index.'); } } } return $data; } protected function decrementRiderSlots(&$hub_rider_slots, $date_schedule, $hour_schedule, $minute_schedule) { if ($minute_schedule != '00') { switch($hour_schedule) { case '8': $hub_rider_slots[$date_schedule][0]--; $hub_rider_slots[$date_schedule][1]--; break; case '9': $hub_rider_slots[$date_schedule][1]--; $hub_rider_slots[$date_schedule][2]--; break; case '10': $hub_rider_slots[$date_schedule][2]--; $hub_rider_slots[$date_schedule][3]--; break; case '11': $hub_rider_slots[$date_schedule][3]--; $hub_rider_slots[$date_schedule][4]--; break; case '12': $hub_rider_slots[$date_schedule][4]--; $hub_rider_slots[$date_schedule][5]--; break; case '13': $hub_rider_slots[$date_schedule][5]--; $hub_rider_slots[$date_schedule][6]--; break; case '14': $hub_rider_slots[$date_schedule][6]--; $hub_rider_slots[$date_schedule][7]--; break; case '15': $hub_rider_slots[$date_schedule][7]--; $hub_rider_slots[$date_schedule][8]--; break; case '16': error_log('No slots for the day'); break; default: error_log('Does not fit in any time slot. ' . $time_schedule); } } else { switch ($hour_schedule) { case '8': $hub_rider_slots[$date_schedule][0]--; break; case '9': $hub_rider_slots[$date_schedule][1]--; break; case '10': $hub_rider_slots[$date_schedule][2]--; break; case '11': $hub_rider_slots[$date_schedule][3]--; break; case '12': $hub_rider_slots[$date_schedule][4]--; break; case '13': $hub_rider_slots[$date_schedule][5]--; break; case '14': $hub_rider_slots[$date_schedule][6]--; break; case '15': $hub_rider_slots[$date_schedule][7]--; break; case '16': $hub_rider_slots[$date_schedule][8]--; break; default: error_log('Does not fit in any time slot.' . $time_schedule); } } } protected function getTimeFromSlot($slot_id) { $time_selected = ''; switch($slot_id) { case '08_09': $time_selected = AdvanceOrderSlot::_08_09; break; case '09_10': $time_selected = AdvanceOrderSlot::_09_10; break; case '10_11': $time_selected = AdvanceOrderSlot::_10_11; break; case '11_12': $time_selected = AdvanceOrderSlot::_11_12; break; case '12_13': $time_selected = AdvanceOrderSlot::_12_13; break; case '13_14': $time_selected = AdvanceOrderSlot::_13_14; break; case '14_15': $time_selected = AdvanceOrderSlot::_14_15; break; case '15_16': $time_selected = AdvanceOrderSlot::_15_16; break; case '16_17': $time_selected = AdvanceOrderSlot::_16_17; break; default: error_log('Invalid slot id ' . $slot_id); } return $time_selected; } }