diff --git a/config/routes/api.yaml b/config/routes/api.yaml index f55b0d83..aa1ce722 100644 --- a/config/routes/api.yaml +++ b/config/routes/api.yaml @@ -149,3 +149,13 @@ api_partner_review: path: /api/partners/{pid}/review controller: App\Controller\APIController:reviewPartner methods: [POST] + +api_nearest_hub_slots: + path: /api/hub_slots + controller: App\Controller\APIController::getNearestHubAndSlots + methods: [GET] + +api_new_jo_request: + path: /api/new_job_order + controller: App\Controller\APIController::newRequestJobOrder + methods: [POST] diff --git a/src/Controller/APIController.php b/src/Controller/APIController.php index 38472669..4c40af9f 100644 --- a/src/Controller/APIController.php +++ b/src/Controller/APIController.php @@ -23,6 +23,7 @@ use App\Ramcar\APIRiderStatus; use App\Ramcar\TransactionOrigin; use App\Ramcar\TradeInType; use App\Ramcar\JOEventType; +use App\Ramcar\AdvanceOrderSlot; use App\Service\InvoiceGeneratorInterface; use App\Service\RisingTideGateway; @@ -57,6 +58,8 @@ use Exception; // mobile API class APIController extends Controller implements LoggedController { + const RIDER_SLOT_ARRAY_LENGTH = 8; + protected $session; public function __construct() @@ -989,214 +992,119 @@ class APIController extends Controller implements LoggedController $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); - // NOTE: Start of code for auto assign advance JO. Uncomment the whole IF snippet when implementing auto assign advance order JO - /* - // check if advance order - // check for time when request came in - $request_time_in_int = time(); - $start = '17:00'; - $end = '08:00'; - - $start_time = strtotime($start); - $end_time = strtotime($end); - - if (($request_time_in_int > $start_time) || - (($request_time_in_int < $start_time) && ($request_time_in_int < $end_time))) + // assign hub and rider + if (($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) || + ($jo->getServicetype() == ServiceType::BATTERY_REPLACEMENT_WARRANTY)) { - // advance order - // 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); - // TODO: when the inventory manager kicks in, create new function that finds the - // the nearest hub with inventory and rider slot - // convert to DateTime - $nearest_hub = $this->findAdvanceNearestHub($jo, $request_time_in_int, $em, $map_tools); - } - else - { - $nearest_hub = $this->findAdvanceNearestHub($jo, $request_time_in_int, $em, $map_tools); - } - - if (!empty($nearest_hub)) - { - $jo->setHub($nearest_hub); - $jo->setStatus(JOStatus::RIDER_ASSIGN); - } - - $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::RIDER_ASSIGN) - { - // 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); - } - - $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); - + // get nearest hub + // $nearest_hub = $this->findNearestHubWithInventory($jo, $batt, $em, $map_tools, $im); + $nearest_hub = $this->findNearestHub($jo, $em, $map_tools); } else - { */ - // NOTE: End of IF snippet for auto assign JO. Uncomment the whole IF snippet when implementing auto assign advance order JO - // 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); - } + { + $nearest_hub = $this->findNearestHub($jo, $em, $map_tools); + } - if (!empty($nearest_hub)) + if (!empty($nearest_hub)) + { + //error_log('found nearest hub ' . $nearest_hub->getID()); + // assign rider + $available_riders = $nearest_hub->getAvailableRiders(); + if (count($available_riders) > 0) { - //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) { - $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) { - // 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); + $riders[] = $rider; } - 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); + $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); + $em->persist($jo); + $em->persist($invoice); - // add event log for JO - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::CREATE) + // 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($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); - $em->persist($hub_assign_event); + $rider_assign_event = new JOEvent(); + $rider_assign_event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::RIDER_ASSIGN) + ->setJobOrder($jo); - $rider_assign_event = new JOEvent(); - $rider_assign_event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::RIDER_ASSIGN) - ->setJobOrder($jo); + $em->persist($rider_assign_event); - $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(), + // user mqtt event + $payload = [ + 'event' => 'outlet_assign' ]; - $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; + $mclient->sendEvent($jo, $payload); - // make job order data - $data = [ - 'jo_id' => $jo->getID(), - 'invoice' => $invoice_data + $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; - // set data - $res->setData($data); - //} // NOTE: uncomment this curly when auto assign advance order JO is to be implemented + // make job order data + $data = [ + 'jo_id' => $jo->getID(), + 'invoice' => $invoice_data + ]; + + // set data + $res->setData($data); return $res->getReturnResponse(); } @@ -2364,6 +2272,376 @@ class APIController extends Controller implements LoggedController 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(); + } + protected function findCustomerByNumber($number) { $em = $this->getDoctrine()->getManager(); @@ -2561,29 +2839,21 @@ class APIController extends Controller implements LoggedController return $selected_rider; } - protected function findAdvanceNearestHub($jo, $request_time, EntityManagerInterface $em, MapTools $map_tools) + protected function findAdvanceNearestHubAndSlots(Point $coordinates, EntityManagerInterface $em, MapTools $map_tools) { // get the nearest 10 hubs - $selected_hub = null; - $hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10); + $hub_data = []; + $hubs = $map_tools->getClosestOpenHubs($coordinates, 10); - $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(); + // TODO: insert checking for branch code here when inventory manager is up } - // 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; $slot_found = false; + // find the nearest hub foreach ($nearest_hubs_with_distance as $nhd) { if (empty($nearest)) @@ -2593,114 +2863,41 @@ class APIController extends Controller implements LoggedController if ($nhd['distance'] < $nearest['distance']) $nearest = $nhd; } - - $result = $this->findRiderSlots($jo, $nearest['hub'], $request_time, $em); - - if ($result != null) - $slot_found = $result['slot_found']; - else - $nearest = null; } - if ($slot_found && $nearest != null) + // get slots of nearest hub + if ($nearest != null) { - $result = $this->findRiderSlots($jo, $nearest['hub'], $request_time, $em); - - if ($result != null) - { - $jo_date_schedule = $result['date_schedule']; - - $jo->setAdvanceOrder(true); - $jo->setDateSchedule($jo_date_schedule); - - $selected_hub = $nearest['hub']; - } + $hub_slots = $this->getHubRiderSlots($nearest['hub'], $em); + $hub_data = [ + 'hub' => $nearest['hub'], + 'slots' => $hub_slots, + ]; } - // uncomment this snippet when inventory check becomes active - // get battery sku - /* - if ($batt != null) - { - $skus[] = $batt->getSAPCode(); + return $hub_data; - // 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 findRiderSlots($jo, Hub $hub, $request_time, EntityManagerInterface $em) + protected function getHubRiderSlots(Hub $hub, EntityManagerInterface $em) { - $flag_found_slot = false; - $results = []; - - // array of # of riders that can handle JOs in a timeslot - $hub_rider_slots = []; - - // populate the array with the hub's rider slots - for ($i = 0; $i <=7; $i++) - { - $hub_rider_slots[$i] = $hub->getRiderSlots(); - } - // 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 - $midnight = strtotime('00:00'); + $request_time = time(); + $midnight = strtotime('00:00'); $start_date = new DateTime(); - $end_date = new DateTime(); + $end_date = new DateTime(); - if ($request_time < $midnight) + if ($request_time < $midnight) { - // add +1 to date to start and end date + // 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('P1D')); - } + } + $end_date->add(new DateInterval('P2D')); // set time bounds for the start and end date $start_date->setTime(0, 1); @@ -2710,15 +2907,17 @@ class APIController extends Controller implements LoggedController $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 day(if before midnight) or current day(if after midnight) with + // 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'); + 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' => JOStatus::ASSIGNED, + 'status_cancelled' => JOStatus::CANCELLED, + 'status_fulfilled' => JOStatus::FULFILLED, ]) ->getResult(); @@ -2732,135 +2931,428 @@ class APIController extends Controller implements LoggedController // 5 - 13-14 // 6 - 14-15 // 7 - 15-16 - foreach ($jos_advance_orders as $advance_jo) + // 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++) { - // get time schedule - $date_schedule = $advance_jo->getDateSchedule(); - $time_schedule = $date_schedule->format('H: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(); + } - $hour_schedule = $date_schedule->format('H'); - $minute_schedule = $date_schedule->format('i'); + // no advance orders + if (empty($jos_advance_orders)) + { + // set hub slots for the next three days + $slot_status = true; - // check minutes. for now, if not 00, take up current slot and next one - // if hour fits in the last slot but minutes is not 00, move to the next available hub - // TODO: need to add custom minutes threshold - if ($minute_schedule != '00') + $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) { - switch($hour_schedule) { + // 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[] = [ + '08_09' => [ + 'label' => '8-9 AM', + 'available' => $status, + ], + '09_10' => [ + 'label' => '9-10 AM', + 'available' => $status, + ], + '10_11' => [ + 'label' => '10-11 AM', + 'available' => $status, + ], + '11_12' => [ + 'label' => '11 AM-12 PM', + 'available' => $status, + ], + '13_14' => [ + 'label' => '12 PM-1 PM', + 'available' => $status, + ], + '14_15' => [ + 'label' => '1-2 PM', + 'available' => $status, + ], + '14_15' => [ + 'label' => '2-3 PM', + 'available' => $status, + ], + '15_16' => [ + 'label' => '3-4 PM', + 'available' => $status, + ], + '16_17' => [ + 'label' => '4-5 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[] = [ + '08_09' => [ + 'label' => '8-9 AM', + 'available' => true, + ] + ]; + break; + case '1': + $data[] = [ + '09_10' => [ + 'label' => '9-10 AM', + 'available' => true, + ] + ]; + break; + case '2': + $data[] = [ + '10_11' => [ + 'label' => '10-11 AM', + 'available' => true, + ] + ]; + break; + case '3': + $data[] = [ + '11_12' => [ + 'label' => '11 AM-12 PM', + 'available' => true, + ] + ]; + break; + case '4': + $data[] = [ + '12_13' => [ + 'label' => '12 PM-1 PM', + 'available' => true, + ] + ]; + break; + case '5': + $data[] = [ + '13_14' => [ + 'label' => '1-2 PM', + 'available' => true, + ] + ]; + break; + case '6': + $data[] = [ + '14_15' => [ + 'label' => '2-3 PM', + 'available' => true, + ] + ]; + break; + case '7': + $data[] = [ + '15_16' => [ + 'label' => '3-4 PM', + 'available' => true, + ] + ]; + break; case '8': - $hub_rider_slots[0]--; - $hub_rider_slots[1]--; - break; - case '9': - $hub_rider_slots[1]--; - $hub_rider_slots[2]--; - break; - case '10': - $hub_rider_slots[2]--; - $hub_rider_slots[3]--; - break; - case '11': - $hub_rider_slots[3]--; - $hub_rider_slots[4]--; - break; - case '12': - $hub_rider_slots[4]--; - $hub_rider_slots[5]--; - break; - case '13': - $hub_rider_slots[5]--; - $hub_rider_slots[6]--; - break; - case '14': - $hub_rider_slots[6]--; - $hub_rider_slots[7]--; - break; - case '15': - error_log('No slots for the day'); + $data[] = [ + '16_17' => [ + 'label' => '4-5 PM', + 'available' => true, + ] + ]; break; default: - error_log('Does not fit in any time slot. ' . $time_schedule); + error_log('Invalid rider slot index.'); } } else { - switch ($hour_schedule) { - case '8': - $hub_rider_slots[0]--; - break; - case '9': - $hub_rider_slots[1]--; - break; - case '10': - $hub_rider_slots[2]--; - break; - case '11': - $hub_rider_slots[3]--; - break; - case '12': - $hub_rider_slots[4]--; - break; - case '13': - $hub_rider_slots[5]--; - break; - case '14': - $hub_rider_slots[6]--; - break; - case '15': - $hub_rider_slots[7]--; - break; - default: - error_log('Does not fit in any time slot.' . $time_schedule); - } - } - - } - - // find first free slot - foreach ($hub_rider_slots as $index => $rider_slot) - { - if ($rider_slot > 0) - { - $flag_found_slot = true; - - $jo_date_schedule = $start_date; - switch ($index) { + // set the time label + switch($i) { case '0': - $jo_date_schedule->setTime(8,0); + $data[] = [ + '08_09' => [ + 'label' => '8-9 AM', + 'available' => false, + ] + ]; break; case '1': - $jo_date_schedule->setTime(9,0); + $data[] = [ + '09_10' => [ + 'label' => '9-10 AM', + 'available' => false, + ] + ]; break; case '2': - $jo_date_schedule->setTime(10,0); + $data[] = [ + '10_11' => [ + 'label' => '10-11 AM', + 'available' => false, + ] + ]; break; case '3': - $jo_date_schedule->setTime(11,0); + $data[] = [ + '11_12' => [ + 'label' => '11 AM-12 PM', + 'available' => false, + ] + ]; break; case '4': - $jo_date_schedule->setTime(12,0); + $data[] = [ + '12_13' => [ + 'label' => '12 PM-1 PM', + 'available' => false, + ] + ]; break; case '5': - $jo_date_schedule->setTime(13,0); + $data[] = [ + '13_14' => [ + 'label' => '1-2 PM', + 'available' => false, + ] + ]; break; case '6': - $jo_date_schedule->setTime(14,0); + $data[] = [ + '14_15' => [ + 'label' => '2-3 PM', + 'available' => false, + ] + ]; break; case '7': - $jo_date_schedule->setTime(15,0); + $data[] = [ + '15_16' => [ + 'label' => '3-4 PM', + 'available' => false, + ] + ]; + break; + case '8': + $data[] = [ + '16_17' => [ + 'label' => '4-5 PM', + 'available' => false, + ] + ]; break; default: - error_log('Cannot find time slot.'); + error_log('Invalid rider slot index.'); } - - $results = [ - 'slot_found' => $flag_found_slot, - 'date_schedule' => $jo_date_schedule, - ]; - - break; } } - return $results; + + 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; + } } diff --git a/src/Ramcar/AdvanceOrderSlot.php b/src/Ramcar/AdvanceOrderSlot.php new file mode 100644 index 00000000..4967eeaf --- /dev/null +++ b/src/Ramcar/AdvanceOrderSlot.php @@ -0,0 +1,29 @@ + '08:00', + '09:00' => '09:00', + '10:00' => '10:00', + '11:00' => '11:00', + '12:00' => '12:00', + '13:00' => '13:00', + '14:00' => '14:00', + '15:00' => '15:00', + '16:00' => '16:00', + ]; +}