em = $em; $this->im = $im; $this->hub_distributor = $hub_distributor; $this->hub_filter_logger = $hub_filter_logger; $this->trans = $trans; $this->rt = $rt; } 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(); $items = $criteria->getItems(); $date_time = $criteria->getDateTime(); $payment_method = $criteria->getPaymentMethod(); $flag_emergency = $criteria->isEmergency(); $flag_round_robin = $criteria->isRoundRobin(); $results = []; // get all the hubs within distance $filtered_hubs = $this->getClosestHubs($point, $limit_distance); error_log('closest hubs ' . json_encode($filtered_hubs)); if (!$flag_emergency) { // filter the first hub results for date and opening times $hubs_date_time = $this->filterHubsByDateAndTime($filtered_hubs, $date_time); $filtered_hubs = $hubs_date_time; //error_log('date_time hubs ' . json_encode($filtered_hubs)); // filter jo types $hubs_jo_type = $this->filterHubsByJoType($filtered_hubs, $jo_type); $filtered_hubs = $hubs_jo_type; //error_log('jo_type hubs ' . json_encode($filtered_hubs)); // filter hubs by payment methods $hubs_payment_method = $this->filterHubsByPaymentMethod($filtered_hubs, $payment_method); $filtered_hubs = $hubs_payment_method; //error_log('payment hubs ' . json_encode($filtered_hubs)); // inventory filter $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items); $filtered_hubs = $hubs_inventory; //error_log('inventory hubs ' . json_encode($filtered_hubs)); // round robin filter $hubs_round_robin = $this->filterHubsByRoundRobin($filtered_hubs, $flag_round_robin); $filtered_hubs = $hubs_round_robin; //error_log('round robin hubs ' . json_encode($filtered_hubs)); // max results filter $hubs_max_result = $this->filterHubsByMaxResults($filtered_hubs, $limit_results); $filtered_hubs = $hubs_max_result; } $results = $filtered_hubs; error_log(json_encode($results)); return $results; } protected function filterHubsByRoundRobin($hubs, $flag_round_robin) { if (empty($hubs)) return $hubs; if (!$flag_round_robin) return $hubs; $results = []; // call hub distributor service $arranged_hubs = $this->hub_distributor->arrangeHubs($hubs); $results = $arranged_hubs; return $results; } protected function filterHubsByMaxResults($hubs, $limit_result) { if (empty($hubs)) return $hubs; if (empty($limit_result)) return $hubs; $results = []; for ($i = 0; $i < count($hubs); $i++) { if ($i < $limit_result) $results[] = $hubs[$i]; else $this->hub_filter_logger->logFilteredHub($hubs[$i]['hub'], 'max_results'); } return $results; } protected function filterHubsByJoType($hubs, $jo_type) { if (empty($hubs)) return $hubs; if (empty($jo_type)) return $hubs; $results = []; foreach ($hubs as $hub_data) { $hub = $hub_data['hub']; // TODO: for now, have this return true $has_jo_type = true; // check if hub offers the jo_type // TODO: add service to hub if ($has_jo_type) $results[] = [ 'hub' => $hub, 'db_distance' => $hub_data['db_distance'], 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], ]; else $this->hub_filter_logger->logFilteredHub($hub, 'job_order_type'); } return $results; } protected function filterHubsByPaymentMethod($hubs, $payment_method) { if (empty($hubs)) return $hubs; if (empty($payment_method)) return $hubs; $results = []; foreach ($hubs as $hub_data) { $hub = $hub_data['hub']; // name of payment method is what is saved $payment_methods = $hub->getPaymentMethods(); if ($payment_methods != null) { $flag_found_pmethod = false; foreach ($payment_methods as $pmethod) { if ($pmethod == $payment_method) { $results[] = [ 'hub' => $hub, 'db_distance' => $hub_data['db_distance'], 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], ]; } $flag_found_pmethod = true; } if (!$flag_found_pmethod) $this->hub_filter_logger->logFilteredHub($hub, 'no_payment_method'); } else $this->hub_filter_logger->logFilteredHub($hub, 'no_payment_method'); } return $results; } protected function filterHubsByDateAndTime($hubs, $date_time) { if (empty($hubs)) return $hubs; if ($date_time == null) return $hubs; $results = []; foreach ($hubs as $hub_data) { // go through each hub's opening times to check if hub is open // for the specified time // get hub opening and closing times // TODO: maybe in the future, might also have to check if hub // 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"); $filter_time = $date_time->format("H:i:s"); 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'], ]; } else $this->hub_filter_logger->logFilteredHub($hub, 'date_and_time'); } return $results; } protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items) { if (empty($hubs)) return $hubs; if (!$flag_inventory_check) return $hubs; $results = []; if ($flag_inventory_check) { foreach ($hubs as $hub_data) { $hub = $hub_data['hub']; if ($jo_type == ServiceType::BATTERY_REPLACEMENT_NEW) { // call inventory $has_items = $this->checkInventory($items, $hub); if ($has_items) $results[] = [ 'hub' => $hub, 'db_distance' => $hub_data['db_distance'], 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], ]; else { // get the skus for the message $sku_text = ''; foreach ($items as $key => $value) { $sku_text .= ' ' . $key; } // send SMS to hub $message = str_replace('item_display', trim($sku_text), $this->trans->trans('no_inventory_message')); error_log($message); $this->sendSMSMessage($hub, $items); $this->hub_filter_logger->logFilteredHub($hub, 'no_inventory'); } } if ($jo_type == ServiceType::BATTERY_REPLACEMENT_WARRANTY) { // call inventory $has_items = $this->checkInventory($items, $hub); if ($has_items) $results[] = [ 'hub' => $hub, 'db_distance' => $hub_data['db_distance'], 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], ]; else { // get the skus for the message $sku_text = ''; foreach ($items as $key => $value) { $sku_text .= ' ' . $key; } // send SMS to hub $message = str_replace('item_display', trim($sku_text), $this->trans->trans('no_inventory_message')); error_log($message); $this->sendSMSMessage($hub, $items); $this->hub_filter_logger->logFilteredHub($hub, 'no_inventory'); } } } } return $results; } protected function getClosestHubs(Point $point, $limit_distance) { // 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, ]; } else { $this->hub_filter_logger->logFilteredHub($row[0], 'distance'); } } return $hubs_data; } protected function checkInventory($items, $hub) { // check if hub has all items $skus = []; $branch_codes[] = $hub->getBranchCode(); $result = false; foreach ($items as $key=> $value) { // add sap code of item/battery into array since // getBranchesInventory takes in an array of hubs/branches // and an array of skus // $items as format: $items[sku] = quantity $skus[] = $key; } // call InventoryManager's getBranchesInventory to check if hub has all items $branches_with_items = $this->im->getBranchesInventory($branch_codes, $skus); if (!empty($branches_with_items)) { // check if branch has enough quantity for item foreach ($branches_with_items as $branch) { // get quantity from call $qty_available = $branch['Quantity']; // get the quantity request $sku_requested = $branch['SapCode']; $qty_requested = $items[$sku_requested]; if ($qty_available >= $qty_requested) $result = true; } } // return true or false return $result; } // 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 sendSMSMessage($hub, $items) { // compose message // get the skus for the message $sku_text = ''; foreach ($items as $key => $value) { $sku_text .= ' ' . $key; } $message = str_replace('item_display', trim($sku_text), $this->trans->trans('no_inventory_message')); // get hub notification number $mobile_number = $hub->getNotifNumber(); if (!empty($mobile_number)) { // send SMS message error_log('sending sms to - ' . $mobile_number); $this->rt->sendSMS($mobile_number, 'MOTOLITE', $message); } } }