633 lines
22 KiB
PHP
633 lines
22 KiB
PHP
<?php
|
|
|
|
namespace App\Controller\CustomerAppAPI;
|
|
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Catalyst\ApiBundle\Component\Response as ApiResponse;
|
|
use CrEOF\Spatial\PHP\Types\Geometry\Point;
|
|
|
|
use App\Ramcar\JOStatus;
|
|
use App\Service\GeofenceTracker;
|
|
use App\Service\InventoryManager;
|
|
use App\Service\MapTools;
|
|
use App\Entity\Hub;
|
|
use App\Entity\Battery;
|
|
use App\Entity\CustomerMetadata;
|
|
|
|
use DateTime;
|
|
use DateInterval;
|
|
|
|
class LocationController extends ApiController
|
|
{
|
|
public function locationSupport(Request $req, GeofenceTracker $geo)
|
|
{
|
|
// validate params
|
|
$validity = $this->validateRequest($req, [
|
|
'longitude',
|
|
'latitude',
|
|
]);
|
|
|
|
if (!$validity['is_valid']) {
|
|
return new ApiResponse(false, $validity['error']);
|
|
}
|
|
|
|
$long = $req->query->get('longitude');
|
|
$lat = $req->query->get('latitude');
|
|
|
|
// NOTE: had to add this for promo tag
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null) {
|
|
return new ApiResponse(false, 'No customer information found.');
|
|
}
|
|
|
|
$is_covered = false;
|
|
// check if customer still has promo
|
|
if (($cust->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) ||
|
|
($cust->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO'))
|
|
) {
|
|
// if has customer tag, customer has not availed of promo
|
|
$is_covered = true;
|
|
} else {
|
|
// geofence
|
|
$is_covered = $geo->isCovered($long, $lat);
|
|
}
|
|
|
|
// geofence
|
|
// $is_covered = $geo->isCovered($long, $lat);
|
|
|
|
$data = [
|
|
'longitude' => $long,
|
|
'latitude' => $lat,
|
|
'supported' => $is_covered,
|
|
];
|
|
|
|
// check if is_covered is false. If so, we need to set the error part in the response
|
|
if (!$is_covered) {
|
|
return new ApiResponse(false, $this->getGeoErrorMessage(), $data);
|
|
}
|
|
|
|
// response
|
|
return new ApiResponse(true, '', $data);
|
|
}
|
|
|
|
public function getNearestHubAndSlots(
|
|
Request $req,
|
|
MapTools $map_tools
|
|
) {
|
|
// validate params
|
|
$validity = $this->validateRequest($req, [
|
|
'longitude',
|
|
'latitude',
|
|
]);
|
|
|
|
if (!$validity['is_valid']) {
|
|
return new ApiResponse(false, $validity['error']);
|
|
}
|
|
|
|
$coordinates = new Point($req->query->get('longitude'), $req->query->get('latitude'));
|
|
|
|
// add checking if customer has a pre-registered hub
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null) {
|
|
return new ApiResponse(false, 'No customer information found.');
|
|
}
|
|
|
|
// check if customer has customer tag promo
|
|
if (($cust->getCustomerTag('TAG_CAR_CLUB_OFFICER_PROMO')) ||
|
|
($cust->getCustomerTag('TAG_CAR_CLUB_MEMBER_PROMO'))
|
|
) {
|
|
// if has customer tag, customer has not availed of promo, get the hub where customer is pre-registered
|
|
$car_club_cust_hub = $cust->getCarClubCustomerHub();
|
|
if ($car_club_cust_hub != null) {
|
|
// need to get the rider slots for the pre-registered hub
|
|
$hub = $car_club_cust_hub->getHub();
|
|
$nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $map_tools, $hub);
|
|
} else {
|
|
$nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $map_tools);
|
|
|
|
if (empty($nearest_hub_slots['hub'])) {
|
|
return new ApiResponse(false, 'Thank you for reaching out to us. Please expect a call from us and we will assist you with your request. Thank you and stay safe!');
|
|
}
|
|
}
|
|
} else {
|
|
$nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($coordinates, $map_tools);
|
|
|
|
if (empty($nearest_hub_slots['hub'])) {
|
|
return new ApiResponse(false, 'Thank you for reaching out to us. Please expect a call from us and we will assist you with your request. Thank you and stay safe!');
|
|
}
|
|
}
|
|
|
|
// response
|
|
return new ApiResponse(true, '', [
|
|
'hub_id' => $nearest_hub_slots['hub']->getID(),
|
|
'hub_slots' => $nearest_hub_slots['slots'],
|
|
]);
|
|
}
|
|
|
|
public function addLocation(Request $req)
|
|
{
|
|
// validate params
|
|
$validity = $this->validateRequest($req, [
|
|
'name',
|
|
'address',
|
|
'longitude',
|
|
'latitude',
|
|
'landmark',
|
|
]);
|
|
|
|
if (!$validity['is_valid']) {
|
|
return new ApiResponse(false, $validity['error']);
|
|
}
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null) {
|
|
return new ApiResponse(false, 'No customer information found.');
|
|
}
|
|
|
|
// get the information
|
|
$name = $req->request->get('name');
|
|
$address = $req->request->get('address');
|
|
$lng = $req->request->get('longitude');
|
|
$lat = $req->request->get('latitude');
|
|
$landmark = $req->request->get('landmark');
|
|
|
|
$loc_info = [
|
|
'address' => $address,
|
|
'longitude' => $lng,
|
|
'latitude' => $lat,
|
|
'landmark' => $landmark,
|
|
];
|
|
|
|
// check if customer already has existing metadata
|
|
$c_meta = $this->em->getRepository(CustomerMetadata::class)->findOneBy(['customer' => $cust]);
|
|
if ($c_meta == null) {
|
|
// create new customer meta
|
|
$cust_meta = new CustomerMetadata();
|
|
$cust_meta->setCustomer($cust);
|
|
$cust_meta->addMetaInfo($name, $loc_info);
|
|
|
|
$this->em->persist($cust_meta);
|
|
} else {
|
|
// limit locations to 6. If more than 6, pop the first one out
|
|
// add location to existing customer meta
|
|
$meta_count = count($c_meta->getAllMetaInfo());
|
|
|
|
if ($meta_count >= 6)
|
|
$c_meta->popMetaInfo();
|
|
|
|
$c_meta->addMetaInfo($name, $loc_info);
|
|
}
|
|
|
|
$this->em->flush();
|
|
|
|
// response
|
|
return new ApiResponse();
|
|
}
|
|
|
|
public function removeLocation($id, Request $req)
|
|
{
|
|
// validate params
|
|
$validity = $this->validateRequest($req);
|
|
|
|
if (!$validity['is_valid']) {
|
|
return new ApiResponse(false, $validity['error']);
|
|
}
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null) {
|
|
return new ApiResponse(false, 'No customer information found.');
|
|
}
|
|
|
|
// find customer metadata and delete entry if present
|
|
$cv = $this->em->getRepository(CustomerMetadata::class)->findOneBy(['customer' => $cust]);
|
|
if ($cv != null) {
|
|
$cv->deleteMetadataInfo(base64_decode($id));
|
|
}
|
|
|
|
$this->em->flush();
|
|
|
|
// response
|
|
return new ApiResponse();
|
|
}
|
|
|
|
public function getLocations(Request $req)
|
|
{
|
|
// validate params
|
|
$validity = $this->validateRequest($req);
|
|
|
|
if (!$validity['is_valid']) {
|
|
return new ApiResponse(false, $validity['error']);
|
|
}
|
|
|
|
// get customer
|
|
$cust = $this->session->getCustomer();
|
|
if ($cust == null) {
|
|
return new ApiResponse(false, 'No customer information found.');
|
|
}
|
|
|
|
// get the customer meta for customer
|
|
$locations = [];
|
|
$cust_meta = $this->em->getRepository(CustomerMetadata::class)->findOneBy(['customer' => $cust]);
|
|
if ($cust_meta != null) {
|
|
$locations = $cust_meta->getAllMetaInfo();
|
|
}
|
|
|
|
$data = [
|
|
'locations' => $locations,
|
|
];
|
|
|
|
// response
|
|
return new ApiResponse(true, '', $data);
|
|
}
|
|
|
|
protected function findAdvanceNearestHubAndSlots(Point $coordinates, MapTools $map_tools, $hub = null)
|
|
{
|
|
$hub_data = [];
|
|
|
|
if ($hub != null) {
|
|
// get the slots of hub
|
|
$hub_slots = $this->getHubRiderSlots($hub);
|
|
|
|
$slots = $hub_slots['slot_data'];
|
|
|
|
$hub_data = [
|
|
'hub' => $hub,
|
|
'slots' => $slots,
|
|
];
|
|
return $hub_data;
|
|
}
|
|
|
|
// get the nearest 10 hubs
|
|
$nearest_hubs_with_distance = [];
|
|
$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;
|
|
$hub_slots = [];
|
|
$slot_found = false;
|
|
// find the nearest hub
|
|
if (!empty($nearest_hubs_with_distance)) {
|
|
// get slots of nearest hub right after getting nearest hub.
|
|
// then check if hub has available slots. If not, get next nearest hub.
|
|
foreach ($nearest_hubs_with_distance as $nhd) {
|
|
if (empty($nearest)) {
|
|
// get the slots for the hub to check if hub is available for assignment
|
|
$hub_slots = $this->getHubRiderSlots($nhd['hub']);
|
|
|
|
$flag_hub_available = $hub_slots['flag_hub_available'];
|
|
if ($flag_hub_available == true) {
|
|
$nearest = $nhd;
|
|
}
|
|
} else {
|
|
if ($nhd['distance'] < $nearest['distance']) {
|
|
// get the slots for nearest which is nhd right now
|
|
$hub_slots = $this->getHubRiderSlots($nhd['hub']);
|
|
|
|
$flag_hub_available = $hub_slots['flag_hub_available'];
|
|
|
|
// if hub is available, set hub to nearest
|
|
if ($flag_hub_available == true) {
|
|
$nearest = $nhd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($nearest != null) {
|
|
// set hub data to what is in nearest
|
|
$hub_data = [
|
|
'hub' => $nearest['hub'],
|
|
'slots' => $hub_slots['slot_data'],
|
|
];
|
|
}
|
|
|
|
return $hub_data;
|
|
}
|
|
|
|
protected function getHubRiderSlots(Hub $hub)
|
|
{
|
|
// 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();
|
|
|
|
// to keep things simple, just start on next day regardless of midnight timer
|
|
$start_date->add(new DateInterval('P1D'));
|
|
$end_date->add(new DateInterval('P3D'));
|
|
|
|
/*
|
|
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('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);
|
|
|
|
// 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 = $this->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 request_time
|
|
|
|
// define slots
|
|
$slots = [
|
|
'08_09' => '8:00 AM',
|
|
'09_10' => '9:00 AM',
|
|
'10_11' => '10:00 AM',
|
|
'11_12' => '11:00 AM',
|
|
'12_13' => '12:00 PM',
|
|
'13_14' => '1:00 PM',
|
|
'14_15' => '2:00 PM',
|
|
'15_16' => '3:00 PM',
|
|
'16_17' => '4:00 PM',
|
|
];
|
|
|
|
|
|
// get the dates for the next three days
|
|
$first_date = $start_date->format('Y-m-d');
|
|
$second_date = $start_date->add(new DateInterval('P1D'));
|
|
$sec_date = $second_date->format('Y-m-d');
|
|
$third_date = $end_date->format('Y-m-d');
|
|
|
|
// define days
|
|
$days = [
|
|
$first_date => $first_date,
|
|
$sec_date => $sec_date,
|
|
$third_date => $third_date,
|
|
];
|
|
|
|
// initialize hub rider slots
|
|
$hub_rider_slots = [];
|
|
foreach ($days as $day) {
|
|
foreach ($slots as $slot_key => $slot) {
|
|
$hub_rider_slots[$day][$slot_key] = $hub->getRiderSlots();
|
|
}
|
|
}
|
|
|
|
// check each JO's date_schedule, decrement rider_slots if date schedule falls in that slot
|
|
foreach ($jos_advance_orders as $jo) {
|
|
// get date key
|
|
$date_sched = $jo->getDateSchedule();
|
|
$date_string = $date_sched->format('Y-m-d');
|
|
$hour = $date_sched->format('H');
|
|
$slot_id = sprintf('%02d_%02d', $hour, $hour + 1);
|
|
|
|
// error_log("SLOT - $date_string - $slot_id");
|
|
|
|
// decrement rider slot
|
|
if (isset($hub_rider_slots[$date_string][$slot_id]))
|
|
$hub_rider_slots[$date_string][$slot_id]--;
|
|
|
|
// check if it goes through next slot (10 min allowance)
|
|
$mins = $date_sched->format('i');
|
|
if ($mins > 10) {
|
|
$next_slot_id = sprintf('%02d_%02d', $hour + 1, $hour + 2);
|
|
// error_log("NEXT SLOT - $date_string - $next_slot_id");
|
|
// decrement rider slot
|
|
if (isset($hub_rider_slots[$date_string][$next_slot_id]))
|
|
$hub_rider_slots[$date_string][$next_slot_id]--;
|
|
}
|
|
}
|
|
|
|
// error_log(print_r($hub_rider_slots, true));
|
|
|
|
$hub_slots = $this->generateHubSlots($hub_rider_slots, $slots);
|
|
|
|
// error_log(print_r($hub_slots, true));
|
|
|
|
return $hub_slots;
|
|
}
|
|
|
|
protected function generateHubSlots($rider_slots, $slots)
|
|
{
|
|
$data = [];
|
|
$total_rslots = 0;
|
|
$total_unavailable_rslots = 0;
|
|
foreach ($rider_slots as $day_id => $rslot) {
|
|
$data[$day_id] = [];
|
|
|
|
foreach ($rslot as $slot_id => $avail_slots) {
|
|
// increment total rider slots
|
|
$total_rslots++;
|
|
|
|
$slot_data = [
|
|
'id' => $slot_id,
|
|
'label' => $slots[$slot_id],
|
|
'available' => true,
|
|
];
|
|
|
|
// mark unavailable ones
|
|
if ($avail_slots <= 0) { // increment total number of unavailable slots
|
|
$total_unavailable_rslots++;
|
|
$slot_data['available'] = false;
|
|
}
|
|
|
|
// add to day data
|
|
$data[$day_id][] = $slot_data;
|
|
}
|
|
}
|
|
|
|
// check if hub has available slots
|
|
$hub_available = true;
|
|
// error_log('total rider slots ' . $total_rslots);
|
|
// error_log('total unavailable slots ' . $total_unavailable_rslots);
|
|
if ($total_rslots == $total_unavailable_rslots) {
|
|
// error_log('hub has no available slots');
|
|
$hub_available = false;
|
|
}
|
|
|
|
$hs_data = [
|
|
'flag_hub_available' => $hub_available,
|
|
'slot_data' => $data,
|
|
];
|
|
|
|
return $hs_data;
|
|
}
|
|
|
|
protected function findNearestHub($jo, 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 = $this->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,
|
|
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 = $this->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;
|
|
}
|
|
}
|