diff --git a/config/routes/customer_api.yaml b/config/routes/customer_api.yaml index 86aae804..49797968 100644 --- a/config/routes/customer_api.yaml +++ b/config/routes/customer_api.yaml @@ -65,11 +65,6 @@ cust_api_battery_list: controller: App\Controller\CustomerAppAPI\VehicleController::getCompatibleBatteries methods: [GET] -cust_api_jo_request: - path: /apiv2/job_order - controller: App\Controller\CustomerAppAPI\JobOrderController::requestJobOrder - methods: [POST] - cust_api_estimate: path: /apiv2/estimate controller: App\Controller\CustomerAppAPI\EstimateController::getEstimate diff --git a/config/services.yaml b/config/services.yaml index 6af4a5d3..9a09882c 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -332,6 +332,9 @@ services: $rt: "@App\\Service\\RisingTideGateway" $trans: "@Symfony\\Contracts\\Translation\\TranslatorInterface" + App\Service\HubFilter\Filters\DistanceHubFilter: + public: true + App\Service\HubFilter\Filters\DateAndTimeHubFilter: public: true diff --git a/src/Controller/CustomerAppAPI/JobOrderController.php b/src/Controller/CustomerAppAPI/JobOrderController.php index 64da86e5..bdee96d4 100644 --- a/src/Controller/CustomerAppAPI/JobOrderController.php +++ b/src/Controller/CustomerAppAPI/JobOrderController.php @@ -995,411 +995,6 @@ class JobOrderController extends ApiController ]); } - // TODO: remove later - // mobile app no longer calls this - public function requestJobOrder( - Request $req, - InvoiceGeneratorInterface $ic, - GeofenceTracker $geo, - MapTools $map_tools, - InventoryManager $im, - MQTTClientApiv2 $mclientv2, - FCMSender $fcmclient, - RiderAssignmentHandlerInterface $rah, - PromoLogger $promo_logger, - HubSelector $hub_select, - HubDistributor $hub_dist, - HubFilterLogger $hub_filter_logger, - HubFilteringGeoChecker $hub_geofence, - JobOrderManager $jo_manager, - PriceTierManager $pt_manager - ) { - // validate params - $validity = $this->validateRequest($req, [ - 'service_type', - 'cv_id', - // 'batt_id', - 'long', - 'lat', - 'warranty', - 'mode_of_payment', - ]); - - if (!$validity['is_valid']) { - return new ApiResponse(false, $validity['error']); - } - - // trade in type - $trade_in_batt = $req->request->get('trade_in_batt'); - $trade_in_type = $req->request->get('trade_in_type', ''); - - // 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 - return new ApiResponse(false, $this->getGeoErrorMessage()); - } - - $jo = new JobOrder(); - $jo->setSource(TransactionOrigin::MOBILE_APP) - ->setStatus(JOStatus::PENDING) - ->setDeliveryInstructions('') - ->setTier1Notes('') - ->setTier2Notes('') - ->setDeliveryAddress($address) - ->setTradeInType($trade_in_type) - ->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) { - return new ApiResponse(false, 'No customer information found.'); - } - - // check if customer has more than one job order already - $flag_cust_new = false; - - $cust_jo_count = $jo_manager->getCustomerJobOrderCount($cust->getID()); - if ($cust_jo_count <= 1) - $flag_cust_new = true; - - $jo->setCustomer($cust); - $jo->setCustNew($flag_cust_new); - - // validate service type - $stype = $req->request->get('service_type'); - if (!ServiceType::validate($stype)) { - return new ApiResponse(false, 'Invalid service type.'); - } - $jo->setServiceType($stype); - - // validate warranty - $warr = $req->request->get('warranty'); - if (!WarrantyClass::validate($warr)) { - return new ApiResponse(false, 'Invalid warranty class.'); - } - $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 = $this->em->getRepository(Promo::class)->find($promo_id); - if ($promo == null) { - return new ApiResponse(false, 'Invalid promo id.'); - } - - // put in criteria - $icrit->addPromo($promo); - } - - // check customer vehicle - $cv = $this->em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id')); - if ($cv == null) { - return new ApiResponse(false, 'Invalid customer vehicle id.'); - } - $icrit->setCustomerVehicle($cv); - $jo->setCustomerVehicle($cv); - - // check if customer owns vehicle - if ($cust->getID() != $cv->getCustomer()->getID()) { - return new ApiResponse(false, 'Customer does not own vehicle.'); - } - - // check battery - $batt_id = $req->request->get('batt_id'); - if ($batt_id != null) { - $batt = $this->em->getRepository(Battery::class)->find($batt_id); - if ($batt == null) { - return new ApiResponse(false, 'Invalid battery id.'); - } - } else - $batt = null; - - /* - // put battery in criteria - $icrit->addBattery($batt); - */ - - // check trade-in - // only allow motolite, other, none - switch ($trade_in_type) { - case TradeInType::MOTOLITE: - case TradeInType::OTHER: - break; - - default: - $trade_in_type = ''; - break; - } - - // add the actual battery item first - $icrit->addEntry($batt, null, 1); - - // if we have a trade in, add it as well - if (!empty($trade_in_type) && !empty($trade_in_batt)) { - $ti_batt_obj = $this->em->getRepository(Battery::class)->find($trade_in_batt); - if (!empty($ti_batt_obj)) { - $battery_size = $ti_batt_obj->getSize(); - $icrit->addTradeInEntry($battery_size, $trade_in_type, 1); - } - } - - // set taxable - $icrit->setIsTaxable(); - - // set JO source - $icrit->setSource(TransactionOrigin::MOBILE_APP); - - // set price tier - $pt_id = $pt_manager->getPriceTier($jo->getCoordinates()); - $icrit->setPriceTier($pt_id); - - // send to invoice generator - $invoice = $ic->generateInvoice($icrit); - $jo->setInvoice($invoice); - - // set more hub criteria fields - $hub_criteria = new HubCriteria(); - $hub_criteria->setPoint($jo->getCoordinates()); - - // get distance limit for mobile from env - $limit_distance = $_ENV['CUST_DISTANCE_LIMIT']; - - // set distance limit - $hub_criteria->setLimitDistance($limit_distance); - - if ($hub_geofence->isCovered($long, $lat)) { - // TODO: set this properly, since the other flags - // are on default values. - // if true, set other values for HubCriteria - // error_log('Area is covered by hub filtering'); - $hub_criteria->setJoType($jo->getServiceType()) - ->setPaymentMethod($jo->getModeOfPayment()) - ->setRoundRobin(true); - } - - // add battery to items - $sku = $batt->getSAPCode(); - if (!empty($sku)) - $hub_criteria->addItem($batt->getSAPCode(), 1); - - // get customer id. No JO id at this point - $customer_id = $cust->getID(); - - $hub_criteria->setCustomerId($customer_id); - - // find nearest hubs - $nearest_hubs = $hub_select->find($hub_criteria); - - $assigned_rider = null; - if (!empty($nearest_hubs)) { - // go through the hub list, find the nearest hub - // with an available rider - //error_log('found nearest hub ' . $nearest_hub->getID()); - foreach ($nearest_hubs as $nearest_hub) { - $available_riders = $nearest_hub['hub']->getAvailableRiders(); - if (count($available_riders) >= 1) { - if (count($available_riders) == 1) { - $assigned_rider = $available_riders[0]; - } else { - // 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); - } - - $jo->setHub($nearest_hub['hub']); - $jo->setRider($assigned_rider); - $jo->setStatus(JOStatus::ASSIGNED); - $jo->setStatusAutoAssign(AutoAssignStatus::HUB_AND_RIDER_ASSIGNED); - - $assigned_rider->setAvailable(false); - - // set rider's current job order - $assigned_rider->setCurrentJobOrder($jo); - - // update redis hub_jo_count for hub - $hub_dist->incrementJoCountForHub($nearest_hub['hub']); - - // break out of loop - break; - } else { - // log hub into hub_filter_log - $hub_filter_logger->logFilteredHub($nearest_hub['hub'], 'no_available_rider', null, $cust->getID()); - // continue to go through list to find hub with an available rider - } - } - } - - $this->em->persist($jo); - $this->em->persist($invoice); - - // add event log for JO - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::CREATE) - ->setJobOrder($jo); - $this->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); - - $this->em->persist($hub_assign_event); - - $rider_assign_event = new JOEvent(); - $rider_assign_event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::RIDER_ASSIGN) - ->setJobOrder($jo); - - $this->em->persist($rider_assign_event); - - // user mqtt event - $payload = [ - 'event' => 'outlet_assign', - ]; - $mclientv2->sendEvent($jo, $payload); - $fcmclient->sendJoEvent($jo, "jo_fcm_title_outlet_assign", "jo_fcm_body_outlet_assign"); - - $rah->assignJobOrder($jo, $jo->getRider()); - } - - $this->em->flush(); - - // make invoice json data - $invoice_data = [ - 'total_price' => $invoice->getTotalPrice(), - 'vat_ex_price' => (float) $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; - - // check service type - if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) { - $customer = $cv->getCustomer(); - $customer_tags = $customer->getCustomerTagObjects(); - if (!empty($customer_tags)) { - foreach ($customer_tags as $customer_tag) { - // TODO: not too comfy with this being hardcoded - if ($customer_tag->getID() == $invoice->getUsedCustomerTagId()) { - // remove associated entity - $customer->removeCustomerTag($customer_tag); - - // log the availment of promo from customer - $created_by = $req->query->get('session_key'); - $cust_id = $jo->getCustomer()->getID(); - $cust_fname = $jo->getCustomer()->getFirstName(); - $cust_lname = $jo->getCustomer()->getLastName(); - $jo_id = $jo->getID(); - $invoice_id = $jo->getInvoice()->getID(); - // TODO: check if we store total price of invoice or just the discounted amount - $amount = $jo->getInvoice()->getTotalPrice(); - $promo_logger->logPromoInfo( - $created_by, - $cust_id, - $cust_fname, - $cust_lname, - $jo_id, - $invoice_id, - $amount - ); - } - } - } - } - - // response - return new ApiResponse(true, '', [ - 'jo_id' => $jo->getID(), - 'invoice' => $invoice_data, - ]); - } - - // commenting it out. Modify the getJOHistory instead to just get the fulfilled - // and cancelled job orders, since ongoing is not yet part of history - /* - public function getCompletedJobOrders(Request $req, EntityManagerInterface $em, RiderTracker $rt) - { - // 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.'); - } - - $completed_jos = $this->getCompletedJOs($cust); - - // initialize data - $jo_data = []; - foreach ($completed_jos as $jo) - { - $jo_data[] = $this->generateJobOrderData($req, $jo, $rt); - } - - // response - return new ApiResponse(true, '', [ - 'completed_job_orders' => $jo_data, - ]); - } - - protected function getCompletedJOs($cust) - { - $completed_jos = $this->em->getRepository(JobOrder::class)->findBy([ - 'customer' => $cust, - 'status' => [JOStatus::CANCELLED, JOStatus::FULFILLED], - ], ['date_schedule' => 'desc']); - - return $completed_jos; - } - */ - protected function generateJobOrderData($req, $jo, $rt) { $status = $jo->getStatus(); diff --git a/src/Service/HubFilter/BaseHubFilter.php b/src/Service/HubFilter/BaseHubFilter.php index 81f75b7d..dbc8c699 100644 --- a/src/Service/HubFilter/BaseHubFilter.php +++ b/src/Service/HubFilter/BaseHubFilter.php @@ -2,6 +2,7 @@ namespace App\Service\HubFilter; +use App\Entity\Customer; use Doctrine\ORM\EntityManagerInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -12,18 +13,19 @@ use App\Entity\JORejection; use App\Ramcar\ServiceType; use App\Ramcar\JORejectionReason; use App\Service\RisingTideGateway; +use App\Ramcar\HubCriteria; use DateTime; class BaseHubFilter { protected $id; - protected $jo_id; - protected $customer_id; + protected $cust; protected $hub_filter_logger; protected $em; protected $rt; protected $trans; + protected $crit; public function __construct(HubFilterLogger $hub_filter_logger, EntityManagerInterface $em, RisingTideGateway $rt, TranslatorInterface $trans) { @@ -37,36 +39,22 @@ class BaseHubFilter error_log("-------------------"); } + public function initialize(HubCriteria $crit, Customer $cust): void + { + $this->crit = $crit; + $this->cust = $cust; + } + public function getID(): string { return $this->id; } - public function setJOID(int $jo_id) - { - $this->jo_id = $jo_id; - return $this; - } - - public function getJOID(): int - { - return $this->jo_id; - } - - public function setCustomerID(int $customer_id) - { - $this->customer_id = $customer_id; - return $this; - } - - public function getCustomerID(): int - { - return $this->customer_id; - } - public function log(Hub $hub): void { - $this->hub_filter_logger->logFilteredHub($hub, $this->getID(), $this->getJOID(), $this->getCustomerID()); + $jo_id = $this->crit->getJobOrderId(); + + $this->hub_filter_logger->logFilteredHub($hub, $this->getID(), $jo_id, $this->cust->getID()); // log to file $filename = '/../../../var/log/hub_rejection.log'; @@ -74,7 +62,7 @@ class BaseHubFilter // build log entry $entry = implode("", [ - "[JO: " . $this->getJOID() . "]", + "[JO: " . $jo_id . "]", "[" . $date . "]", "[" . $this->getID() . "]", " " . $hub->getName() . " (ID: " . $hub->getID() . ")", @@ -84,9 +72,41 @@ class BaseHubFilter @file_put_contents(__DIR__ . $filename, $entry, FILE_APPEND); } + public function isExemptedByArea(): bool + { + $point = $this->crit->getPoint(); + $long = $point->getLongitude(); + $lat = $point->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[$this->id])) { + // disable this filter for this area + return true; + } + } + + // filter is in place + return false; + } + protected function createRejectionEntry($hub, $reason, $remarks = ""): JORejection { - $jo = $this->em->getRepository(JobOrder::class)->find($this->getJOID()); + $jo_id = $this->crit->getJobOrderId(); + $jo = null; + + if (!empty($jo_id)) { + $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); + } $robj = new JORejection(); $robj->setDateCreate(new DateTime()) @@ -103,7 +123,7 @@ class BaseHubFilter protected function sendSMSMessage($hub, $order_date, $service_type, $rejection, $reason = "", $remarks = ""): void { - $jo_id = $this->getJOID(); + $jo_id = $this->crit->getJobOrderId(); // check if we already have a rejection record for this hub and JO. this also means an SMS was already sent $rejection_count = $this->em->createQueryBuilder() diff --git a/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php b/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php index 67097320..976b7496 100644 --- a/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php +++ b/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php @@ -4,21 +4,22 @@ namespace App\Service\HubFilter\Filters; use App\Service\HubFilter\BaseHubFilter; use App\Service\HubFilter\HubFilterInterface; +use App\Ramcar\HubCriteria; class DateAndTimeHubFilter extends BaseHubFilter implements HubFilterInterface { protected $id = 'date_and_time'; - public function getRequestedParams() : array + public function isApplicable() : bool { - return [ - 'date_time', - ]; + return true; } - public function filter(array $hubs, array $params = []) : array + public function filter(array $hubs) : array { - if ($params['date_time'] == null) + $filter_time = $this->crit->getDateTime(); + + if ($filter_time == null) return $hubs; $results = []; @@ -32,22 +33,27 @@ class DateAndTimeHubFilter extends BaseHubFilter implements HubFilterInterface // is open/available on date/day $hub = $hub_data['hub']; - $time_open = $hub->getTimeOpen()->format("H:i:s"); - $time_close = $hub->getTimeClose()->format("H:i:s"); + $time_open = $hub->getTimeOpen(); + $time_close = $hub->getTimeClose(); - $filter_time = $params['date_time']->format("H:i:s"); + // normalize dates to be the same as filter time + $time_open->setDate( + $filter_time->format('Y'), + $filter_time->format('m'), + $filter_time->format('d') + ); + $time_close->setDate( + $filter_time->format('Y'), + $filter_time->format('m'), + $filter_time->format('d') + ); + + // check filter time falls within operating hours if (($filter_time >= $time_open) && ($filter_time <= $time_close)) { - $results[] = [ - 'hub' => $hub, - 'db_distance' => $hub_data['db_distance'], - 'distance' => $hub_data['distance'], - 'duration' => $hub_data['duration'], - 'jo_count' => 0, - 'inventory' => $hub_data['inventory'], - ]; + $results[] = $hub_data; } else $this->log($hub); diff --git a/src/Service/HubFilter/Filters/DistanceHubFilter.php b/src/Service/HubFilter/Filters/DistanceHubFilter.php new file mode 100644 index 00000000..cadd8186 --- /dev/null +++ b/src/Service/HubFilter/Filters/DistanceHubFilter.php @@ -0,0 +1,59 @@ +crit->isEmergency(); + } + + public function filter(array $hubs) : array + { + $results = []; + foreach ($hubs as $hub_data) + { + $hub = $hub_data['hub']; + $dist = $hub_data['distance']; + + // check if within distance threshold + if ($dist < $this->crit->getLimitDistance()) { + // log to legacy file + $this->logHubWithinDistance($hub, $dist); + + // add to results + $results[] = $hub_data; + } else { + $this->log($hub); + } + } + + return $results; + } + + // NOTE: pulled from old hub selector class to maintain the log file + protected function logHubWithinDistance(Hub $hub, float $distance): void + { + // log to file + $filename = '/../../../../var/log/closest_hubs_selected.log'; + $date = date("Y-m-d H:i:s"); + + // build log entry + $entry = implode("", [ + "[JO: " . $this->crit->getJobOrderId() . "]", + "[" . $date . "]", + "[Distance: " . $distance . " vs " . $this->crit->getLimitDistance() . "]", + " " . $hub->getName() . " (ID: " . $hub->getID() . ")", + "\r\n", + ]); + + @file_put_contents(__DIR__ . $filename, $entry, FILE_APPEND); + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/Filters/InventoryHubFilter.php b/src/Service/HubFilter/Filters/InventoryHubFilter.php index d7526d0f..ee628a92 100644 --- a/src/Service/HubFilter/Filters/InventoryHubFilter.php +++ b/src/Service/HubFilter/Filters/InventoryHubFilter.php @@ -28,45 +28,40 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface $this->im = $im; } - public function getRequestedParams() : array + public function isApplicable() : bool { - return [ - 'flag_inventory_check', - 'customer_class', - 'jo_type', - 'jo_origin', - 'order_date', - 'service_type', - 'items', - ]; + return true; } - public function filter(array $hubs, array $params = []) : array + public function filter(array $hubs) : array { + $jo_id = $this->crit->getJobOrderId(); + $jo_type = $this->crit->getJoType(); + // check if this is enabled - if (!$params['flag_inventory_check']) { - error_log("INVENTORY CHECK " . $this->getJOID() . ": DISABLED"); + if (!$this->crit->hasInventoryCheck()) { + error_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); return $hubs; } // check customer class - if ((!empty($params['customer_class']) && $params['customer_class'] == CustomerClassification::VIP) || - $params['jo_origin'] === TransactionOrigin::VIP) { - error_log("INVENTORY CHECK " . $this->getJOID() . ": VIP CLASS"); + if ((!empty($params['customer_class']) && $this->crit->getCustomerClass() == CustomerClassification::VIP) || + $this->crit->getJoOrigin() === TransactionOrigin::VIP) { + error_log("INVENTORY CHECK " . $jo_id . ": VIP CLASS"); return $hubs; } // check item list is not empty if (empty($params['items'])) { - error_log("INVENTORY CHECK " . $this->getJOID() . ": NO ITEMS"); + error_log("INVENTORY CHECK " . $jo_id . ": NO ITEMS"); return $hubs; } // check this is a battery item related JO - if ($params['jo_type'] != ServiceType::BATTERY_REPLACEMENT_NEW && - $params['jo_type'] != ServiceType::BATTERY_REPLACEMENT_WARRANTY + if ($jo_type != ServiceType::BATTERY_REPLACEMENT_NEW && + $jo_type != ServiceType::BATTERY_REPLACEMENT_WARRANTY ) { - error_log("INVENTORY CHECK " . $this->getJOID() . ": INVALID SERVICE TYPE: " . $params['jo_type']); + error_log("INVENTORY CHECK " . $jo_id . ": INVALID SERVICE TYPE: " . $jo_type); return $hubs; } @@ -84,7 +79,8 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface $qtys = []; // call inventory manager for all hubs for selected SKUs - $skus = array_keys($params['items']); + $items = $this->crit->getItems(); + $skus = array_keys($items); error_log("CHECKING INVENTORY FOR " . count($skus) . " ITEM(S) ON HUBS " . count($branch_codes) . "..."); @@ -96,7 +92,7 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface foreach ($branches as $branch) { if (isset($branch['BranchCode'])) { // filter out branch if it does not have sufficient inventory - if (!isset($params['items'][$branch['SapCode']]) || $branch['Quantity'] < $params['items'][$branch['SapCode']] && + if (!isset($params['items'][$branch['SapCode']]) || $branch['Quantity'] < $items[$branch['SapCode']] && !isset($hubs_to_filter[$branch['BranchCode']]) ) { error_log("FILTERING BRANCH WITH NO INVENTORY: " . $branch['BranchCode']); @@ -124,8 +120,6 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface // check if we are filtering this hub if (isset($hubs_to_filter[$branch_code]) || empty($branch_code) || !isset($qtys[$branch_code])) { // if we have a JO, create rejection record and notify - $jo_id = $this->getJOID(); - if (!empty($jo_id)) { // create rejection report entry $robj = $this->createRejectionEntry( @@ -137,8 +131,8 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface // build SMS message $this->sendSMSMessage( $hub, - $params['order_date'], - $params['service_type'], + $this->crit->getOrderDate(), + $this->crit->getServiceType(), $robj, JORejectionReason::getName(JORejectionReason::NO_STOCK_SALES), "Requested SKU(s) - " . $battery_string diff --git a/src/Service/HubFilter/Filters/JoTypeHubFilter.php b/src/Service/HubFilter/Filters/JoTypeHubFilter.php index 01ed733f..6469ee80 100644 --- a/src/Service/HubFilter/Filters/JoTypeHubFilter.php +++ b/src/Service/HubFilter/Filters/JoTypeHubFilter.php @@ -9,20 +9,17 @@ class JoTypeHubFilter extends BaseHubFilter implements HubFilterInterface { protected $id = 'job_order_type'; - public function getRequestedParams() : array + public function isApplicable() : bool { - return [ - 'flag_emergency', - 'jo_type', - ]; + return true; } - public function filter(array $hubs, array $params = []) : array + public function filter(array $hubs) : array { - if ($params['flag_emergency']) + if ($this->crit->isEmergency()) return $hubs; - if (empty($params['jo_type'])) + if (empty($this->crit->getJoType())) return $hubs; $results = []; diff --git a/src/Service/HubFilter/Filters/MaxResultsHubFilter.php b/src/Service/HubFilter/Filters/MaxResultsHubFilter.php index 16c1b514..eb7cfd7f 100644 --- a/src/Service/HubFilter/Filters/MaxResultsHubFilter.php +++ b/src/Service/HubFilter/Filters/MaxResultsHubFilter.php @@ -9,22 +9,22 @@ class MaxResultsHubFilter extends BaseHubFilter implements HubFilterInterface { protected $id = 'max_results'; - public function getRequestedParams() : array + public function isApplicable() : bool { - return [ - 'limit_results', - ]; + return true; } - public function filter(array $hubs, array $params = []) : array + public function filter(array $hubs) : array { - if (empty($params['limit_results'])) + $limit_results = $this->crit->getLimitResults(); + + if (empty($limit_results)) return $hubs; $results = []; for ($i = 0; $i < count($hubs); $i++) { - if ($i < $params['limit_results']) + if ($i < $limit_results) $results[] = $hubs[$i]; else $this->log($hubs[$i]['hub']); diff --git a/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php b/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php index 8af6705b..2f3e33b6 100644 --- a/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php +++ b/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php @@ -9,20 +9,17 @@ class PaymentMethodHubFilter extends BaseHubFilter implements HubFilterInterface { protected $id = 'no_payment_method'; - public function getRequestedParams() : array + public function isApplicable() : bool { - return [ - 'flag_emergency', - 'payment_method', - ]; + return true; } - public function filter(array $hubs, array $params = []) : array + public function filter(array $hubs) : array { - if ($params['flag_emergency']) + if ($this->crit->isEmergency()) return $hubs; - if (empty($params['payment_method'])) + if (empty($this->crit->getPaymentMethod())) return $hubs; $results = []; @@ -37,7 +34,7 @@ class PaymentMethodHubFilter extends BaseHubFilter implements HubFilterInterface $flag_found_pmethod = false; foreach ($payment_methods as $pmethod) { - if ($pmethod == $params['payment_method']) + if ($pmethod == $this->crit->getPaymentMethod()) { $results[] = [ 'hub' => $hub, diff --git a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php index d2697feb..b5f61b5b 100644 --- a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php +++ b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php @@ -12,26 +12,24 @@ class RiderAvailabilityHubFilter extends BaseHubFilter implements HubFilterInter { protected $id = 'no_available_rider'; - public function getRequestedParams() : array + public function isApplicable() : bool { - return [ - 'flag_riders_check', - 'customer_class', - 'order_date', - 'service_type', - ]; + return true; } - public function filter(array $hubs, array $params = []) : array + public function filter(array $hubs) : array { + $cust_class = $this->crit->getCustomerClass(); + $jo_id = $this->crit->getJobOrderId(); + // check if this is enabled - if (!$params['flag_riders_check']) { + if (!$this->crit->hasRidersCheck()) { return $hubs; } // check customer class - if (!empty($params['customer_class']) && $params['customer_class'] == CustomerClassification::VIP) { - error_log("RIDER CHECK " . $this->getJOID() . ": VIP CLASS"); + if (!empty($cust_class) && $cust_class == CustomerClassification::VIP) { + error_log("RIDER CHECK " . $jo_id . ": VIP CLASS"); return $hubs; } @@ -45,8 +43,6 @@ class RiderAvailabilityHubFilter extends BaseHubFilter implements HubFilterInter error_log("TOTAL RIDERS: " . $available_riders); if ($available_riders === 0) { // if we have a JO, create rejection record and notify - $jo_id = $this->getJOID(); - if (!empty($jo_id)) { // create rejection report entry $robj = $this->createRejectionEntry($hub, JORejectionReason::NO_RIDER_AVAILABLE); @@ -54,8 +50,8 @@ class RiderAvailabilityHubFilter extends BaseHubFilter implements HubFilterInter // build SMS message $this->sendSMSMessage( $hub, - $params['order_date'], - $params['service_type'], + $this->crit->getOrderDate(), + $this->crit->getServiceType(), $robj, JORejectionReason::getName(JORejectionReason::NO_RIDER_AVAILABLE), ); diff --git a/src/Service/HubFilter/Filters/RoundRobinHubFilter.php b/src/Service/HubFilter/Filters/RoundRobinHubFilter.php index 235293a9..deeab226 100644 --- a/src/Service/HubFilter/Filters/RoundRobinHubFilter.php +++ b/src/Service/HubFilter/Filters/RoundRobinHubFilter.php @@ -23,16 +23,14 @@ class RoundRobinHubFilter extends BaseHubFilter implements HubFilterInterface $this->hub_distributor = $hub_distributor; } - public function getRequestedParams() : array + public function isApplicable() : bool { - return [ - 'flag_round_robin', - ]; + return true; } - public function filter(array $hubs, array $params = []) : array + public function filter(array $hubs) : array { - if (!$params['flag_round_robin']) + if (!$this->crit->isRoundRobin()) return $hubs; $results = []; diff --git a/src/Service/HubFilter/HubFilterInterface.php b/src/Service/HubFilter/HubFilterInterface.php index f3b9d2ab..9e592757 100644 --- a/src/Service/HubFilter/HubFilterInterface.php +++ b/src/Service/HubFilter/HubFilterInterface.php @@ -2,19 +2,16 @@ namespace App\Service\HubFilter; +use App\Entity\Customer; +use App\Ramcar\HubCriteria; + interface HubFilterInterface { + public function initialize(HubCriteria $crit, Customer $cust); + public function getID() : string; - public function filter(array $hubs, array $params = []) : array; + public function isApplicable() : bool; - public function setJOID(int $jo_id); - - public function getJOID() : int; - - public function setCustomerID(int $customer_id); - - public function getCustomerID() : int; - - public function getRequestedParams() : array; + public function filter(array $hubs) : array; } \ No newline at end of file diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 6207248d..66fce2e9 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -2,6 +2,7 @@ namespace App\Service; +use App\Entity\Customer; use Doctrine\ORM\EntityManagerInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -26,9 +27,7 @@ class HubSelector protected $trans; protected $rt; - public function __construct(ContainerInterface $container, EntityManagerInterface $em, InventoryManager $im, - HubDistributor $hub_distributor, HubFilterLogger $hub_filter_logger, - TranslatorInterface $trans, RisingTideGateway $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; @@ -53,82 +52,37 @@ class HubSelector 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(); - $jo_origin = $criteria->getJoOrigin(); - $customer_id = $criteria->getCustomerId(); - $customer_class = $criteria->getCustomerClass(); + // get all the hubs + $hubs = $this->getHubList($criteria->getPoint()); - // 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, - 'jo_origin' => $jo_origin, - '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, - ]; + // get customer record + $cust = $this->em->getRepository(Customer::class)->find($criteria->getCustomerId()); // loop through all enabled filters foreach ($this->getActiveFilters() as $hub_filter) { // no hubs left to filter - if (empty($filtered_hubs)) { + if (empty($hubs)) { break; } $f = $this->container->get($hub_filter); + $f->initialize($criteria, $cust); // check if supported area is exempted from this filter - if ($this->isExemptedByArea($f->getID(), $point)) { + if ($f->isExemptedByArea()) { 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)); + $hubs = $f->filter($hubs, $criteria); } - // error_log('final hub list ' . json_encode($filtered_hubs)); + // error_log('final hub list ' . json_encode($hubs)); - return $filtered_hubs; + return $hubs; } - protected function getClosestHubs(Point $point, $limit_distance, $jo_id, $customer_id) + protected function getHubList(Point $point): array { // 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'; @@ -141,49 +95,34 @@ class HubSelector $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(); + + foreach ($result as $row) { + $hub = $row[0]; // get distance in kilometers from customer point to hub point - $dist = $this->distance($cust_lat, $cust_lng, $hub_lat, $hub_lng); + $dist = $this->getKmDistance($point, $hub->getCoordinates()); - if ($dist < $limit_distance) - { - $hubs_data[] = [ - 'hub' => $row[0], - 'db_distance' => $row['dist'], - 'distance' => $dist, - 'duration' => 0, - 'jo_count' => 0, - 'inventory' => 0, - ]; - - // log to file - $this->logClosestHubResult($jo_id, $row[0], $dist, $limit_distance); - } - else - { - $this->hub_filter_logger->logFilteredHub($row[0], 'not_within_distance', $jo_id, $customer_id); - } + $hubs[] = [ + 'hub' => $hub, + 'db_distance' => $row['dist'], + 'distance' => $dist, + 'duration' => 0, + 'jo_count' => 0, + 'inventory' => 0, + ]; } - return $hubs_data; + return $hubs; } - // convert db distance to kilometers - protected function distance($lat1, $lon1, $lat2, $lon2) + protected function getKmDistance(Point $point1, Point $point2): float { + // get coordinates in radians + $lat1 = $point1->getLatitude(); + $lon1 = $point1->getLongitude(); + $lat2 = $point2->getLatitude(); + $lon2 = $point2->getLongitude(); + if (($lat1 == $lat2) && ($lon1 == $lon2)) return 0; @@ -193,53 +132,7 @@ class HubSelector $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; - } - - protected function logClosestHubResult($jo_id, $hub, $distance, $limit_distance) - { - // log to file - $filename = '/../../var/log/closest_hubs_selected.log'; - $date = date("Y-m-d H:i:s"); - - // build log entry - $entry = implode("", [ - "[JO: " . $jo_id . "]", - "[" . $date . "]", - "[Distance: " . $distance . " vs " . $limit_distance . "]", - " " . $hub->getName() . " (ID: " . $hub->getID() . ")", - "\r\n", - ]); - - @file_put_contents(__DIR__ . $filename, $entry, FILE_APPEND); + return round($miles * 1.609344, 1); } }