224 lines
7.7 KiB
PHP
224 lines
7.7 KiB
PHP
<?php
|
|
|
|
namespace App\Service;
|
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
|
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
|
|
|
use CrEOF\Spatial\PHP\Types\Geometry\Point;
|
|
|
|
use App\Service\HubDistributor;
|
|
use App\Service\InventoryManager;
|
|
use App\Service\HubFilterLogger;
|
|
use App\Service\RisingTideGateway;
|
|
|
|
use App\Ramcar\HubCriteria;
|
|
|
|
class HubSelector
|
|
{
|
|
protected $container;
|
|
protected $em;
|
|
protected $im;
|
|
protected $hub_distributor;
|
|
protected $hub_filter_logger;
|
|
protected $trans;
|
|
protected $rt;
|
|
|
|
public function __construct(ContainerInterface $container, EntityManagerInterface $em, InventoryManager $im,
|
|
HubDistributor $hub_distributor, HubFilterLogger $hub_filter_logger,
|
|
TranslatorInterface $trans, RisingTideGateway $rt)
|
|
{
|
|
$this->container = $container;
|
|
$this->em = $em;
|
|
$this->im = $im;
|
|
$this->hub_distributor = $hub_distributor;
|
|
$this->hub_filter_logger = $hub_filter_logger;
|
|
$this->trans = $trans;
|
|
$this->rt = $rt;
|
|
}
|
|
|
|
// TODO: move this to env or something so we can enable and disable filters without code changes
|
|
protected function getActiveFilters(): array
|
|
{
|
|
return [
|
|
'App\Service\HubFilter\Filters\DateAndTimeHubFilter',
|
|
'App\Service\HubFilter\Filters\JoTypeHubFilter',
|
|
'App\Service\HubFilter\Filters\PaymentMethodHubFilter',
|
|
'App\Service\HubFilter\Filters\RiderAvailabilityHubFilter',
|
|
'App\Service\HubFilter\Filters\InventoryHubFilter',
|
|
'App\Service\HubFilter\Filters\RoundRobinHubFilter',
|
|
'App\Service\HubFilter\Filters\MaxResultsHubFilter',
|
|
];
|
|
}
|
|
|
|
public function find(HubCriteria $criteria)
|
|
{
|
|
$point = $criteria->getPoint();
|
|
$limit_results = $criteria->getLimitResults();
|
|
$limit_distance = $criteria->getLimitDistance();
|
|
$jo_type = $criteria->getJoType();
|
|
$flag_inventory_check = $criteria->hasInventoryCheck();
|
|
$flag_riders_check = $criteria->hasRidersCheck();
|
|
$items = $criteria->getItems();
|
|
$date_time = $criteria->getDateTime();
|
|
$payment_method = $criteria->getPaymentMethod();
|
|
$flag_emergency = $criteria->isEmergency();
|
|
$flag_round_robin = $criteria->isRoundRobin();
|
|
$jo_id = $criteria->getJobOrderId();
|
|
$customer_id = $criteria->getCustomerId();
|
|
$customer_class = $criteria->getCustomerClass();
|
|
|
|
// needed for JORejection records and SMS notifs
|
|
$order_date = $criteria->getOrderDate();
|
|
$service_type = $criteria->getServiceType();
|
|
|
|
// error_log('payment methods ' . $payment_method);
|
|
// error_log('distance limit ' . $limit_distance);
|
|
// error_log('emergency flag ' . $flag_emergency);
|
|
|
|
// get all the hubs within distance
|
|
$filtered_hubs = $this->getClosestHubs($point, $limit_distance, $jo_id, $customer_id);
|
|
|
|
// build param list
|
|
$params = [
|
|
'date_time' => $date_time,
|
|
'flag_inventory_check' => $flag_inventory_check,
|
|
'customer_class' => $customer_class,
|
|
'jo_type' => $jo_type,
|
|
'order_date' => $order_date,
|
|
'service_type' => $service_type,
|
|
'items' => $items,
|
|
'flag_emergency' => $flag_emergency,
|
|
'limit_results' => $limit_results,
|
|
'payment_method' => $payment_method,
|
|
'flag_riders_check' => $flag_riders_check,
|
|
'flag_round_robin' => $flag_round_robin,
|
|
];
|
|
|
|
// loop through all enabled filters
|
|
foreach ($this->getActiveFilters() as $hub_filter) {
|
|
// no hubs left to filter
|
|
if (empty($filtered_hubs)) {
|
|
break;
|
|
}
|
|
|
|
$f = $this->container->get($hub_filter);
|
|
|
|
// check if supported area is exempted from this filter
|
|
if ($this->isExemptedByArea($f->getID(), $point)) {
|
|
continue;
|
|
}
|
|
|
|
$f->setJOID($jo_id);
|
|
$f->setCustomerID($customer_id);
|
|
|
|
// get requested params only
|
|
$req_params = array_intersect_key($params, array_flip($f->getRequestedParams()));
|
|
|
|
// filter hub list
|
|
$filtered_hubs = $f->filter($filtered_hubs, $req_params);
|
|
|
|
// error_log($f->getID() . ' hubs ' . json_encode($filtered_hubs));
|
|
}
|
|
|
|
// error_log('final hub list ' . json_encode($filtered_hubs));
|
|
|
|
return $filtered_hubs;
|
|
}
|
|
|
|
protected function getClosestHubs(Point $point, $limit_distance, $jo_id, $customer_id)
|
|
{
|
|
// get closest hubs based on st_distance function from db
|
|
$query_string = 'SELECT h, st_distance(h.coordinates, point(:lng, :lat)) as dist FROM App\Entity\Hub h WHERE h.status_open = true ORDER BY dist';
|
|
|
|
$query = $this->em->createQuery($query_string)
|
|
->setParameter('lat', $point->getLatitude())
|
|
->setParameter('lng', $point->getLongitude());
|
|
|
|
// error_log($query->getSql());
|
|
$result = $query->getResult();
|
|
|
|
$hubs = [];
|
|
$hubs_data = [];
|
|
foreach ($result as $row)
|
|
{
|
|
$hubs[] = $row[0];
|
|
|
|
// get coordinates of hub
|
|
$hub_coordinates = $row[0]->getCoordinates();
|
|
|
|
$cust_lat = $point->getLatitude();
|
|
$cust_lng = $point->getLongitude();
|
|
|
|
$hub_lat = $hub_coordinates->getLatitude();
|
|
$hub_lng = $hub_coordinates->getLongitude();
|
|
|
|
// get distance in kilometers from customer point to hub point
|
|
$dist = $this->distance($cust_lat, $cust_lng, $hub_lat, $hub_lng);
|
|
|
|
if ($dist < $limit_distance)
|
|
{
|
|
$hubs_data[] = [
|
|
'hub' => $row[0],
|
|
'db_distance' => $row['dist'],
|
|
'distance' => $dist,
|
|
'duration' => 0,
|
|
'jo_count' => 0,
|
|
'inventory' => 0,
|
|
];
|
|
}
|
|
else
|
|
{
|
|
$this->hub_filter_logger->logFilteredHub($row[0], 'not_within_distance', $jo_id, $customer_id);
|
|
}
|
|
}
|
|
|
|
return $hubs_data;
|
|
}
|
|
|
|
// convert db distance to kilometers
|
|
protected function distance($lat1, $lon1, $lat2, $lon2)
|
|
{
|
|
if (($lat1 == $lat2) && ($lon1 == $lon2))
|
|
return 0;
|
|
|
|
$theta = $lon1 - $lon2;
|
|
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
|
|
$dist = acos($dist);
|
|
$dist = rad2deg($dist);
|
|
$miles = $dist * 60 * 1.1515;
|
|
|
|
return round(($miles * 1.609344), 1);
|
|
}
|
|
|
|
protected function isExemptedByArea(string $filter_id, Point $coordinates): bool
|
|
{
|
|
$long = $coordinates->getLongitude();
|
|
$lat = $coordinates->getLatitude();
|
|
|
|
// get supported area given a set of coordinates
|
|
$query = $this->em->createQuery('SELECT s from App\Entity\SupportedArea s where st_contains(s.coverage_area, point(:long, :lat)) = true');
|
|
$area = $query->setParameter('long', $long)
|
|
->setParameter('lat', $lat)
|
|
->setMaxResults(1)
|
|
->getOneOrNullResult();
|
|
|
|
if ($area !== null) {
|
|
// get all exceptions
|
|
$exceptions = $area->getHubFilterExceptions();
|
|
|
|
if (isset($exceptions[$filter_id])) {
|
|
error_log("FILTER " . $filter_id . " DISABLED FOR AREA: " . $area->getName());
|
|
|
|
// disable this filter for this area
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// filter is in place
|
|
return false;
|
|
}
|
|
}
|
|
|