From a45c3dd65cab9ee9df91ea617aaa7871e2442619 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 14 May 2024 03:39:45 +0800 Subject: [PATCH 01/49] Add hub selector filtering for available riders, refactor inventory filter to send a single batch call instead #800 --- src/Ramcar/HubCriteria.php | 13 ++++ src/Service/HubSelector.php | 111 +++++++++++++++++++++++++++++++--- translations/messages.en.yaml | 3 +- 3 files changed, 119 insertions(+), 8 deletions(-) diff --git a/src/Ramcar/HubCriteria.php b/src/Ramcar/HubCriteria.php index 5a663551..ffb11265 100644 --- a/src/Ramcar/HubCriteria.php +++ b/src/Ramcar/HubCriteria.php @@ -12,6 +12,7 @@ class HubCriteria protected $limit_results; // number of results to return protected $limit_distance; // distance limit for search in km protected $flag_inventory_check; // flag if we need to check for inventory + protected $flag_riders_check; // flag if we need to check for riders available protected $jo_type; // jo service needed protected $date_time; // date and time to check if hub is open or not protected $items; // array of items: items[sku] = quantity to check for @@ -29,6 +30,7 @@ class HubCriteria $this->jo_type = ''; $this->date_time = new DateTime(); $this->flag_inventory_check = false; + $this->flag_riders_check = false; $this->items = []; $this->payment_method = ''; $flag_emergency = false; @@ -81,6 +83,17 @@ class HubCriteria return $this->flag_inventory_check; } + public function setRidersCheck($flag_riders_check = true) + { + $this->flag_riders_check = $flag_riders_check; + return $this; + } + + public function hasRidersCheck() + { + return $this->flag_riders_check; + } + public function setJoType($jo_type) { // TODO: validate the jo type diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index ec6b65d0..3777ae6d 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -46,6 +46,7 @@ class HubSelector $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(); @@ -86,10 +87,13 @@ class HubSelector //error_log('payment hubs ' . json_encode($filtered_hubs)); // inventory filter - $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, - $jo_type, $items, $jo_id, $customer_id); + $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id); $filtered_hubs = $hubs_inventory; + // available riders filter + $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_type, $jo_id, $customer_id); + $filtered_hubs = $hubs_riders; + //error_log('inventory hubs ' . json_encode($filtered_hubs)); // round robin filter @@ -262,13 +266,70 @@ class HubSelector protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id) { - if (empty($hubs)) + // check if this is enabled + if (!$flag_inventory_check) { return $hubs; + } - if (!$flag_inventory_check) + // check hub list is not empty + if (empty($hubs)) { return $hubs; + } + + // check item list is not empty + if (empty($items)) { + return $hubs; + } + // check this is a battery item related JO + if ($jo_type != ServiceType::BATTERY_REPLACEMENT_NEW && + $jo_type != ServiceType::BATTERY_REPLACEMENT_WARRANTY + ) { + return $hubs; + } + + // get a list of all hubs with branch codes + $hub_array = $hubs->toArray(); + $branch_codes = array_filter(array_column($hub_array, 'branch_code')); + + $hubs_to_filter = []; $results = []; + + // call inventory manager for all hubs for selected SKUs + $skus = array_keys($items); + $branches = $this->im->getBranchesInventory($branch_codes, $skus); + + // check each result to see if sufficient quantity exists to meet request + foreach ($branches as $branch) { + // filter out branch if it does not have sufficient inventory + if ($branch['Quantity'] < $items[$branch['SapCode']] && + !isset($hubs_to_filter[$branch['BranchCode']]) + ) { + $hubs_to_filter[$branch['BranchCode']] = true; + } + } + + // remove filtered hubs from list + foreach ($hubs as $hub_data) { + $hub_obj = $hub_data['hub']; + + // check if we are filtering this hub + if (isset($hubs_to_filter[$hub_data['hub']->getBranchCode()])) { + // send SMS to hub + $this->sendSMSMessage($hub_obj, $items); + + // log this filter + $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_inventory', $jo_id, $customer_id); + } else { + $results[] = $hub_data; + } + } + + // return filtered hubs + return $results; + + // NOTE: leaving the old code here for posterity, for now + /* if ($flag_inventory_check) { foreach ($hubs as $hub_data) @@ -334,6 +395,43 @@ class HubSelector } } + return $results; + */ + } + + protected function filterHubsByRiderAvailability($hubs, $flag_riders_check, $jo_type, $jo_id, $customer_id) + { + // check if this is enabled + if (!$flag_riders_check) { + return $hubs; + } + + // check hub list is not empty + if (empty($hubs)) { + return $hubs; + } + + $results = []; + + foreach ($hubs as $hub_data) { + $hub_obj = $hub_data['hub']; + + // check we have available riders + if ($hub_obj->getAvailableRiders() === 0) { + // send SMS to hub + $this->rt->sendSMS( + $hub_obj->getNotifNumber(), + $this->trans->trans('message.battery_brand_allcaps'), + $this->trans->trans('message.no_riders_message') + ); + + // log this filter + $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_available_rider', $jo_id, $customer_id); + } else { + $results[] = $hub_data; + } + } + return $results; } @@ -386,14 +484,13 @@ class HubSelector return $hubs_data; } - protected function checkInventory($items, $hub) + protected function checkInventory($items, $branch_codes) { // check if hub has all items $skus = []; - $branch_codes[] = $hub->getBranchCode(); $result = false; - foreach ($items as $key=> $value) + foreach ($items as $key => $value) { // add sap code of item/battery into array since // getBranchesInventory takes in an array of hubs/branches diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 8387663e..b0d3a1b1 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -13,7 +13,8 @@ add_cust_vehicle_battery_info: This vehicle is using a Motolite battery jo_title_pdf: Motolite Res-Q Job Order country_code_prefix: '+63' delivery_instructions_label: Delivery Instructions -no_inventory_message: No stock for [item_display] +no_inventory_message: 'A RESQ Job Order was created but there is no stock for the following SKU(s) on this branch: [item_display]' +no_riders_message: A RESQ Job Order was created but there are no riders available for this branch. # images image_logo_login: /assets/images/logo-resq.png From d52402a2ef8d4942d5ae110b39485863bdc8ba38 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 14 May 2024 04:18:37 +0800 Subject: [PATCH 02/49] Enable inventory and rider checks on hub criteria on user app and TAPI #800 --- src/Controller/CustomerAppAPI/JobOrderController.php | 4 ++++ src/Controller/TAPI/JobOrderController.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Controller/CustomerAppAPI/JobOrderController.php b/src/Controller/CustomerAppAPI/JobOrderController.php index fa5a9a85..fd5926e5 100644 --- a/src/Controller/CustomerAppAPI/JobOrderController.php +++ b/src/Controller/CustomerAppAPI/JobOrderController.php @@ -752,6 +752,10 @@ class JobOrderController extends ApiController $hub_criteria->setCustomerId($customer_id); + // set filter flags for inventory and available riders + $hub_criteria->setInventoryCheck(); + $hub_criteria->setRidersCheck(); + // find nearest hubs $nearest_hubs = $hub_select->find($hub_criteria); diff --git a/src/Controller/TAPI/JobOrderController.php b/src/Controller/TAPI/JobOrderController.php index ae4d5a61..cedf0320 100644 --- a/src/Controller/TAPI/JobOrderController.php +++ b/src/Controller/TAPI/JobOrderController.php @@ -239,6 +239,10 @@ class JobOrderController extends ApiController // find nearest hubs $nearest_hubs = $hub_select->find($hub_criteria); + // set filter flags for inventory and available riders + $hub_criteria->setInventoryCheck(); + $hub_criteria->setRidersCheck(); + if (!empty($nearest_hubs)) { // go through the hub list, find the nearest hub From be0e69db89d31feae3b11130a5d98759e413dc63 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 14 May 2024 14:45:51 +0800 Subject: [PATCH 03/49] Add blacklist support to base NameValue class, for hiding options when getting entire collection #800 --- src/Ramcar/JORejectionReason.php | 4 ++++ src/Ramcar/NameValue.php | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Ramcar/JORejectionReason.php b/src/Ramcar/JORejectionReason.php index 9dee1217..cedaf60a 100644 --- a/src/Ramcar/JORejectionReason.php +++ b/src/Ramcar/JORejectionReason.php @@ -29,4 +29,8 @@ class JORejectionReason extends NameValue 'no_credit_card' => 'NO CREDIT CARD PAYMENT / NO TERMINAL', 'discount' => 'DISCOUNT', ]; + + const BLACKLIST = [ + self::ADMINISTRATIVE => true, + ]; } diff --git a/src/Ramcar/NameValue.php b/src/Ramcar/NameValue.php index 58b6d75e..c368b7c4 100644 --- a/src/Ramcar/NameValue.php +++ b/src/Ramcar/NameValue.php @@ -4,9 +4,21 @@ namespace App\Ramcar; class NameValue { + const BLACKLIST = []; + static public function getCollection() { - return static::COLLECTION; + $result = []; + $blacklist = static::getBlacklist(); + + // filter from blacklist + foreach(static::COLLECTION as $key => $row) { + if (!isset($blacklist[$key])) { + $result[] = $row; + } + } + + return $result; } static public function validate($value) @@ -24,4 +36,9 @@ class NameValue return 'Unknown'; } + + static public function getBlacklist() + { + return static::BLACKLIST ?? []; + } } From 0bd6a898400c1bd0945d44ac7638a322be2c035b Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 14 May 2024 17:49:14 +0800 Subject: [PATCH 04/49] Fix missing keys on NameValue class #800 --- src/Ramcar/NameValue.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ramcar/NameValue.php b/src/Ramcar/NameValue.php index c368b7c4..99366a8d 100644 --- a/src/Ramcar/NameValue.php +++ b/src/Ramcar/NameValue.php @@ -14,7 +14,7 @@ class NameValue // filter from blacklist foreach(static::COLLECTION as $key => $row) { if (!isset($blacklist[$key])) { - $result[] = $row; + $result[$key] = $row; } } From 89e5dd799f61626d8d1272e26f98bfc01f986c6a Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 14 May 2024 17:49:34 +0800 Subject: [PATCH 05/49] Update hub rejection SMS translations #800 --- translations/messages.en.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index b0d3a1b1..43013469 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -13,8 +13,8 @@ add_cust_vehicle_battery_info: This vehicle is using a Motolite battery jo_title_pdf: Motolite Res-Q Job Order country_code_prefix: '+63' delivery_instructions_label: Delivery Instructions -no_inventory_message: 'A RESQ Job Order was created but there is no stock for the following SKU(s) on this branch: [item_display]' -no_riders_message: A RESQ Job Order was created but there are no riders available for this branch. +no_inventory_message: 'A Job Order was created but there is no stock for the following SKU(s) on this branch: [item_display]' +no_riders_message: A Job Order was created but there are no riders available for this branch. # images image_logo_login: /assets/images/logo-resq.png From 76496ed6fecc8326529058f22fee6c00389b30c0 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 14 May 2024 17:51:00 +0800 Subject: [PATCH 06/49] Fix issues with inventory filter flow #800 --- src/Service/HubSelector.php | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 3777ae6d..f6840cde 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -86,14 +86,14 @@ class HubSelector //error_log('payment hubs ' . json_encode($filtered_hubs)); - // inventory filter - $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id); - $filtered_hubs = $hubs_inventory; - // available riders filter $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_type, $jo_id, $customer_id); $filtered_hubs = $hubs_riders; + // inventory filter + $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id); + $filtered_hubs = $hubs_inventory; + //error_log('inventory hubs ' . json_encode($filtered_hubs)); // round robin filter @@ -266,18 +266,23 @@ class HubSelector protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id) { + error_log("IN INVENTORY CHECK NOW"); + // check if this is enabled if (!$flag_inventory_check) { + error_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); return $hubs; } // check hub list is not empty if (empty($hubs)) { + error_log("INVENTORY CHECK " . $jo_id . ": NO HUBS"); return $hubs; } // check item list is not empty if (empty($items)) { + error_log("INVENTORY CHECK " . $jo_id . ": NO ITEMS"); return $hubs; } @@ -285,22 +290,32 @@ class HubSelector if ($jo_type != ServiceType::BATTERY_REPLACEMENT_NEW && $jo_type != ServiceType::BATTERY_REPLACEMENT_WARRANTY ) { + error_log("INVENTORY CHECK " . $jo_id . ": INVALID SERVICE TYPE"); return $hubs; } // get a list of all hubs with branch codes - $hub_array = $hubs->toArray(); - $branch_codes = array_filter(array_column($hub_array, 'branch_code')); + $branch_codes = []; + foreach ($hubs as $hub_data) { + $branch_codes[] = $hub_data['hub']->getBranchCode(); + }; $hubs_to_filter = []; $results = []; // call inventory manager for all hubs for selected SKUs $skus = array_keys($items); + + error_log("CHECKING INVENTORY FOR " . count($skus) . " ITEM(S) ON HUBS " . count($branch_codes) . "..."); + $branches = $this->im->getBranchesInventory($branch_codes, $skus); + + error_log("REQUEST COMPLETE, RESULT COUNT: " . count($branches)); + error_log(print_r($branches, true)); // check each result to see if sufficient quantity exists to meet request foreach ($branches as $branch) { + error_log("CHECKING BRANCH " . print_r($branch, true)); // filter out branch if it does not have sufficient inventory if ($branch['Quantity'] < $items[$branch['SapCode']] && !isset($hubs_to_filter[$branch['BranchCode']]) @@ -309,6 +324,8 @@ class HubSelector } } + error_log("COMPLETED BRANCH CHECKS"); + // remove filtered hubs from list foreach ($hubs as $hub_data) { $hub_obj = $hub_data['hub']; @@ -325,6 +342,8 @@ class HubSelector } } + error_log("COMPLETED HUB FILTERING, TOTAL RESULTS: " . count($results)); + // return filtered hubs return $results; @@ -427,6 +446,8 @@ class HubSelector // log this filter $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_available_rider', $jo_id, $customer_id); + + error_log("FILTERED HUB " . $hub_obj->getID() . " (no_available_rider)"); } else { $results[] = $hub_data; } From aed31f2a33785d24763ef561b84644ece8b14728 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 15 May 2024 17:59:27 +0800 Subject: [PATCH 07/49] Rearrange flow logic on hub selector, save inventory counts upon retrieval if enabled #800 --- src/Service/HubDistributor.php | 2 ++ src/Service/HubSelector.php | 63 ++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/Service/HubDistributor.php b/src/Service/HubDistributor.php index 27a70f15..b1d20ad6 100644 --- a/src/Service/HubDistributor.php +++ b/src/Service/HubDistributor.php @@ -81,6 +81,7 @@ class HubDistributor 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], 'jo_count' => $hub_jo_count, + 'inventory' => $hub_data['inventory'] ?? 0, ]; } else @@ -91,6 +92,7 @@ class HubDistributor 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], 'jo_count' => 0, + 'inventory' => $hub_data['inventory'] ?? 0, ]; } } diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index f6840cde..eeeba2db 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -86,6 +86,10 @@ class HubSelector //error_log('payment hubs ' . json_encode($filtered_hubs)); + //error_log('inventory hubs ' . json_encode($filtered_hubs)); + + // error_log('round robin hubs ' . json_encode($filtered_hubs)); + // available riders filter $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_type, $jo_id, $customer_id); $filtered_hubs = $hubs_riders; @@ -94,14 +98,10 @@ class HubSelector $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id); $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, $jo_id, $customer_id); $filtered_hubs = $hubs_max_result; @@ -172,6 +172,7 @@ class HubSelector 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], 'jo_count' => 0, + 'inventory' => $hub_data['inventory'], ]; else $this->hub_filter_logger->logFilteredHub($hub, 'job_order_type', $jo_id, $customer_id); @@ -207,6 +208,7 @@ class HubSelector 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], 'jo_count' => 0, + 'inventory' => $hub_data['inventory'], ]; } $flag_found_pmethod = true; @@ -255,6 +257,7 @@ class HubSelector 'distance' => $hub_data['distance'], 'duration' => $hub_data['duration'], 'jo_count' => 0, + 'inventory' => $hub_data['inventory'], ]; } else @@ -266,23 +269,21 @@ class HubSelector protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id) { - error_log("IN INVENTORY CHECK NOW"); - // check if this is enabled if (!$flag_inventory_check) { - error_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); + //rror_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); return $hubs; } // check hub list is not empty if (empty($hubs)) { - error_log("INVENTORY CHECK " . $jo_id . ": NO HUBS"); + //error_log("INVENTORY CHECK " . $jo_id . ": NO HUBS"); return $hubs; } // check item list is not empty if (empty($items)) { - error_log("INVENTORY CHECK " . $jo_id . ": NO ITEMS"); + //error_log("INVENTORY CHECK " . $jo_id . ": NO ITEMS"); return $hubs; } @@ -290,60 +291,69 @@ class HubSelector if ($jo_type != ServiceType::BATTERY_REPLACEMENT_NEW && $jo_type != ServiceType::BATTERY_REPLACEMENT_WARRANTY ) { - error_log("INVENTORY CHECK " . $jo_id . ": INVALID SERVICE TYPE"); + //error_log("INVENTORY CHECK " . $jo_id . ": INVALID SERVICE TYPE"); return $hubs; } // get a list of all hubs with branch codes $branch_codes = []; foreach ($hubs as $hub_data) { - $branch_codes[] = $hub_data['hub']->getBranchCode(); + $branch_code = $hub_data['hub']->getBranchCode(); + if (!empty($branch_code)) { + $branch_codes[] = $branch_code; + } }; $hubs_to_filter = []; $results = []; + $qtys = []; // call inventory manager for all hubs for selected SKUs $skus = array_keys($items); - error_log("CHECKING INVENTORY FOR " . count($skus) . " ITEM(S) ON HUBS " . count($branch_codes) . "..."); + //error_log("CHECKING INVENTORY FOR " . count($skus) . " ITEM(S) ON HUBS " . count($branch_codes) . "..."); $branches = $this->im->getBranchesInventory($branch_codes, $skus); - error_log("REQUEST COMPLETE, RESULT COUNT: " . count($branches)); - error_log(print_r($branches, true)); + //error_log("REQUEST COMPLETE, RESULT COUNT: " . count($branches)); // check each result to see if sufficient quantity exists to meet request foreach ($branches as $branch) { - error_log("CHECKING BRANCH " . print_r($branch, true)); - // filter out branch if it does not have sufficient inventory - if ($branch['Quantity'] < $items[$branch['SapCode']] && - !isset($hubs_to_filter[$branch['BranchCode']]) - ) { - $hubs_to_filter[$branch['BranchCode']] = true; + if (isset($branch['BranchCode'])) { + // filter out branch if it does not have sufficient inventory + if ($branch['Quantity'] < $items[$branch['SapCode']] && + !isset($hubs_to_filter[$branch['BranchCode']]) + ) { + error_log("FILTERING BRANCH WITH NO INVENTORY: " . $branch['BranchCode']); + $hubs_to_filter[$branch['BranchCode']] = true; + } else { + // save inventory count so we don't have to recheck later + $qtys[$branch['BranchCode']] = $branch['Quantity']; + } } } - error_log("COMPLETED BRANCH CHECKS"); - // remove filtered hubs from list foreach ($hubs as $hub_data) { $hub_obj = $hub_data['hub']; + $branch_code = $hub_data['hub']->getBranchCode(); // check if we are filtering this hub - if (isset($hubs_to_filter[$hub_data['hub']->getBranchCode()])) { + if (isset($hubs_to_filter[$branch_code])) { // send SMS to hub $this->sendSMSMessage($hub_obj, $items); // log this filter $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_inventory', $jo_id, $customer_id); - } else { + } else if (!empty($branch_code) && isset($qtys[$branch_code])) { + // include inventory in hub data + $hub_data['inventory'] = $qtys[$branch_code]; + + // we only include branches with branch codes and quantities $results[] = $hub_data; } } - error_log("COMPLETED HUB FILTERING, TOTAL RESULTS: " . count($results)); - // return filtered hubs return $results; @@ -494,6 +504,7 @@ class HubSelector 'distance' => $dist, 'duration' => 0, 'jo_count' => 0, + 'inventory' => 0, ]; } else From 190ac88153582e6ac9b575d4aa1e1f50736454b2 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 15 May 2024 18:00:07 +0800 Subject: [PATCH 08/49] Enable inventory and rider filtering on CRM JO form hub lists #800 --- .../JobOrderHandler/ResqJobOrderHandler.php | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 419c27c3..9fbea4b9 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -2577,6 +2577,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $hub_criteria->setEmergency(true); } + // set filter flags for inventory and available riders + $hub_criteria->setInventoryCheck(); + $hub_criteria->setRidersCheck(); + // get JO and customer id for logging purposes $jo_id = $obj->getID(); $customer_id = $obj->getCustomer()->getID(); @@ -2646,7 +2650,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // handle inventory data $bcode = $hub['hub']->getBranchCode(); - $hub['inventory'] = 0; + //$hub['inventory'] = 0; if ($bcode != '') { $branch_codes[] = $bcode; @@ -2664,7 +2668,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // get template to display $params['template'] = $this->getTwigTemplate('jo_processing_form'); + // NOTE: as we have included inventory now from the hub selector, we no longer have to redo it here + // get battery (if any) + /* $skus = []; $invoice = $obj->getInvoice(); $inv_items = $invoice->getItems(); @@ -2694,6 +2701,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } } } + */ // error_log(print_r($mres, true)); @@ -2908,6 +2916,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $hub_criteria->setEmergency(true); } + // set filter flags for inventory and available riders + $hub_criteria->setInventoryCheck(); + $hub_criteria->setRidersCheck(); + // get JO and customer id for logging purposes $jo_id = $obj->getID(); $customer_id = $obj->getCustomer()->getID(); @@ -2921,6 +2933,8 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $params['hubs'] = []; $branch_codes = []; + $inv_data = []; + // format duration and distance into friendly time foreach ($hubs as $hub) { // duration @@ -2976,7 +2990,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // handle inventory data $bcode = $hub['hub']->getBranchCode(); - $hub['inventory'] = 0; + //$hub['inventory'] = 0; if ($bcode != '') { $branch_codes[] = $bcode; @@ -2990,7 +3004,12 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $params['hubs'][$hub_id] = $hub; } + // NOTE: as we have included inventory now from the hub selector, we no longer have to redo it here + + //error_log("TOTAL HUBS FOUND WITH BRANCH CODE: " . count($inv_data)); + // get battery (if any) + /* $skus = []; $invoice = $obj->getInvoice(); $inv_items = $invoice->getItems(); @@ -3005,6 +3024,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // get inventory $mres = $motiv->getInventory($branch_codes, $skus); + $x = 0; + + error_log("TOTAL RESULTS FROM MOTIV: " . count($mres) . " OUT OF " . count($branch_codes) . " BRANCH CODES AND " . count($skus) . " SKUS"); + foreach ($mres as $mres_item) { // check if we have a valid response from motiv, ignore otherwise @@ -3017,11 +3040,21 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $hub_id = $inv_data[$bcode]['hub_id']; $params['hubs'][$hub_id]['inventory'] = $inv_count; + + error_log("SETTING HUB " . $hub_id . " INVENTORY TO " . $inv_count); + $x++; + } else { + error_log("CANNOT FIND BCODE FOR " . $bcode); } + } else { + error_log("CANNOT FIND BCODE FOR RESULT: " . print_r($mres_item, true)); } } + error_log("SET QUANTITY OF " . $x . " HUBS TO NON ZERO"); + // error_log(print_r($mres, true)); + */ $params['obj'] = $obj; // get template to display @@ -3449,6 +3482,8 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // get riders for dropdown $params['riders'] = $this->em->getRepository(Rider::class)->findAll(); + error_log("YEO----------------------------------"); + // get hubs for dropdown $params['hubs'] = $this->em->getRepository(Hub::class)->findBy(['flag_hub_view' => true]); From 7b6afbb09901d6e643f849ae3c839ee73cc9f9e0 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 15 May 2024 18:00:27 +0800 Subject: [PATCH 09/49] Modify inventory SMS message #800 --- translations/messages.en.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 43013469..5fbc11a9 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -13,7 +13,7 @@ add_cust_vehicle_battery_info: This vehicle is using a Motolite battery jo_title_pdf: Motolite Res-Q Job Order country_code_prefix: '+63' delivery_instructions_label: Delivery Instructions -no_inventory_message: 'A Job Order was created but there is no stock for the following SKU(s) on this branch: [item_display]' +no_inventory_message: 'A Job Order was created but there is insufficient stock for the following SKU(s) on this branch: [item_display]' no_riders_message: A Job Order was created but there are no riders available for this branch. # images From 5cf2c3619fb84833b14b81a0edb2d5aa530afe69 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 16 May 2024 15:07:35 +0800 Subject: [PATCH 10/49] Remove debug code from JO handler #800 --- src/Service/JobOrderHandler/ResqJobOrderHandler.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 9fbea4b9..02d2e8df 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -3482,8 +3482,6 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // get riders for dropdown $params['riders'] = $this->em->getRepository(Rider::class)->findAll(); - error_log("YEO----------------------------------"); - // get hubs for dropdown $params['hubs'] = $this->em->getRepository(Hub::class)->findBy(['flag_hub_view' => true]); From 21c97df677488b943217c08325f03cb1a0a82656 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 16 May 2024 21:46:02 +0800 Subject: [PATCH 11/49] Set JO type regardless of area support on hub form initialization to trigger inventory filter #800 --- src/Service/HubSelector.php | 12 ++++++------ src/Service/JobOrderHandler/ResqJobOrderHandler.php | 8 ++++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index eeeba2db..f16b4615 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -271,19 +271,19 @@ class HubSelector { // check if this is enabled if (!$flag_inventory_check) { - //rror_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); + error_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); return $hubs; } // check hub list is not empty if (empty($hubs)) { - //error_log("INVENTORY CHECK " . $jo_id . ": NO HUBS"); + error_log("INVENTORY CHECK " . $jo_id . ": NO HUBS"); return $hubs; } // check item list is not empty if (empty($items)) { - //error_log("INVENTORY CHECK " . $jo_id . ": NO ITEMS"); + error_log("INVENTORY CHECK " . $jo_id . ": NO ITEMS"); return $hubs; } @@ -291,7 +291,7 @@ class HubSelector if ($jo_type != ServiceType::BATTERY_REPLACEMENT_NEW && $jo_type != ServiceType::BATTERY_REPLACEMENT_WARRANTY ) { - //error_log("INVENTORY CHECK " . $jo_id . ": INVALID SERVICE TYPE"); + error_log("INVENTORY CHECK " . $jo_id . ": INVALID SERVICE TYPE: " . $jo_type); return $hubs; } @@ -311,11 +311,11 @@ class HubSelector // call inventory manager for all hubs for selected SKUs $skus = array_keys($items); - //error_log("CHECKING INVENTORY FOR " . count($skus) . " ITEM(S) ON HUBS " . count($branch_codes) . "..."); + error_log("CHECKING INVENTORY FOR " . count($skus) . " ITEM(S) ON HUBS " . count($branch_codes) . "..."); $branches = $this->im->getBranchesInventory($branch_codes, $skus); - //error_log("REQUEST COMPLETE, RESULT COUNT: " . count($branches)); + error_log("REQUEST COMPLETE, RESULT COUNT: " . count($branches)); // check each result to see if sufficient quantity exists to meet request foreach ($branches as $branch) { diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 02d2e8df..a6a9449b 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -2551,6 +2551,9 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setDateTime($obj->getDateSchedule()) ->setLimitResults(50); + // NOTE: set JO type regardless, for now + $hub_criteria->setJoType($obj->getServiceType()); + // check if hub filter is enabled. If not, use default values // for the rest of the HubCriteria fields if ($this->hub_filter_enabled == 'true') @@ -2562,7 +2565,6 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // error_log('Area is covered by hub filtering'); $hub_criteria->setLimitDistance($this->cust_distance_limit) ->setPaymentMethod($obj->getModeOfPayment()) - ->setJoType($obj->getServiceType()) ->setRoundRobin(true); } } @@ -2897,13 +2899,15 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setDateTime($obj->getDateSchedule()) ->setLimitResults(50); + // NOTE: set JO type regardless, for now + $hub_criteria->setJoType($obj->getServiceType()); + if ($this->hub_geofence->isCovered($long, $lat)) { // if true, set other values for HubCriteria // error_log('Area is covered by hub'); $hub_criteria->setLimitDistance($this->cust_distance_limit) ->setPaymentMethod($obj->getModeOfPayment()) - ->setJoType($obj->getServiceType()) ->setRoundRobin(true); } From 4be9134090c5a2c6779d97c7ad463757203c77f0 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 17 May 2024 21:40:16 +0800 Subject: [PATCH 12/49] Update SMS message format for rejected hubs, add JO rejection entries for no inventory or no riders #800 --- src/Service/HubSelector.php | 97 ++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 28 deletions(-) diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index f16b4615..40de050d 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -4,11 +4,13 @@ namespace App\Service; use Doctrine\ORM\EntityManagerInterface; +use Proxies\__CG__\App\Entity\JORejection; use Symfony\Contracts\Translation\TranslatorInterface; use CrEOF\Spatial\PHP\Types\Geometry\Point; use App\Entity\Hub; +use App\Entity\JobOrder; use App\Service\HubDistributor; use App\Service\InventoryManager; @@ -17,6 +19,9 @@ use App\Service\RisingTideGateway; use App\Ramcar\HubCriteria; use App\Ramcar\ServiceType; +use App\Ramcar\JOEventType; +use App\Ramcar\JORejectionReason; +use DateTime; class HubSelector { @@ -57,6 +62,9 @@ class HubSelector $results = []; + // get the job order object + $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); + // error_log('payment methods ' . $payment_method); // error_log('distance limit ' . $limit_distance); // error_log('emergency flag ' . $flag_emergency); @@ -91,11 +99,11 @@ class HubSelector // error_log('round robin hubs ' . json_encode($filtered_hubs)); // available riders filter - $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_type, $jo_id, $customer_id); + $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_type, $jo, $customer_id); $filtered_hubs = $hubs_riders; // inventory filter - $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id); + $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo, $customer_id); $filtered_hubs = $hubs_inventory; // round robin filter @@ -267,8 +275,10 @@ class HubSelector return $results; } - protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id) + protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo, $customer_id) { + $jo_id = $jo->getID(); + // check if this is enabled if (!$flag_inventory_check) { error_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); @@ -340,11 +350,26 @@ class HubSelector // check if we are filtering this hub if (isset($hubs_to_filter[$branch_code])) { - // send SMS to hub - $this->sendSMSMessage($hub_obj, $items); + // create rejection report entry + $robj = $this->createRejectionEntry( + $jo, + $hub_obj, + "SKU(s): " . implode(", ", $skus) + ); + + // build SMS message + $this->sendSMSMessage( + $hub_obj, + $jo, + $robj, + JORejectionReason::getName(JORejectionReason::NO_STOCK_SALES), + "Requested SKU(s) - " . implode(", ", $skus) + ); // log this filter $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_inventory', $jo_id, $customer_id); + + error_log("FILTERED HUB " . $hub_obj->getID() . " (no_inventory)"); } else if (!empty($branch_code) && isset($qtys[$branch_code])) { // include inventory in hub data $hub_data['inventory'] = $qtys[$branch_code]; @@ -428,7 +453,7 @@ class HubSelector */ } - protected function filterHubsByRiderAvailability($hubs, $flag_riders_check, $jo_type, $jo_id, $customer_id) + protected function filterHubsByRiderAvailability($hubs, $flag_riders_check, $jo_type, $jo, $customer_id) { // check if this is enabled if (!$flag_riders_check) { @@ -440,6 +465,7 @@ class HubSelector return $hubs; } + $jo_id = $jo->getID(); $results = []; foreach ($hubs as $hub_data) { @@ -447,11 +473,15 @@ class HubSelector // check we have available riders if ($hub_obj->getAvailableRiders() === 0) { - // send SMS to hub - $this->rt->sendSMS( - $hub_obj->getNotifNumber(), - $this->trans->trans('message.battery_brand_allcaps'), - $this->trans->trans('message.no_riders_message') + // create rejection report entry + $robj = $this->createRejectionEntry($jo, $hub_obj); + + // build SMS message + $this->sendSMSMessage( + $hub_obj, + $jo, + $robj, + JORejectionReason::getName(JORejectionReason::NO_RIDER_AVAILABLE), ); // log this filter @@ -569,26 +599,37 @@ class HubSelector return round(($miles * 1.609344), 1); } - protected function sendSMSMessage($hub, $items) + protected function createRejectionEntry($jo, $hub, $remarks = ""): JORejection { - // 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')); + $robj = new JORejection(); + $robj->setDateCreate(new DateTime()) + ->setHub($hub) + ->setJobOrder($jo) + ->setReason(JORejectionReason::NO_STOCK_SALES) + ->setRemarks(implode(" ", ["Automatically filtered by hub selector.", $remarks])); - // get hub notification number - $mobile_number = $hub->getNotifNumber(); + $this->em->persist($robj); + $this->em->flush(); - if (!empty($mobile_number)) - { - // send SMS message - // error_log('sending sms to - ' . $mobile_number); - $this->rt->sendSMS($mobile_number, $this->trans->trans('message.battery_brand_allcaps'), $message); - } + return $robj; + } + + protected function sendSMSMessage($hub, $jo, $rejection, $reason = "", $remarks = ""): void + { + $message = 'Job Order #: ' . $jo->getID() . "\n" . + 'Order Date and Time: ' . $jo->getDateCreate()->format('d M Y g:i A') . "\n" . + 'Date and Time Rejected: ' . $rejection->getDateCreate()->format('d M Y g:i A') . "\n" . + 'Enrollee Name: ' . implode(" - ", [$hub->getName(), $hub->getBranch()]) . "\n" . + 'Reason of Rejection: ' . $reason . "\n" . + 'Remarks: ' . $remarks . "\n" . + 'Type of Service: ' . ServiceType::getName($jo->getServiceType()); + + // send SMS message to hub + $this->rt->sendSMS( + $hub->getNotifNumber(), + $this->trans->trans('message.battery_brand_allcaps'), + $message + ); } } From 22683e1edfcdde330617d2bc452d881f08c82839 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 17 May 2024 22:01:43 +0800 Subject: [PATCH 13/49] Add null check to user on detailed rejection report #800 --- src/Controller/ReportController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/ReportController.php b/src/Controller/ReportController.php index 4f3ed31e..1fe1e337 100644 --- a/src/Controller/ReportController.php +++ b/src/Controller/ReportController.php @@ -244,7 +244,7 @@ class ReportController extends Controller JORejectionReason::getName($jor->getReason()), $jor->getContactPerson(), $jor->getRemarks(), - $jor->getUser()->getFullName(), + !empty($jor->getUser()) ? $jor->getUser()->getFullName() : "", ServiceType::getName($jo->getServiceType()), ]; } From b53aacb840c250813fcaa9b96608bb2426491a76 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 17 May 2024 22:23:26 +0800 Subject: [PATCH 14/49] Update rejection messages to use battery name and model vs SKUs #800 --- src/Service/HubSelector.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 40de050d..fedc1ed9 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -11,6 +11,7 @@ use CrEOF\Spatial\PHP\Types\Geometry\Point; use App\Entity\Hub; use App\Entity\JobOrder; +use App\Entity\Battery; use App\Service\HubDistributor; use App\Service\InventoryManager; @@ -343,6 +344,14 @@ class HubSelector } } + // get battery models for each requested SKU + $batteries = []; + foreach ($skus as $sku) { + $bobj = $this->em->getRepository(Battery::class)->findOneBy(['sap_code' => $sku]); + $batteries[] = implode(" ", [$bobj->getModel()->getName(), $bobj->getSize()->getName()]); + } + $battery_string = implode(", ", $batteries); + // remove filtered hubs from list foreach ($hubs as $hub_data) { $hub_obj = $hub_data['hub']; @@ -354,8 +363,8 @@ class HubSelector $robj = $this->createRejectionEntry( $jo, $hub_obj, - "SKU(s): " . implode(", ", $skus) - ); + "SKU(s): " . $battery_string + ); // build SMS message $this->sendSMSMessage( @@ -363,7 +372,7 @@ class HubSelector $jo, $robj, JORejectionReason::getName(JORejectionReason::NO_STOCK_SALES), - "Requested SKU(s) - " . implode(", ", $skus) + "Requested SKU(s) - " . $battery_string ); // log this filter @@ -624,6 +633,8 @@ class HubSelector 'Remarks: ' . $remarks . "\n" . 'Type of Service: ' . ServiceType::getName($jo->getServiceType()); + error_log("SENDING SMS MESsAGE:\r\n" . $message); + // send SMS message to hub $this->rt->sendSMS( $hub->getNotifNumber(), From aae4aaa3903c4e25f505639a9ff710bb33635764 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Sat, 18 May 2024 15:21:32 +0800 Subject: [PATCH 15/49] Enable inventory and rider hub filters for emergency JOs, disable for VIP customers #800 --- src/Service/HubSelector.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index fedc1ed9..9bb1c13d 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -12,7 +12,7 @@ use CrEOF\Spatial\PHP\Types\Geometry\Point; use App\Entity\Hub; use App\Entity\JobOrder; use App\Entity\Battery; - +use App\Ramcar\CustomerClassification; use App\Service\HubDistributor; use App\Service\InventoryManager; use App\Service\HubFilterLogger; @@ -63,8 +63,9 @@ class HubSelector $results = []; - // get the job order object + // get the job order object and customer class $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); + $customer_class = $jo->getCustomer()->getCustomerClassification(); // error_log('payment methods ' . $payment_method); // error_log('distance limit ' . $limit_distance); @@ -81,6 +82,8 @@ class HubSelector // error_log('date_time hubs ' . json_encode($filtered_hubs)); + // TODO: allow toggling of each filter individually for specific conditions (e.g. customer class, emergency) + if (!$flag_emergency) { // filter jo types @@ -98,7 +101,10 @@ class HubSelector //error_log('inventory hubs ' . json_encode($filtered_hubs)); // error_log('round robin hubs ' . json_encode($filtered_hubs)); + } + // only enable rider and inventory checks if not VIP + if ($customer_class !== CustomerClassification::VIP) { // available riders filter $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_type, $jo, $customer_id); $filtered_hubs = $hubs_riders; @@ -106,7 +112,9 @@ class HubSelector // inventory filter $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo, $customer_id); $filtered_hubs = $hubs_inventory; + } + if (!$flag_emergency) { // round robin filter $hubs_round_robin = $this->filterHubsByRoundRobin($filtered_hubs, $flag_round_robin); $filtered_hubs = $hubs_round_robin; From 6dfaeee799004b98bee039745b8dd6311bb5d515 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Mon, 20 May 2024 03:46:08 +0800 Subject: [PATCH 16/49] Generate JO ID before hub selection on user app API, add additional params to HubCriteria for JORejection and SMS #800 --- .../CustomerAppAPI/JobOrderController.php | 26 +++++- src/Ramcar/HubCriteria.php | 46 +++++++++- src/Service/HubSelector.php | 92 ++++++++++--------- .../JobOrderHandler/ResqJobOrderHandler.php | 18 +++- 4 files changed, 133 insertions(+), 49 deletions(-) diff --git a/src/Controller/CustomerAppAPI/JobOrderController.php b/src/Controller/CustomerAppAPI/JobOrderController.php index fd5926e5..521a32d2 100644 --- a/src/Controller/CustomerAppAPI/JobOrderController.php +++ b/src/Controller/CustomerAppAPI/JobOrderController.php @@ -488,6 +488,8 @@ class JobOrderController extends ApiController JobOrderManager $jo_manager, PriceTierManager $pt_manager ) { + //error_log("CREATING JOB ORDER WITH PARAMS " . print_r($req->request->all(), true)); + // validate params $validity = $this->validateRequest($req, [ 'service_type', @@ -575,6 +577,8 @@ class JobOrderController extends ApiController $flag_advance_order = true; // $flag_advance_order = $advance_order ? true : false; + //error_log("RUNNING QUERY NEXT"); + $jo = new JobOrder(); $jo->setSource(TransactionOrigin::MOBILE_APP) ->setStatus(JOStatus::PENDING) @@ -643,6 +647,8 @@ class JobOrderController extends ApiController $icrit->addPromo($promo); } + //error_log("CONTINUING QUERY BUILDING"); + // check customer vehicle $cv = $this->em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id')); if ($cv == null) { @@ -708,6 +714,13 @@ class JobOrderController extends ApiController $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); + //error_log("GENERATED INVOICE"); + + // save here first so we have a JO ID which is required for the hub selector + $this->em->persist($invoice); + $this->em->persist($jo); + $this->em->flush(); + // assign hub and rider // check if hub is null if ($hub == null) { @@ -715,6 +728,13 @@ class JobOrderController extends ApiController $hub_criteria = new HubCriteria(); $hub_criteria->setPoint($jo->getCoordinates()); + // set job order info + $hub_criteria->setJobOrderId($jo->getID()) + ->setJoType($jo->getServiceType()) + ->setCustomerClass($cust->getCustomerClassification()) + ->setOrderDate($jo->getDateCreate()) + ->setServiceType($jo->getServiceType()); + // get distance limit for mobile from env // get value of hub_filter_enable from env $limit_distance = $_ENV['CUST_DISTANCE_LIMIT']; @@ -845,8 +865,10 @@ class JobOrderController extends ApiController $hub_dist->incrementJoCountForHub($hub); } + //error_log("DONE SELECTING HUB"); + + // save additional hub related changes $this->em->persist($jo); - $this->em->persist($invoice); // add event log for JO $event = new JOEvent(); @@ -957,6 +979,8 @@ class JobOrderController extends ApiController } } + //error_log("DONE CREATING JOB ORDER " . $jo->getID()); + // response return new ApiResponse(true, '', [ 'jo_id' => $jo->getID(), diff --git a/src/Ramcar/HubCriteria.php b/src/Ramcar/HubCriteria.php index ffb11265..6dff2743 100644 --- a/src/Ramcar/HubCriteria.php +++ b/src/Ramcar/HubCriteria.php @@ -21,6 +21,9 @@ class HubCriteria protected $flag_round_robin; // flag if we use round robin or not protected $jo_id; // JO id. This is null if called from mobile API protected $customer_id; // customer id + protected $customer_class; // customer class + protected $order_date; // date JO was created + protected $service_type; // service type of JO public function __construct() { @@ -33,10 +36,13 @@ class HubCriteria $this->flag_riders_check = false; $this->items = []; $this->payment_method = ''; - $flag_emergency = false; - $flag_round_robin = false; - $jo_id = null; - $customer_id = null; + $this->flag_emergency = false; + $this->flag_round_robin = false; + $this->jo_id = null; + $this->customer_id = null; + $this->customer_class = null; + $this->order_date = new DateTime(); + $this->service_type = null; } public function setPoint(Point $point) @@ -184,5 +190,37 @@ class HubCriteria return $this->customer_id; } + public function setCustomerClass($customer_class) + { + $this->customer_class = $customer_class; + return $this; + } + + public function getCustomerClass() + { + return $this->customer_class; + } + + public function setOrderDate($order_date) + { + $this->order_date = $order_date; + return $this; + } + + public function getOrderDate() + { + return $this->order_date; + } + + public function setServiceType($service_type) + { + $this->service_type = $service_type; + return $this; + } + + public function getServiceType() + { + return $this->service_type; + } } diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 9bb1c13d..2cfcd50d 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -60,13 +60,14 @@ class HubSelector $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(); $results = []; - // get the job order object and customer class - $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); - $customer_class = $jo->getCustomer()->getCustomerClassification(); - // error_log('payment methods ' . $payment_method); // error_log('distance limit ' . $limit_distance); // error_log('emergency flag ' . $flag_emergency); @@ -104,13 +105,13 @@ class HubSelector } // only enable rider and inventory checks if not VIP - if ($customer_class !== CustomerClassification::VIP) { + if (!empty($customer_class) && $customer_class !== CustomerClassification::VIP) { // available riders filter - $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_type, $jo, $customer_id); + $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_id, $customer_id, $order_date, $service_type); $filtered_hubs = $hubs_riders; // inventory filter - $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo, $customer_id); + $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id, $order_date, $service_type); $filtered_hubs = $hubs_inventory; } @@ -284,10 +285,8 @@ class HubSelector return $results; } - protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo, $customer_id) + protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id, $order_date, $service_type) { - $jo_id = $jo->getID(); - // check if this is enabled if (!$flag_inventory_check) { error_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); @@ -367,21 +366,26 @@ class HubSelector // check if we are filtering this hub if (isset($hubs_to_filter[$branch_code])) { - // create rejection report entry - $robj = $this->createRejectionEntry( - $jo, - $hub_obj, - "SKU(s): " . $battery_string - ); + // if we have a JO, create rejection record and notify + if (!empty($jo_id)) { + // create rejection report entry + $robj = $this->createRejectionEntry( + $jo_id, + $hub_obj, + "SKU(s): " . $battery_string + ); - // build SMS message - $this->sendSMSMessage( - $hub_obj, - $jo, - $robj, - JORejectionReason::getName(JORejectionReason::NO_STOCK_SALES), - "Requested SKU(s) - " . $battery_string - ); + // build SMS message + $this->sendSMSMessage( + $hub_obj, + $jo_id, + $order_date, + $service_type, + $robj, + JORejectionReason::getName(JORejectionReason::NO_STOCK_SALES), + "Requested SKU(s) - " . $battery_string + ); + } // log this filter $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_inventory', $jo_id, $customer_id); @@ -470,7 +474,7 @@ class HubSelector */ } - protected function filterHubsByRiderAvailability($hubs, $flag_riders_check, $jo_type, $jo, $customer_id) + protected function filterHubsByRiderAvailability($hubs, $flag_riders_check, $jo_id, $customer_id, $order_date, $service_type) { // check if this is enabled if (!$flag_riders_check) { @@ -482,7 +486,6 @@ class HubSelector return $hubs; } - $jo_id = $jo->getID(); $results = []; foreach ($hubs as $hub_data) { @@ -490,17 +493,22 @@ class HubSelector // check we have available riders if ($hub_obj->getAvailableRiders() === 0) { - // create rejection report entry - $robj = $this->createRejectionEntry($jo, $hub_obj); - - // build SMS message - $this->sendSMSMessage( - $hub_obj, - $jo, - $robj, - JORejectionReason::getName(JORejectionReason::NO_RIDER_AVAILABLE), - ); + // if we have a JO, create rejection record and notify + if (!empty($jo_id)) { + // create rejection report entry + $robj = $this->createRejectionEntry($jo_id, $hub_obj); + // build SMS message + $this->sendSMSMessage( + $hub_obj, + $jo_id, + $order_date, + $service_type, + $robj, + JORejectionReason::getName(JORejectionReason::NO_RIDER_AVAILABLE), + ); + } + // log this filter $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_available_rider', $jo_id, $customer_id); @@ -616,8 +624,10 @@ class HubSelector return round(($miles * 1.609344), 1); } - protected function createRejectionEntry($jo, $hub, $remarks = ""): JORejection + protected function createRejectionEntry($jo_id, $hub, $remarks = ""): JORejection { + $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); + $robj = new JORejection(); $robj->setDateCreate(new DateTime()) ->setHub($hub) @@ -631,15 +641,15 @@ class HubSelector return $robj; } - protected function sendSMSMessage($hub, $jo, $rejection, $reason = "", $remarks = ""): void + protected function sendSMSMessage($hub, $jo_id, $order_date, $service_type, $rejection, $reason = "", $remarks = ""): void { - $message = 'Job Order #: ' . $jo->getID() . "\n" . - 'Order Date and Time: ' . $jo->getDateCreate()->format('d M Y g:i A') . "\n" . + $message = 'Job Order #: ' . $jo_id . "\n" . + 'Order Date and Time: ' . $order_date->format('d M Y g:i A') . "\n" . 'Date and Time Rejected: ' . $rejection->getDateCreate()->format('d M Y g:i A') . "\n" . 'Enrollee Name: ' . implode(" - ", [$hub->getName(), $hub->getBranch()]) . "\n" . 'Reason of Rejection: ' . $reason . "\n" . 'Remarks: ' . $remarks . "\n" . - 'Type of Service: ' . ServiceType::getName($jo->getServiceType()); + 'Type of Service: ' . ServiceType::getName($service_type); error_log("SENDING SMS MESsAGE:\r\n" . $message); diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index a6a9449b..19c74e33 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -2552,7 +2552,13 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setLimitResults(50); // NOTE: set JO type regardless, for now - $hub_criteria->setJoType($obj->getServiceType()); + $hub_criteria->setJoType($obj->getServiceType()) + ->setOrderDate($obj->getDateCreate()) + ->setServiceType($obj->getServiceType()); + + // set customer class + $cust = $obj->getCustomer(); + $hub_criteria->setCustomerClass($cust->getCustomerClass()); // check if hub filter is enabled. If not, use default values // for the rest of the HubCriteria fields @@ -2900,7 +2906,13 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setLimitResults(50); // NOTE: set JO type regardless, for now - $hub_criteria->setJoType($obj->getServiceType()); + $hub_criteria->setJoType($obj->getServiceType()) + ->setOrderDate($obj->getDateCreate()) + ->setServiceType($obj->getServiceType()); + + // set customer class + $cust = $obj->getCustomer(); + $hub_criteria->setCustomerClass($cust->getCustomerClass()); if ($this->hub_geofence->isCovered($long, $lat)) { @@ -2926,7 +2938,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // get JO and customer id for logging purposes $jo_id = $obj->getID(); - $customer_id = $obj->getCustomer()->getID(); + $customer_id = $cust->getID(); $hub_criteria->setJobOrderId($jo_id) ->setCustomerId($customer_id); From 404401d8542e7655b7f75b1838e4cf5f3ad220e7 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 30 May 2024 16:06:04 +0800 Subject: [PATCH 17/49] Fix erroneous delivery status IDs on collection #800 --- src/Ramcar/DeliveryStatus.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ramcar/DeliveryStatus.php b/src/Ramcar/DeliveryStatus.php index b4d06871..c567032f 100644 --- a/src/Ramcar/DeliveryStatus.php +++ b/src/Ramcar/DeliveryStatus.php @@ -23,8 +23,8 @@ class DeliveryStatus extends NameValue const COLLECTION = [ 'rider_assign' => 'Assigned Rider', 'requeue' => 'Requeue', - 'accept' => 'Rider Accept', - 'arrive' => 'Rider Arrive', + 'rider_accept' => 'Rider Accept', + 'rider_arrive' => 'Rider Arrive', 'rider_edit' => 'Rider Edit', 'rider_depart_hub' => 'Rider Depart Hub', 'rider_arrive_hub_pre_jo' => 'Rider Arrive Hub Pre JO', From 2ebb6e040a01949abfc6b95c659c31ead1c2e4ac Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 30 May 2024 16:06:55 +0800 Subject: [PATCH 18/49] Add placeholder debug logs #800 --- .../CustomerAppAPI/JobOrderController.php | 4 ++++ src/Service/HubSelector.php | 16 +++++++++------- .../JobOrderHandler/ResqJobOrderHandler.php | 4 ++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Controller/CustomerAppAPI/JobOrderController.php b/src/Controller/CustomerAppAPI/JobOrderController.php index 521a32d2..a6fc8baa 100644 --- a/src/Controller/CustomerAppAPI/JobOrderController.php +++ b/src/Controller/CustomerAppAPI/JobOrderController.php @@ -724,6 +724,8 @@ class JobOrderController extends ApiController // assign hub and rider // check if hub is null if ($hub == null) { + //error_log("NO HUB"); + // TODO: need to factor out the setting of HubCriteria fields $hub_criteria = new HubCriteria(); $hub_criteria->setPoint($jo->getCoordinates()); @@ -854,6 +856,8 @@ class JobOrderController extends ApiController } } } else { + //error_log("HAS HUB: " . $hub->getID()); + $jo->setHub($hub); $jo->setStatus(JOStatus::RIDER_ASSIGN); $jo->setStatusAutoAssign(AutoAssignStatus::HUB_ASSIGNED); diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 2cfcd50d..97a4a8f8 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -91,17 +91,13 @@ class HubSelector $hubs_jo_type = $this->filterHubsByJoType($filtered_hubs, $jo_type, $jo_id, $customer_id); $filtered_hubs = $hubs_jo_type; - //error_log('jo_type hubs ' . json_encode($filtered_hubs)); + // error_log('jo_type hubs ' . json_encode($filtered_hubs)); // filter hubs by payment methods $hubs_payment_method = $this->filterHubsByPaymentMethod($filtered_hubs, $payment_method, $jo_id, $customer_id); $filtered_hubs = $hubs_payment_method; - //error_log('payment hubs ' . json_encode($filtered_hubs)); - - //error_log('inventory hubs ' . json_encode($filtered_hubs)); - - // error_log('round robin hubs ' . json_encode($filtered_hubs)); + // error_log('payment hubs ' . json_encode($filtered_hubs)); } // only enable rider and inventory checks if not VIP @@ -110,9 +106,13 @@ class HubSelector $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_id, $customer_id, $order_date, $service_type); $filtered_hubs = $hubs_riders; + // error_log('available riders hubs ' . json_encode($filtered_hubs)); + // inventory filter $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id, $order_date, $service_type); $filtered_hubs = $hubs_inventory; + + // error_log('inventory hubs ' . json_encode($filtered_hubs)); } if (!$flag_emergency) { @@ -120,6 +120,8 @@ class HubSelector $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, $jo_id, $customer_id); $filtered_hubs = $hubs_max_result; @@ -127,7 +129,7 @@ class HubSelector $results = $filtered_hubs; - // error_log(json_encode($results)); + // error_log('final hub list ' . json_encode($filtered_hubs)); return $results; } diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 19c74e33..025239b7 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -2558,7 +2558,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // set customer class $cust = $obj->getCustomer(); - $hub_criteria->setCustomerClass($cust->getCustomerClass()); + $hub_criteria->setCustomerClass($cust->getCustomerClassification()); // check if hub filter is enabled. If not, use default values // for the rest of the HubCriteria fields @@ -2912,7 +2912,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // set customer class $cust = $obj->getCustomer(); - $hub_criteria->setCustomerClass($cust->getCustomerClass()); + $hub_criteria->setCustomerClass($cust->getCustomerClassification()); if ($this->hub_geofence->isCovered($long, $lat)) { From 4b19cff996620938bee047d2782d82afb4aeb7b5 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 30 May 2024 17:15:47 +0800 Subject: [PATCH 19/49] Add checks to rider app API for JO status and delivery status #802 --- src/Controller/CAPI/RiderAppController.php | 88 ++++++++++++++++------ 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/src/Controller/CAPI/RiderAppController.php b/src/Controller/CAPI/RiderAppController.php index 694ffdd0..bb9c8546 100644 --- a/src/Controller/CAPI/RiderAppController.php +++ b/src/Controller/CAPI/RiderAppController.php @@ -413,7 +413,9 @@ class RiderAppController extends ApiController return new APIResponse(false, $msg); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // TODO: refactor this into a jo handler class, so we don't have to repeat for control center @@ -466,7 +468,9 @@ class RiderAppController extends ApiController return new APIResponse(true, $msg); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // requeue it, instead of cancelling it $jo->requeue(); @@ -527,7 +531,9 @@ class RiderAppController extends ApiController $jo = $rider->getCurrentJobOrder(); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // set delivery status $jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB); @@ -570,7 +576,9 @@ class RiderAppController extends ApiController $jo = $rider->getCurrentJobOrder(); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // set delivery status $jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB_PRE_JO); @@ -613,7 +621,9 @@ class RiderAppController extends ApiController $jo = $rider->getCurrentJobOrder(); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // set delivery status $jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB_PRE_JO); @@ -656,7 +666,9 @@ class RiderAppController extends ApiController $jo = $rider->getCurrentJobOrder(); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // set delivery status $jo->setDeliveryStatus(DeliveryStatus::RIDER_START); @@ -700,7 +712,9 @@ class RiderAppController extends ApiController $jo->setStatus(JOStatus::IN_PROGRESS); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // set delivery status $jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE); @@ -761,7 +775,9 @@ class RiderAppController extends ApiController $jo = $rider->getCurrentJobOrder(); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // set delivery status $jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB); @@ -854,7 +870,9 @@ class RiderAppController extends ApiController return new APIResponse(false, $msg); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // need to check if service type is battery sales // if so, serial is a required parameter @@ -1002,7 +1020,9 @@ class RiderAppController extends ApiController $jo = $rider->getCurrentJobOrder(); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // set delivery status $jo->setDeliveryStatus(DeliveryStatus::RIDER_ARRIVE_HUB_POST_JO); @@ -1046,7 +1066,9 @@ class RiderAppController extends ApiController $jo = $rider->getCurrentJobOrder(); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // set delivery status $jo->setDeliveryStatus(DeliveryStatus::RIDER_DEPART_HUB_POST_JO); @@ -1265,7 +1287,9 @@ class RiderAppController extends ApiController $jo = $em->getRepository(JobOrder::class)->find($jo_id); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // check if we have trade in items $ti_items = []; @@ -1376,7 +1400,9 @@ class RiderAppController extends ApiController return new APIResponse(false, $msg); // check if JO can be modified first - $this->checkJOProgressionAllowed($jo, $rider); + if (!$this->checkJOProgressionAllowed($em, $jo, $rider)) { + return new APIResponse(false, 'Job order can no longer be modified.'); + } // check service type $stype_id = $req->request->get('stype_id'); @@ -1740,22 +1766,40 @@ class RiderAppController extends ApiController return $msg; } - protected function checkJOProgressionAllowed(JobOrder $jo, $rider) + protected function checkJOProgressionAllowed(EntityManagerInterface $em, JobOrder $jo, &$rider) { + $allowed = true; + + error_log("JO delivery status is " . $jo->getDeliveryStatus() . " (not allowed: " . DeliveryStatus::CANCELLED . ")"); + error_log("JO status is " . $jo->getStatus() . " (not allowed: " . JOStatus::CANCELLED . ")"); + // TODO: add more statuses to block if needed, hence. this is a failsafe in case MQTT is not working. + // check delivery status + switch ($jo->getDeliveryStatus()) + { + case DeliveryStatus::CANCELLED: + $allowed = false; + break; + } + + // check JO status as well switch ($jo->getStatus()) { case JOStatus::CANCELLED: - // if this is the rider's current JO, set to null - if ($rider->getCurrentJobOrder() === $jo) { - $rider->setCurrentJobOrder(); - } - - return new APIResponse(false, 'Job order can no longer be modified.'); + $allowed = false; break; - default: - return true; } + + // if this is the rider's current JO, set to null + if (!$allowed) { + if ($rider->getCurrentJobOrder() === $jo) { + $rider->setCurrentJobOrder(); + $em->persist($rider); + $em->flush(); + } + } + + return $allowed; } protected function debugRequest(Request $req) From 75b2ada03f1a6b0a73988578ac3aef6f886454ac Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 31 May 2024 06:48:44 +0800 Subject: [PATCH 20/49] Add command for processing late pending paymongo transactions #801 --- ...ProcessLatePaymongoTransactionsCommand.php | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/Command/ProcessLatePaymongoTransactionsCommand.php diff --git a/src/Command/ProcessLatePaymongoTransactionsCommand.php b/src/Command/ProcessLatePaymongoTransactionsCommand.php new file mode 100644 index 00000000..6f65dde1 --- /dev/null +++ b/src/Command/ProcessLatePaymongoTransactionsCommand.php @@ -0,0 +1,101 @@ +em = $em; + $this->paymongo = $paymongo; + $this->insurance = $insurance; + + parent::__construct(); + } + + protected function configure() + { + $this->setName('paymongo:checkpending') + ->setDescription('Check for any late PayMongo transactions and process if needed.') + ->setHelp('Check for any late PayMongo transactions and process if needed.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('Fetching all late pending transactions...'); + + // set date threshold to 24 hours ago + $date_threshold = (new DateTime())->modify('-24 hours'); + + $transactions = $this->em->getRepository(GatewayTransaction::class) + ->createQueryBuilder('t') + ->select('t') + ->where('t.status = :status') + ->andWhere('t.date_create <= :date_threshold') + ->setParameter('status', TransactionStatus::PENDING) + ->setParameter('date_threshold', $date_threshold) + ->getQuery() + ->getResult(); + + $output->writeln('Found '. count($transactions) . ' rows matching criteria.'); + + $x = 0; + + foreach ($transactions as $trans) { + // check paymongo status + $checkout = $this->paymongo->getCheckout($trans->getExtTransactionId()); + + if ($checkout['success']) { + // check if we have any payments made + $payments = $checkout['response']['data']['attributes']['payments'] ?? []; + + if (!empty($payments)) { + $amount_paid = 0; + + // for good measure, we get all successful payments and add them up + foreach ($payments as $payment) { + if ($payment['attributes']['status'] === TransactionStatus::PAID) { + $amount_paid = bcadd($amount_paid, $payment['attributes']['amount']); + } + } + + // this transaction is fully paid, so we mark it as paid + if (bccomp($trans->getAmount(), $amount_paid) <= 0) { + $trans->setStatus(TransactionStatus::PAID); + $trans->setDatePay(new DateTime()); + $this->em->flush(); + + $output->writeln('Marked transaction '. $trans->getID() .'as paid.'); + $x++; + } + } else { + $output->writeln('No payments found for transaction: ' . $trans->getID() . ''); + } + } else { + $output->writeln('Checkout not found: ' . $checkout['error']['message'] . ''); + } + } + + $output->writeln('Done! Processed ' . $x . ' rows.'); + + return 0; + } +} From ad841a7e258f4b4813a3cef2360a45508d12e2ec Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 31 May 2024 06:49:55 +0800 Subject: [PATCH 21/49] Prevent success handler from catching failed paymongo payment attempts #801 --- src/Controller/PayMongoController.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Controller/PayMongoController.php b/src/Controller/PayMongoController.php index 21cd864c..af698acd 100644 --- a/src/Controller/PayMongoController.php +++ b/src/Controller/PayMongoController.php @@ -11,6 +11,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use DateTime; + class PayMongoController extends Controller { protected $pm; @@ -45,10 +47,8 @@ class PayMongoController extends Controller switch ($event_name) { case "payment.paid": return $this->handlePaymentPaid($event); - break; case "payment.failed": - return $this->handlePaymentPaid($event); - break; + return $this->handlePaymentFailed($event); case "payment.refunded": // TODO: handle refunds case "payment.refund.updated": case "checkout_session.payment.paid": @@ -69,6 +69,7 @@ class PayMongoController extends Controller if (!empty($obj)) { // mark as paid $obj->setStatus(TransactionStatus::PAID); + $obj->setDatePay(new DateTime()); $this->em->flush(); } @@ -77,7 +78,7 @@ class PayMongoController extends Controller ]); } - protected function handlePaymentFailed(Request $req) + protected function handlePaymentFailed($event) { // TODO: do something about failed payments? return $this->json([ From 952122a39e06948a8871d26d32536784937d668e Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 31 May 2024 06:50:34 +0800 Subject: [PATCH 22/49] Prevent insurance applications from being flagged as paid more than once #801 --- src/EntityListener/GatewayTransactionListener.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/EntityListener/GatewayTransactionListener.php b/src/EntityListener/GatewayTransactionListener.php index c9007aaf..0dd6966d 100644 --- a/src/EntityListener/GatewayTransactionListener.php +++ b/src/EntityListener/GatewayTransactionListener.php @@ -58,17 +58,20 @@ class GatewayTransactionListener 'gateway_transaction' => $gt_obj, ]); - if (!empty($obj)) { + // make sure the object exists and has not been processed yet + if (!empty($obj) && $obj->getStatus === InsuranceApplicationStatus::CREATED) { // mark as paid $obj->setDatePay(new DateTime()); $obj->setStatus(InsuranceApplicationStatus::PAID); $this->em->flush(); - } - // flag on api as paid - $result = $this->ic->tagApplicationPaid($obj->getExtTransactionId()); - if (!$result['success'] || $result['response']['transaction_code'] !== 'GR004') { - error_log("INSURANCE MARK AS PAID FAILED FOR " . $obj->getID() . ": " . $result['error']['message']); + // flag on api as paid + $result = $this->ic->tagApplicationPaid($obj->getExtTransactionId()); + + // something went wrong with insurance api + if (!$result['success'] || $result['response']['transaction_code'] !== 'GR004') { + error_log("INSURANCE MARK AS PAID FAILED FOR " . $obj->getID() . ": " . $result['error']['message']); + } } } } From e97cebd2b214e7eff7430401dcb210aeb7b14579 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 31 May 2024 12:11:13 +0800 Subject: [PATCH 23/49] Fix typo in log message for new command #801 --- src/Command/ProcessLatePaymongoTransactionsCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command/ProcessLatePaymongoTransactionsCommand.php b/src/Command/ProcessLatePaymongoTransactionsCommand.php index 6f65dde1..c09545e5 100644 --- a/src/Command/ProcessLatePaymongoTransactionsCommand.php +++ b/src/Command/ProcessLatePaymongoTransactionsCommand.php @@ -83,7 +83,7 @@ class ProcessLatePaymongoTransactionsCommand extends Command $trans->setDatePay(new DateTime()); $this->em->flush(); - $output->writeln('Marked transaction '. $trans->getID() .'as paid.'); + $output->writeln('Marked transaction '. $trans->getID() . ' as paid.'); $x++; } } else { From 191a02f4c43fc45bf952b5376f3c8352aa391a1f Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 31 May 2024 12:28:43 +0800 Subject: [PATCH 24/49] Fix typo on getStatus call on entity listener #801 --- src/Command/ProcessLatePaymongoTransactionsCommand.php | 2 ++ src/EntityListener/GatewayTransactionListener.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Command/ProcessLatePaymongoTransactionsCommand.php b/src/Command/ProcessLatePaymongoTransactionsCommand.php index c09545e5..5397a7a2 100644 --- a/src/Command/ProcessLatePaymongoTransactionsCommand.php +++ b/src/Command/ProcessLatePaymongoTransactionsCommand.php @@ -85,6 +85,8 @@ class ProcessLatePaymongoTransactionsCommand extends Command $output->writeln('Marked transaction '. $trans->getID() . ' as paid.'); $x++; + } else { + $output->writeln('Insufficient payment amount (' . $amount_paid . '/' . $trans->getAmount() . ') for this transaction: ' . $trans->getID() . ''); } } else { $output->writeln('No payments found for transaction: ' . $trans->getID() . ''); diff --git a/src/EntityListener/GatewayTransactionListener.php b/src/EntityListener/GatewayTransactionListener.php index 0dd6966d..530f0093 100644 --- a/src/EntityListener/GatewayTransactionListener.php +++ b/src/EntityListener/GatewayTransactionListener.php @@ -59,7 +59,7 @@ class GatewayTransactionListener ]); // make sure the object exists and has not been processed yet - if (!empty($obj) && $obj->getStatus === InsuranceApplicationStatus::CREATED) { + if (!empty($obj) && $obj->getStatus() === InsuranceApplicationStatus::CREATED) { // mark as paid $obj->setDatePay(new DateTime()); $obj->setStatus(InsuranceApplicationStatus::PAID); From 3846ad5a43568cea5ee18bcc313c88b542af6e6b Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 31 May 2024 18:45:16 +0800 Subject: [PATCH 25/49] Add checker for paymongo webhook status before running manual processing command #801 --- config/services.yaml | 6 +++++ ...ProcessLatePaymongoTransactionsCommand.php | 26 ++++++++++++++----- src/Service/PayMongoConnector.php | 5 ++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index 00085811..12c32894 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -109,6 +109,12 @@ services: arguments: $callback_url: "%env(WARRANTY_SERIAL_CALLBACK_URL)%" + App\Command\ProcessLatePaymongoTransactionsCommand: + arguments: + $em: "@doctrine.orm.entity_manager" + $paymongo: "@App\\Service\\PayMongoConnector" + $webhook_id: "%env(PAYMONGO_WEBHOOK_ID)%" + # rider tracker service App\Service\RiderTracker: arguments: diff --git a/src/Command/ProcessLatePaymongoTransactionsCommand.php b/src/Command/ProcessLatePaymongoTransactionsCommand.php index 5397a7a2..2c86d3ab 100644 --- a/src/Command/ProcessLatePaymongoTransactionsCommand.php +++ b/src/Command/ProcessLatePaymongoTransactionsCommand.php @@ -10,8 +10,6 @@ use Doctrine\ORM\EntityManagerInterface; use App\Ramcar\TransactionStatus; use App\Entity\GatewayTransaction; - -use App\Service\InsuranceConnector; use App\Service\PayMongoConnector; use DateTime; @@ -20,13 +18,14 @@ class ProcessLatePaymongoTransactionsCommand extends Command { protected $em; protected $paymongo; - protected $insurance; - public function __construct(EntityManagerInterface $em, PayMongoConnector $paymongo, InsuranceConnector $insurance) + protected $webhook_id; + + public function __construct(EntityManagerInterface $em, PayMongoConnector $paymongo, $webhook_id) { $this->em = $em; $this->paymongo = $paymongo; - $this->insurance = $insurance; + $this->webhook_id = $webhook_id; parent::__construct(); } @@ -40,6 +39,21 @@ class ProcessLatePaymongoTransactionsCommand extends Command protected function execute(InputInterface $input, OutputInterface $output) { + $output->writeln('Checking webhook status...'); + + // check if webhook is disabled + $webhook = $this->paymongo->getWebhook($this->webhook_id); + + if ($webhook['success'] && $webhook['response']['data']['attributes']['status'] === 'enabled') { + $output->writeln('Webhook is enabled, no need to do anything.'); + + return 0; + } else { + $output->writeln('Webhook is disabled! Logging event and proceeding...'); + + $this->paymongo->log('WEBHOOK', "[]", json_encode($webhook['response'], JSON_PRETTY_PRINT), 'webhook'); + } + $output->writeln('Fetching all late pending transactions...'); // set date threshold to 24 hours ago @@ -96,7 +110,7 @@ class ProcessLatePaymongoTransactionsCommand extends Command } } - $output->writeln('Done! Processed ' . $x . ' rows.'); + $output->writeln('Done! Processed ' . $x . ' rows.'); return 0; } diff --git a/src/Service/PayMongoConnector.php b/src/Service/PayMongoConnector.php index c34a94e7..25f380ca 100644 --- a/src/Service/PayMongoConnector.php +++ b/src/Service/PayMongoConnector.php @@ -74,6 +74,11 @@ class PayMongoConnector return $this->doRequest('/v1/checkout_sessions/' . $checkout_id, 'GET'); } + public function getWebhook($id) + { + return $this->doRequest('/v1/webhooks/'. $id, 'GET'); + } + protected function generateHash() { return base64_encode($this->secret_key); From 08084f682c32df9d90f74a8ad88ff801a8a466f4 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 31 May 2024 18:51:55 +0800 Subject: [PATCH 26/49] Add force option to enable or disable webhook status checking, defaults to false #801 --- ...ProcessLatePaymongoTransactionsCommand.php | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Command/ProcessLatePaymongoTransactionsCommand.php b/src/Command/ProcessLatePaymongoTransactionsCommand.php index 2c86d3ab..f3bc0312 100644 --- a/src/Command/ProcessLatePaymongoTransactionsCommand.php +++ b/src/Command/ProcessLatePaymongoTransactionsCommand.php @@ -5,6 +5,7 @@ namespace App\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; use Doctrine\ORM\EntityManagerInterface; @@ -34,24 +35,30 @@ class ProcessLatePaymongoTransactionsCommand extends Command { $this->setName('paymongo:checkpending') ->setDescription('Check for any late PayMongo transactions and process if needed.') - ->setHelp('Check for any late PayMongo transactions and process if needed.'); + ->setHelp('Check for any late PayMongo transactions and process if needed.') + ->addOption('force', 'f', InputOption::VALUE_NONE, 'Ignore webhook status and process anyway.'); } protected function execute(InputInterface $input, OutputInterface $output) { - $output->writeln('Checking webhook status...'); + $force = $input->getOption('force'); - // check if webhook is disabled - $webhook = $this->paymongo->getWebhook($this->webhook_id); + // if we aren't forcing, check webhook status first + if (!$force) { + $output->writeln('Checking webhook status...'); - if ($webhook['success'] && $webhook['response']['data']['attributes']['status'] === 'enabled') { - $output->writeln('Webhook is enabled, no need to do anything.'); + // check if webhook is disabled + $webhook = $this->paymongo->getWebhook($this->webhook_id); - return 0; - } else { - $output->writeln('Webhook is disabled! Logging event and proceeding...'); + if ($webhook['success'] && $webhook['response']['data']['attributes']['status'] === 'enabled') { + $output->writeln('Webhook is enabled, no need to do anything.'); - $this->paymongo->log('WEBHOOK', "[]", json_encode($webhook['response'], JSON_PRETTY_PRINT), 'webhook'); + return 0; + } else { + $output->writeln('Webhook is disabled! Logging event and proceeding...'); + + $this->paymongo->log('WEBHOOK', "[]", json_encode($webhook['response'], JSON_PRETTY_PRINT), 'webhook'); + } } $output->writeln('Fetching all late pending transactions...'); From c4b05133865d3556ba7f8b3c8d687951f752b501 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 6 Jun 2024 05:50:36 +0800 Subject: [PATCH 27/49] Attempt to re-enable paymongo webhook if found disabled #801 --- .../ProcessLatePaymongoTransactionsCommand.php | 16 ++++++++++++++-- src/Service/PayMongoConnector.php | 5 +++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Command/ProcessLatePaymongoTransactionsCommand.php b/src/Command/ProcessLatePaymongoTransactionsCommand.php index f3bc0312..8bdc66ca 100644 --- a/src/Command/ProcessLatePaymongoTransactionsCommand.php +++ b/src/Command/ProcessLatePaymongoTransactionsCommand.php @@ -55,9 +55,21 @@ class ProcessLatePaymongoTransactionsCommand extends Command return 0; } else { - $output->writeln('Webhook is disabled! Logging event and proceeding...'); + $output->writeln('Webhook is disabled! Logging event and attempting to re-enable...'); - $this->paymongo->log('WEBHOOK', "[]", json_encode($webhook['response'], JSON_PRETTY_PRINT), 'webhook'); + // attempt re-enabling of webhook + $result = $this->paymongo->enableWebhook($this->webhook_id); + if ($result['success'] && $result['response']['data']['attributes']['status'] ?? null === 'enabled') { + $output->writeln('Webhook ' . $this->webhook_id . ' re-enabled!'); + + // log event + $this->paymongo->log('WEBHOOK RE-ENABLED', "[]", json_encode($result['response'], JSON_PRETTY_PRINT), 'webhook'); + } else { + $output->writeln('Webhook ' . $this->webhook_id . ' could not be re-enabled.'); + + // log event + $this->paymongo->log('WEBHOOK FAILURE', "[]", json_encode($result['response'], JSON_PRETTY_PRINT), 'webhook'); + } } } diff --git a/src/Service/PayMongoConnector.php b/src/Service/PayMongoConnector.php index 25f380ca..3abafc22 100644 --- a/src/Service/PayMongoConnector.php +++ b/src/Service/PayMongoConnector.php @@ -79,6 +79,11 @@ class PayMongoConnector return $this->doRequest('/v1/webhooks/'. $id, 'GET'); } + public function enableWebhook($id) + { + return $this->doRequest('/v1/webhooks/' . $id . '/enable', 'POST'); + } + protected function generateHash() { return base64_encode($this->secret_key); From 10c44fbe64c15f78c986af62a4c89115c3c94f16 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 7 Jun 2024 04:19:08 +0800 Subject: [PATCH 28/49] Limit hub rejection SMS to one per JO/hub combo #800 --- src/Service/HubSelector.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 97a4a8f8..270ec61f 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -645,6 +645,25 @@ class HubSelector protected function sendSMSMessage($hub, $jo_id, $order_date, $service_type, $rejection, $reason = "", $remarks = ""): void { + // 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() + ->select('count(r)') + ->from(JORejection::class, 'r') + ->where('r.job_order = :jo_id') + ->andWhere('r.hub = :hub_id') + ->andWhere('r.id != :rejection_id') + ->setParameter('jo_id', $jo_id) + ->setParameter('hub_id', $hub->getID()) + ->setParameter('rejection_id', $rejection->getID()) + ->getQuery() + ->getSingleScalarResult(); + + // if we already have a rejection record for this hub and JO, do not send another SMS + if ($rejection_count >= 1) { + error_log("ALREADY SENT REJECTION SMS TO HUB " . $hub->getID() . " FOR JO " . $jo_id); + return; + } + $message = 'Job Order #: ' . $jo_id . "\n" . 'Order Date and Time: ' . $order_date->format('d M Y g:i A') . "\n" . 'Date and Time Rejected: ' . $rejection->getDateCreate()->format('d M Y g:i A') . "\n" . From 8c886c4dbfa560e86d975260e0fe8d47d5f089bd Mon Sep 17 00:00:00 2001 From: Korina Cordero Date: Mon, 10 Jun 2024 02:20:28 -0400 Subject: [PATCH 29/49] Add check for null for user for hub rejection. #804 --- src/Controller/ReportController.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Controller/ReportController.php b/src/Controller/ReportController.php index 4f3ed31e..e5309afb 100644 --- a/src/Controller/ReportController.php +++ b/src/Controller/ReportController.php @@ -236,6 +236,10 @@ class ReportController extends Controller $reason = $jor->getReason(); + $dispatched_by = 'system'; + if ($jor->getUser() != null) + $dispatched_by = $jor->getUser()->getFullName(); + $res[] = [ $jo->getID(), $jo->getDateSchedule()->format('m/d/Y H:i'), @@ -244,7 +248,7 @@ class ReportController extends Controller JORejectionReason::getName($jor->getReason()), $jor->getContactPerson(), $jor->getRemarks(), - $jor->getUser()->getFullName(), + $dispatched_by, ServiceType::getName($jo->getServiceType()), ]; } From b4057de938ec3ec6ff78f24437413a7ed2682900 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 25 Jun 2024 03:18:31 +0800 Subject: [PATCH 30/49] Add hub filter exceptions to supported areas #800 --- src/Entity/SupportedArea.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Entity/SupportedArea.php b/src/Entity/SupportedArea.php index 0e176f6a..c9cd52bf 100644 --- a/src/Entity/SupportedArea.php +++ b/src/Entity/SupportedArea.php @@ -39,6 +39,12 @@ class SupportedArea */ protected $coverage_area; + // prevent certain hub filters from being used + /** + * @ORM\Column(type="json", nullable=true) + */ + protected $hub_filter_exceptions; + /** * @ORM\ManyToOne(targetEntity="PriceTier", inversedBy="supported_areas") * @ORM\JoinColumn(name="price_tier_id", referencedColumnName="id", nullable=true) @@ -50,6 +56,7 @@ class SupportedArea $this->date_create = new DateTime(); $this->price_tier = null; + $this->hub_filter_exceptions = []; } public function getID() @@ -65,7 +72,7 @@ class SupportedArea public function getDateCreate() { - return $this->date_Create; + return $this->date_create; } public function setName($name) @@ -101,5 +108,16 @@ class SupportedArea { return $this->price_tier; } + + public function setHubFilterExceptions($exceptions) + { + $this->hub_filter_exceptions = $exceptions; + return $this; + } + + public function getHubFilterExceptions() + { + return $this->hub_filter_exceptions; + } } From 204c039fba687f139f9290f1b6d785a750b22844 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 25 Jun 2024 03:19:10 +0800 Subject: [PATCH 31/49] Refactor hub selector to separate filters into individual classes #800 --- config/services.yaml | 33 + src/Service/HubFilter/BaseHubFilter.php | 123 ++++ .../Filters/DateAndTimeHubFilter.php | 51 ++ .../HubFilter/Filters/InventoryHubFilter.php | 148 +++++ .../HubFilter/Filters/JoTypeHubFilter.php | 44 ++ .../HubFilter/Filters/MaxResultsHubFilter.php | 28 + .../Filters/PaymentMethodHubFilter.php | 55 ++ .../Filters/RiderAvailabilityHubFilter.php | 62 ++ .../HubFilter/Filters/RoundRobinHubFilter.php | 39 ++ src/Service/HubFilter/HubFilterInterface.php | 18 + src/Service/HubSelector.php | 608 ++---------------- 11 files changed, 663 insertions(+), 546 deletions(-) create mode 100644 src/Service/HubFilter/BaseHubFilter.php create mode 100644 src/Service/HubFilter/Filters/DateAndTimeHubFilter.php create mode 100644 src/Service/HubFilter/Filters/InventoryHubFilter.php create mode 100644 src/Service/HubFilter/Filters/JoTypeHubFilter.php create mode 100644 src/Service/HubFilter/Filters/MaxResultsHubFilter.php create mode 100644 src/Service/HubFilter/Filters/PaymentMethodHubFilter.php create mode 100644 src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php create mode 100644 src/Service/HubFilter/Filters/RoundRobinHubFilter.php create mode 100644 src/Service/HubFilter/HubFilterInterface.php diff --git a/config/services.yaml b/config/services.yaml index 12c32894..2bb23c98 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -322,3 +322,36 @@ services: App\Service\PriceTierManager: arguments: $em: "@doctrine.orm.entity_manager" + + # hub filters + App\Service\HubFilter\BaseHubFilter: + arguments: + $hub_filter_logger: "@App\\Service\\HubFilterLogger" + $em: "@doctrine.orm.entity_manager" + $rt: "@App\\Service\\RisingTideGateway" + $trans: "@Symfony\\Contracts\\Translation\\TranslatorInterface" + + App\Service\HubFilter\Filters\DateAndTimeHubFilter: + public: true + + App\Service\HubFilter\Filters\JoTypeHubFilter: + public: true + + App\Service\HubFilter\Filters\MaxResultsHubFilter: + public: true + + App\Service\HubFilter\Filters\PaymentMethodHubFilter: + public: true + + App\Service\HubFilter\Filters\RiderAvailabilityHubFilter: + public: true + + App\Service\HubFilter\Filters\InventoryHubFilter: + public: true + arguments: + $im: "@App\\Service\\InventoryManager" + + App\Service\HubFilter\Filters\RoundRobinHubFilter: + public: true + arguments: + $hub_distributor: "@App\\Service\\HubDistributor" diff --git a/src/Service/HubFilter/BaseHubFilter.php b/src/Service/HubFilter/BaseHubFilter.php new file mode 100644 index 00000000..36b72363 --- /dev/null +++ b/src/Service/HubFilter/BaseHubFilter.php @@ -0,0 +1,123 @@ +hub_filter_logger = $hub_filter_logger; + $this->em = $em; + $this->rt = $rt; + $this->trans = $trans; + } + + 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()); + } + + protected function createRejectionEntry($jo_id, $hub, $remarks = ""): JORejection + { + $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); + + $robj = new JORejection(); + $robj->setDateCreate(new DateTime()) + ->setHub($hub) + ->setJobOrder($jo) + ->setReason(JORejectionReason::NO_STOCK_SALES) + ->setRemarks(implode(" ", ["Automatically filtered by hub selector.", $remarks])); + + $this->em->persist($robj); + $this->em->flush(); + + return $robj; + } + + protected function sendSMSMessage($hub, $jo_id, $order_date, $service_type, $rejection, $reason = "", $remarks = ""): void + { + // 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() + ->select('count(r)') + ->from(JORejection::class, 'r') + ->where('r.job_order = :jo_id') + ->andWhere('r.hub = :hub_id') + ->andWhere('r.id != :rejection_id') + ->setParameter('jo_id', $jo_id) + ->setParameter('hub_id', $hub->getID()) + ->setParameter('rejection_id', $rejection->getID()) + ->getQuery() + ->getSingleScalarResult(); + + // if we already have a rejection record for this hub and JO, do not send another SMS + if ($rejection_count >= 1) { + error_log("ALREADY SENT REJECTION SMS TO HUB " . $hub->getID() . " FOR JO " . $jo_id); + return; + } + + $message = 'Job Order #: ' . $jo_id . "\n" . + 'Order Date and Time: ' . $order_date->format('d M Y g:i A') . "\n" . + 'Date and Time Rejected: ' . $rejection->getDateCreate()->format('d M Y g:i A') . "\n" . + 'Enrollee Name: ' . implode(" - ", [$hub->getName(), $hub->getBranch()]) . "\n" . + 'Reason of Rejection: ' . $reason . "\n" . + 'Remarks: ' . $remarks . "\n" . + 'Type of Service: ' . ServiceType::getName($service_type); + + error_log("SENDING SMS MESsAGE:\r\n" . $message); + + // send SMS message to hub + $this->rt->sendSMS( + $hub->getNotifNumber(), + $this->trans->trans('message.battery_brand_allcaps'), + $message + ); + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php b/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php new file mode 100644 index 00000000..0b25096a --- /dev/null +++ b/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php @@ -0,0 +1,51 @@ +getTimeOpen()->format("H:i:s"); + $time_close = $hub->getTimeClose()->format("H:i:s"); + + $filter_time = $params['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'], + 'jo_count' => 0, + 'inventory' => $hub_data['inventory'], + ]; + } + else + $this->log($hub); + } + + return $results; + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/Filters/InventoryHubFilter.php b/src/Service/HubFilter/Filters/InventoryHubFilter.php new file mode 100644 index 00000000..82be87c5 --- /dev/null +++ b/src/Service/HubFilter/Filters/InventoryHubFilter.php @@ -0,0 +1,148 @@ +im = $im; + } + + public function filter(array $hubs, array $params = []) : array + { + // check if this is enabled + if (!$params['flag_inventory_check']) { + error_log("INVENTORY CHECK " . $this->getJOID() . ": DISABLED"); + return $hubs; + } + + // check customer class + if (!empty($params['customer_class']) && $params['customer_class'] == CustomerClassification::VIP) { + error_log("INVENTORY CHECK " . $this->getJOID() . ": VIP CLASS"); + return $hubs; + } + + // check item list is not empty + if (empty($params['items'])) { + error_log("INVENTORY CHECK " . $this->getJOID() . ": 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 + ) { + error_log("INVENTORY CHECK " . $this->getJOID() . ": INVALID SERVICE TYPE: " . $params['jo_type']); + return $hubs; + } + + // get a list of all hubs with branch codes + $branch_codes = []; + foreach ($hubs as $hub_data) { + $branch_code = $hub_data['hub']->getBranchCode(); + if (!empty($branch_code)) { + $branch_codes[] = $branch_code; + } + }; + + $hubs_to_filter = []; + $results = []; + $qtys = []; + + // call inventory manager for all hubs for selected SKUs + $skus = array_keys($params['items']); + + error_log("CHECKING INVENTORY FOR " . count($skus) . " ITEM(S) ON HUBS " . count($branch_codes) . "..."); + + $branches = $this->im->getBranchesInventory($branch_codes, $skus); + + error_log("REQUEST COMPLETE, RESULT COUNT: " . count($branches)); + + // check each result to see if sufficient quantity exists to meet request + foreach ($branches as $branch) { + if (isset($branch['BranchCode'])) { + // filter out branch if it does not have sufficient inventory + if ($branch['Quantity'] < $params['items'][$branch['SapCode']] && + !isset($hubs_to_filter[$branch['BranchCode']]) + ) { + error_log("FILTERING BRANCH WITH NO INVENTORY: " . $branch['BranchCode']); + $hubs_to_filter[$branch['BranchCode']] = true; + } else { + // save inventory count so we don't have to recheck later + $qtys[$branch['BranchCode']] = $branch['Quantity']; + } + } + } + + // get battery models for each requested SKU + $batteries = []; + foreach ($skus as $sku) { + $bobj = $this->em->getRepository(Battery::class)->findOneBy(['sap_code' => $sku]); + $batteries[] = implode(" ", [$bobj->getModel()->getName(), $bobj->getSize()->getName()]); + } + $battery_string = implode(", ", $batteries); + + // remove filtered hubs from list + foreach ($hubs as $hub_data) { + $hub = $hub_data['hub']; + $branch_code = $hub_data['hub']->getBranchCode(); + + // 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 + if (!empty($jo_id)) { + // create rejection report entry + $robj = $this->createRejectionEntry( + $jo_id, + $hub, + "SKU(s): " . $battery_string + ); + + // build SMS message + $this->sendSMSMessage( + $hub, + $jo_id, + $params['order_date'], + $params['service_type'], + $robj, + JORejectionReason::getName(JORejectionReason::NO_STOCK_SALES), + "Requested SKU(s) - " . $battery_string + ); + } + + // log this filter + $this->log($hub); + + error_log("FILTERED HUB " . $hub->getID() . " (no_inventory)"); + } else { + // include inventory in hub data + $hub_data['inventory'] = $qtys[$branch_code]; + + // we only include branches with branch codes and quantities + $results[] = $hub_data; + } + } + + // return filtered hubs + return $results; + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/Filters/JoTypeHubFilter.php b/src/Service/HubFilter/Filters/JoTypeHubFilter.php new file mode 100644 index 00000000..15ba2952 --- /dev/null +++ b/src/Service/HubFilter/Filters/JoTypeHubFilter.php @@ -0,0 +1,44 @@ + $hub, + 'db_distance' => $hub_data['db_distance'], + 'distance' => $hub_data['distance'], + 'duration' => $hub_data['duration'], + 'jo_count' => 0, + 'inventory' => $hub_data['inventory'], + ]; + else + $this->log($hub); + } + + return $results; + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/Filters/MaxResultsHubFilter.php b/src/Service/HubFilter/Filters/MaxResultsHubFilter.php new file mode 100644 index 00000000..027ab2cd --- /dev/null +++ b/src/Service/HubFilter/Filters/MaxResultsHubFilter.php @@ -0,0 +1,28 @@ +log($hubs[$i]['hub']); + } + + return $results; + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php b/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php new file mode 100644 index 00000000..bade7e65 --- /dev/null +++ b/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php @@ -0,0 +1,55 @@ +getPaymentMethods(); + if ($payment_methods != null) + { + $flag_found_pmethod = false; + foreach ($payment_methods as $pmethod) + { + if ($pmethod == $params['payment_method']) + { + $results[] = [ + 'hub' => $hub, + 'db_distance' => $hub_data['db_distance'], + 'distance' => $hub_data['distance'], + 'duration' => $hub_data['duration'], + 'jo_count' => 0, + 'inventory' => $hub_data['inventory'], + ]; + } + $flag_found_pmethod = true; + } + + if (!$flag_found_pmethod) + $this->log($hub); + } + else + $this->log($hub); + } + + return $results; + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php new file mode 100644 index 00000000..8166a226 --- /dev/null +++ b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php @@ -0,0 +1,62 @@ +getJOID() . ": VIP CLASS"); + return $hubs; + } + + $results = []; + + foreach ($hubs as $hub_data) { + $hub = $hub_data['hub']; + + // check we have available riders + if ($hub->getAvailableRiders() === 0) { + // if we have a JO, create rejection record and notify + if (!empty($jo_id)) { + // create rejection report entry + $robj = $this->createRejectionEntry($jo_id, $hub); + + // build SMS message + $this->sendSMSMessage( + $hub, + $jo_id, + $params['order_date'], + $params['service_type'], + $robj, + JORejectionReason::getName(JORejectionReason::NO_RIDER_AVAILABLE), + ); + } + + // log this filter + $this->log($hub); + + error_log("FILTERED HUB " . $hub->getID() . " (no_available_rider)"); + } else { + $results[] = $hub_data; + } + } + + return $results; + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/Filters/RoundRobinHubFilter.php b/src/Service/HubFilter/Filters/RoundRobinHubFilter.php new file mode 100644 index 00000000..ae0012f4 --- /dev/null +++ b/src/Service/HubFilter/Filters/RoundRobinHubFilter.php @@ -0,0 +1,39 @@ +hub_distributor = $hub_distributor; + } + + public function filter(array $hubs, array $params = []) : array + { + if (!$params['flag_round_robin']) + return $hubs; + + $results = []; + + // call hub distributor service + $arranged_hubs = $this->hub_distributor->arrangeHubs($hubs); + $results = $arranged_hubs; + + return $results; + } +} \ No newline at end of file diff --git a/src/Service/HubFilter/HubFilterInterface.php b/src/Service/HubFilter/HubFilterInterface.php new file mode 100644 index 00000000..fb9dea51 --- /dev/null +++ b/src/Service/HubFilter/HubFilterInterface.php @@ -0,0 +1,18 @@ +container = $container; $this->em = $em; $this->im = $im; $this->hub_distributor = $hub_distributor; @@ -45,6 +39,20 @@ class HubSelector $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(); @@ -66,461 +74,63 @@ class HubSelector $order_date = $criteria->getOrderDate(); $service_type = $criteria->getServiceType(); - $results = []; - // 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); + + // gather all params in one array + // TODO: figure out a better way to do this where we don't have to specify the filter names here + $params = [ + 'date_and_time' => [ + 'date_time' => $date_time, + ], + 'no_inventory' => [ + 'flag_inventory_check' => $flag_inventory_check, + 'customer_class' => $customer_class, + 'jo_type' => $jo_type, + 'order_date' => $order_date, + 'service_type' => $service_type, + 'items' => $items, + ], + 'job_order_type' => [ + 'flag_emergency' => $flag_emergency, + 'jo_type' => $jo_type, + ], + 'max_results' => [ + 'limit_results' => $limit_results, + ], + 'no_payment_method' => [ + 'flag_emergency' => $flag_emergency, + 'payment_method' => $payment_method, + ], + 'no_available_rider' => [ + 'flag_riders_check' => $flag_riders_check, + 'customer_class' => $customer_class, + 'order_date' => $order_date, + 'service_type' => $service_type, + ], + 'round_robin' => [ + 'flag_round_robin' => $flag_round_robin, + ], + ]; - // error_log('closest hubs ' . json_encode($filtered_hubs)); + // loop through all enabled filters + foreach ($this->getActiveFilters() as $hub_filter) { + $f = $this->container->get($hub_filter); + $f->setJOID($jo_id); + $f->setCustomerID($customer_id); - // filter the first hub results for date and opening times - $hubs_date_time = $this->filterHubsByDateAndTime($filtered_hubs, $date_time, $jo_id, $customer_id); - $filtered_hubs = $hubs_date_time; + $filtered_hubs = $f->filter($filtered_hubs, $params[$f->getID()]); - // error_log('date_time hubs ' . json_encode($filtered_hubs)); - - // TODO: allow toggling of each filter individually for specific conditions (e.g. customer class, emergency) - - if (!$flag_emergency) - { - // filter jo types - $hubs_jo_type = $this->filterHubsByJoType($filtered_hubs, $jo_type, $jo_id, $customer_id); - $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, $jo_id, $customer_id); - $filtered_hubs = $hubs_payment_method; - - // error_log('payment hubs ' . json_encode($filtered_hubs)); + // error_log($f->getID() . ' hubs ' . json_encode($filtered_hubs)); } - // only enable rider and inventory checks if not VIP - if (!empty($customer_class) && $customer_class !== CustomerClassification::VIP) { - // available riders filter - $hubs_riders = $this->filterHubsByRiderAvailability($filtered_hubs, $flag_riders_check, $jo_id, $customer_id, $order_date, $service_type); - $filtered_hubs = $hubs_riders; - - // error_log('available riders hubs ' . json_encode($filtered_hubs)); - - // inventory filter - $hubs_inventory = $this->filterHubsByInventory($filtered_hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id, $order_date, $service_type); - $filtered_hubs = $hubs_inventory; - - // error_log('inventory hubs ' . json_encode($filtered_hubs)); - } - - if (!$flag_emergency) { - // 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, $jo_id, $customer_id); - $filtered_hubs = $hubs_max_result; - } - - $results = $filtered_hubs; - // error_log('final hub list ' . json_encode($filtered_hubs)); - 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, $jo_id, $customer_id) - { - 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', $jo_id, $customer_id); - } - - return $results; - } - - protected function filterHubsByJoType($hubs, $jo_type, $jo_id, $customer_id) - { - 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'], - 'jo_count' => 0, - 'inventory' => $hub_data['inventory'], - ]; - else - $this->hub_filter_logger->logFilteredHub($hub, 'job_order_type', $jo_id, $customer_id); - } - - return $results; - } - - protected function filterHubsByPaymentMethod($hubs, $payment_method, $jo_id, $customer_id) - { - 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'], - 'jo_count' => 0, - 'inventory' => $hub_data['inventory'], - ]; - } - $flag_found_pmethod = true; - } - - if (!$flag_found_pmethod) - $this->hub_filter_logger->logFilteredHub($hub, 'no_payment_method', $jo_id, $customer_id); - } - else - $this->hub_filter_logger->logFilteredHub($hub, 'no_payment_method', $jo_id, $customer_id); - } - - return $results; - } - - protected function filterHubsByDateAndTime($hubs, $date_time, $jo_id, $customer_id) - { - 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'], - 'jo_count' => 0, - 'inventory' => $hub_data['inventory'], - ]; - } - else - $this->hub_filter_logger->logFilteredHub($hub, 'date_and_time', $jo_id, $customer_id); - } - - return $results; - } - - protected function filterHubsByInventory($hubs, $flag_inventory_check, $jo_type, $items, $jo_id, $customer_id, $order_date, $service_type) - { - // check if this is enabled - if (!$flag_inventory_check) { - error_log("INVENTORY CHECK " . $jo_id . ": DISABLED"); - return $hubs; - } - - // check hub list is not empty - if (empty($hubs)) { - error_log("INVENTORY CHECK " . $jo_id . ": NO HUBS"); - return $hubs; - } - - // check item list is not empty - if (empty($items)) { - error_log("INVENTORY CHECK " . $jo_id . ": NO ITEMS"); - return $hubs; - } - - // check this is a battery item related JO - if ($jo_type != ServiceType::BATTERY_REPLACEMENT_NEW && - $jo_type != ServiceType::BATTERY_REPLACEMENT_WARRANTY - ) { - error_log("INVENTORY CHECK " . $jo_id . ": INVALID SERVICE TYPE: " . $jo_type); - return $hubs; - } - - // get a list of all hubs with branch codes - $branch_codes = []; - foreach ($hubs as $hub_data) { - $branch_code = $hub_data['hub']->getBranchCode(); - if (!empty($branch_code)) { - $branch_codes[] = $branch_code; - } - }; - - $hubs_to_filter = []; - $results = []; - $qtys = []; - - // call inventory manager for all hubs for selected SKUs - $skus = array_keys($items); - - error_log("CHECKING INVENTORY FOR " . count($skus) . " ITEM(S) ON HUBS " . count($branch_codes) . "..."); - - $branches = $this->im->getBranchesInventory($branch_codes, $skus); - - error_log("REQUEST COMPLETE, RESULT COUNT: " . count($branches)); - - // check each result to see if sufficient quantity exists to meet request - foreach ($branches as $branch) { - if (isset($branch['BranchCode'])) { - // filter out branch if it does not have sufficient inventory - if ($branch['Quantity'] < $items[$branch['SapCode']] && - !isset($hubs_to_filter[$branch['BranchCode']]) - ) { - error_log("FILTERING BRANCH WITH NO INVENTORY: " . $branch['BranchCode']); - $hubs_to_filter[$branch['BranchCode']] = true; - } else { - // save inventory count so we don't have to recheck later - $qtys[$branch['BranchCode']] = $branch['Quantity']; - } - } - } - - // get battery models for each requested SKU - $batteries = []; - foreach ($skus as $sku) { - $bobj = $this->em->getRepository(Battery::class)->findOneBy(['sap_code' => $sku]); - $batteries[] = implode(" ", [$bobj->getModel()->getName(), $bobj->getSize()->getName()]); - } - $battery_string = implode(", ", $batteries); - - // remove filtered hubs from list - foreach ($hubs as $hub_data) { - $hub_obj = $hub_data['hub']; - $branch_code = $hub_data['hub']->getBranchCode(); - - // check if we are filtering this hub - if (isset($hubs_to_filter[$branch_code])) { - // if we have a JO, create rejection record and notify - if (!empty($jo_id)) { - // create rejection report entry - $robj = $this->createRejectionEntry( - $jo_id, - $hub_obj, - "SKU(s): " . $battery_string - ); - - // build SMS message - $this->sendSMSMessage( - $hub_obj, - $jo_id, - $order_date, - $service_type, - $robj, - JORejectionReason::getName(JORejectionReason::NO_STOCK_SALES), - "Requested SKU(s) - " . $battery_string - ); - } - - // log this filter - $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_inventory', $jo_id, $customer_id); - - error_log("FILTERED HUB " . $hub_obj->getID() . " (no_inventory)"); - } else if (!empty($branch_code) && isset($qtys[$branch_code])) { - // include inventory in hub data - $hub_data['inventory'] = $qtys[$branch_code]; - - // we only include branches with branch codes and quantities - $results[] = $hub_data; - } - } - - // return filtered hubs - return $results; - - // NOTE: leaving the old code here for posterity, for now - /* - 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'], - 'jo_count' => 0, - ]; - 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', $jo_id, $customer_id); - } - } - 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'], - 'jo_count' => 0, - ]; - 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', $jo_id, $customer_id); - } - } - } - } - - return $results; - */ - } - - protected function filterHubsByRiderAvailability($hubs, $flag_riders_check, $jo_id, $customer_id, $order_date, $service_type) - { - // check if this is enabled - if (!$flag_riders_check) { - return $hubs; - } - - // check hub list is not empty - if (empty($hubs)) { - return $hubs; - } - - $results = []; - - foreach ($hubs as $hub_data) { - $hub_obj = $hub_data['hub']; - - // check we have available riders - if ($hub_obj->getAvailableRiders() === 0) { - // if we have a JO, create rejection record and notify - if (!empty($jo_id)) { - // create rejection report entry - $robj = $this->createRejectionEntry($jo_id, $hub_obj); - - // build SMS message - $this->sendSMSMessage( - $hub_obj, - $jo_id, - $order_date, - $service_type, - $robj, - JORejectionReason::getName(JORejectionReason::NO_RIDER_AVAILABLE), - ); - } - - // log this filter - $this->hub_filter_logger->logFilteredHub($hub_obj, 'no_available_rider', $jo_id, $customer_id); - - error_log("FILTERED HUB " . $hub_obj->getID() . " (no_available_rider)"); - } else { - $results[] = $hub_data; - } - } - - return $results; + return $filtered_hubs; } protected function getClosestHubs(Point $point, $limit_distance, $jo_id, $customer_id) @@ -573,44 +183,6 @@ class HubSelector return $hubs_data; } - protected function checkInventory($items, $branch_codes) - { - // check if hub has all items - $skus = []; - $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) { @@ -625,61 +197,5 @@ class HubSelector return round(($miles * 1.609344), 1); } - - protected function createRejectionEntry($jo_id, $hub, $remarks = ""): JORejection - { - $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); - - $robj = new JORejection(); - $robj->setDateCreate(new DateTime()) - ->setHub($hub) - ->setJobOrder($jo) - ->setReason(JORejectionReason::NO_STOCK_SALES) - ->setRemarks(implode(" ", ["Automatically filtered by hub selector.", $remarks])); - - $this->em->persist($robj); - $this->em->flush(); - - return $robj; - } - - protected function sendSMSMessage($hub, $jo_id, $order_date, $service_type, $rejection, $reason = "", $remarks = ""): void - { - // 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() - ->select('count(r)') - ->from(JORejection::class, 'r') - ->where('r.job_order = :jo_id') - ->andWhere('r.hub = :hub_id') - ->andWhere('r.id != :rejection_id') - ->setParameter('jo_id', $jo_id) - ->setParameter('hub_id', $hub->getID()) - ->setParameter('rejection_id', $rejection->getID()) - ->getQuery() - ->getSingleScalarResult(); - - // if we already have a rejection record for this hub and JO, do not send another SMS - if ($rejection_count >= 1) { - error_log("ALREADY SENT REJECTION SMS TO HUB " . $hub->getID() . " FOR JO " . $jo_id); - return; - } - - $message = 'Job Order #: ' . $jo_id . "\n" . - 'Order Date and Time: ' . $order_date->format('d M Y g:i A') . "\n" . - 'Date and Time Rejected: ' . $rejection->getDateCreate()->format('d M Y g:i A') . "\n" . - 'Enrollee Name: ' . implode(" - ", [$hub->getName(), $hub->getBranch()]) . "\n" . - 'Reason of Rejection: ' . $reason . "\n" . - 'Remarks: ' . $remarks . "\n" . - 'Type of Service: ' . ServiceType::getName($service_type); - - error_log("SENDING SMS MESsAGE:\r\n" . $message); - - // send SMS message to hub - $this->rt->sendSMS( - $hub->getNotifNumber(), - $this->trans->trans('message.battery_brand_allcaps'), - $message - ); - } } From 3b287236ecbc20bb2e1c1959ade35c5b69101de1 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 25 Jun 2024 03:38:56 +0800 Subject: [PATCH 32/49] Add regional filter support for hub filters #800 --- src/Service/HubSelector.php | 36 ++++++++++++++++++- .../hub_filter_exceptions.sql | 2 ++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 utils/hub_filter_exceptions/hub_filter_exceptions.sql diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 8de3c718..929d7bf5 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -80,7 +80,7 @@ class HubSelector // get all the hubs within distance $filtered_hubs = $this->getClosestHubs($point, $limit_distance, $jo_id, $customer_id); - + // gather all params in one array // TODO: figure out a better way to do this where we don't have to specify the filter names here $params = [ @@ -120,6 +120,12 @@ class HubSelector // loop through all enabled filters foreach ($this->getActiveFilters() as $hub_filter) { $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); @@ -197,5 +203,33 @@ class HubSelector 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; + } } diff --git a/utils/hub_filter_exceptions/hub_filter_exceptions.sql b/utils/hub_filter_exceptions/hub_filter_exceptions.sql new file mode 100644 index 00000000..ef165614 --- /dev/null +++ b/utils/hub_filter_exceptions/hub_filter_exceptions.sql @@ -0,0 +1,2 @@ +update supported_area set hub_filter_exceptions = '{"no_inventory":true,"no_available_rider":true}' where id = 34; +update supported_area set hub_filter_exceptions = '{"no_inventory":true,"no_available_rider":true}' where id = 35; \ No newline at end of file From a21ed6649009bf71d128d354829f838a2be2ebcb Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 26 Jun 2024 09:59:29 +0800 Subject: [PATCH 33/49] Move requested params to filters instead of hub selector #800 --- .../Filters/DateAndTimeHubFilter.php | 7 +++ .../HubFilter/Filters/InventoryHubFilter.php | 12 +++++ .../HubFilter/Filters/JoTypeHubFilter.php | 8 +++ .../HubFilter/Filters/MaxResultsHubFilter.php | 7 +++ .../Filters/PaymentMethodHubFilter.php | 8 +++ .../Filters/RiderAvailabilityHubFilter.php | 10 ++++ .../HubFilter/Filters/RoundRobinHubFilter.php | 7 +++ src/Service/HubFilter/HubFilterInterface.php | 6 ++- src/Service/HubSelector.php | 52 +++++++------------ 9 files changed, 81 insertions(+), 36 deletions(-) diff --git a/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php b/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php index 0b25096a..67097320 100644 --- a/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php +++ b/src/Service/HubFilter/Filters/DateAndTimeHubFilter.php @@ -9,6 +9,13 @@ class DateAndTimeHubFilter extends BaseHubFilter implements HubFilterInterface { protected $id = 'date_and_time'; + public function getRequestedParams() : array + { + return [ + 'date_time', + ]; + } + public function filter(array $hubs, array $params = []) : array { if ($params['date_time'] == null) diff --git a/src/Service/HubFilter/Filters/InventoryHubFilter.php b/src/Service/HubFilter/Filters/InventoryHubFilter.php index 82be87c5..9bcc883c 100644 --- a/src/Service/HubFilter/Filters/InventoryHubFilter.php +++ b/src/Service/HubFilter/Filters/InventoryHubFilter.php @@ -27,6 +27,18 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface $this->im = $im; } + public function getRequestedParams() : array + { + return [ + 'flag_inventory_check', + 'customer_class', + 'jo_type', + 'order_date', + 'service_type', + 'items', + ]; + } + public function filter(array $hubs, array $params = []) : array { // check if this is enabled diff --git a/src/Service/HubFilter/Filters/JoTypeHubFilter.php b/src/Service/HubFilter/Filters/JoTypeHubFilter.php index 15ba2952..01ed733f 100644 --- a/src/Service/HubFilter/Filters/JoTypeHubFilter.php +++ b/src/Service/HubFilter/Filters/JoTypeHubFilter.php @@ -9,6 +9,14 @@ class JoTypeHubFilter extends BaseHubFilter implements HubFilterInterface { protected $id = 'job_order_type'; + public function getRequestedParams() : array + { + return [ + 'flag_emergency', + 'jo_type', + ]; + } + public function filter(array $hubs, array $params = []) : array { if ($params['flag_emergency']) diff --git a/src/Service/HubFilter/Filters/MaxResultsHubFilter.php b/src/Service/HubFilter/Filters/MaxResultsHubFilter.php index 027ab2cd..16c1b514 100644 --- a/src/Service/HubFilter/Filters/MaxResultsHubFilter.php +++ b/src/Service/HubFilter/Filters/MaxResultsHubFilter.php @@ -9,6 +9,13 @@ class MaxResultsHubFilter extends BaseHubFilter implements HubFilterInterface { protected $id = 'max_results'; + public function getRequestedParams() : array + { + return [ + 'limit_results', + ]; + } + public function filter(array $hubs, array $params = []) : array { if (empty($params['limit_results'])) diff --git a/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php b/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php index bade7e65..8af6705b 100644 --- a/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php +++ b/src/Service/HubFilter/Filters/PaymentMethodHubFilter.php @@ -9,6 +9,14 @@ class PaymentMethodHubFilter extends BaseHubFilter implements HubFilterInterface { protected $id = 'no_payment_method'; + public function getRequestedParams() : array + { + return [ + 'flag_emergency', + 'payment_method', + ]; + } + public function filter(array $hubs, array $params = []) : array { if ($params['flag_emergency']) diff --git a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php index 8166a226..0e46e50b 100644 --- a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php +++ b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php @@ -12,6 +12,16 @@ class RiderAvailabilityHubFilter extends BaseHubFilter implements HubFilterInter { protected $id = 'no_available_rider'; + public function getRequestedParams() : array + { + return [ + 'flag_riders_check', + 'customer_class', + 'order_date', + 'service_type', + ]; + } + public function filter(array $hubs, array $params = []) : array { // check if this is enabled diff --git a/src/Service/HubFilter/Filters/RoundRobinHubFilter.php b/src/Service/HubFilter/Filters/RoundRobinHubFilter.php index ae0012f4..235293a9 100644 --- a/src/Service/HubFilter/Filters/RoundRobinHubFilter.php +++ b/src/Service/HubFilter/Filters/RoundRobinHubFilter.php @@ -23,6 +23,13 @@ class RoundRobinHubFilter extends BaseHubFilter implements HubFilterInterface $this->hub_distributor = $hub_distributor; } + public function getRequestedParams() : array + { + return [ + 'flag_round_robin', + ]; + } + public function filter(array $hubs, array $params = []) : array { if (!$params['flag_round_robin']) diff --git a/src/Service/HubFilter/HubFilterInterface.php b/src/Service/HubFilter/HubFilterInterface.php index fb9dea51..f3b9d2ab 100644 --- a/src/Service/HubFilter/HubFilterInterface.php +++ b/src/Service/HubFilter/HubFilterInterface.php @@ -10,9 +10,11 @@ interface HubFilterInterface public function setJOID(int $jo_id); - public function getJOID(): int; + public function getJOID() : int; public function setCustomerID(int $customer_id); - public function getCustomerID(): int; + public function getCustomerID() : int; + + public function getRequestedParams() : array; } \ No newline at end of file diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 929d7bf5..fa1fb0b6 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -81,40 +81,20 @@ class HubSelector // get all the hubs within distance $filtered_hubs = $this->getClosestHubs($point, $limit_distance, $jo_id, $customer_id); - // gather all params in one array - // TODO: figure out a better way to do this where we don't have to specify the filter names here + // build param list $params = [ - 'date_and_time' => [ - 'date_time' => $date_time, - ], - 'no_inventory' => [ - 'flag_inventory_check' => $flag_inventory_check, - 'customer_class' => $customer_class, - 'jo_type' => $jo_type, - 'order_date' => $order_date, - 'service_type' => $service_type, - 'items' => $items, - ], - 'job_order_type' => [ - 'flag_emergency' => $flag_emergency, - 'jo_type' => $jo_type, - ], - 'max_results' => [ - 'limit_results' => $limit_results, - ], - 'no_payment_method' => [ - 'flag_emergency' => $flag_emergency, - 'payment_method' => $payment_method, - ], - 'no_available_rider' => [ - 'flag_riders_check' => $flag_riders_check, - 'customer_class' => $customer_class, - 'order_date' => $order_date, - 'service_type' => $service_type, - ], - 'round_robin' => [ - 'flag_round_robin' => $flag_round_robin, - ], + '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 @@ -129,7 +109,11 @@ class HubSelector $f->setJOID($jo_id); $f->setCustomerID($customer_id); - $filtered_hubs = $f->filter($filtered_hubs, $params[$f->getID()]); + // 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)); } From 44b3753ecea09a4e4f858537cf4656a886e0ed25 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 26 Jun 2024 10:02:04 +0800 Subject: [PATCH 34/49] Skip hub filtering if hub list is alraedy empty #800 --- src/Service/HubSelector.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index fa1fb0b6..89265c66 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -99,6 +99,11 @@ class HubSelector // 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 From e8b14ff379ab0ee8a311020b25deaa238395a11d Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 27 Jun 2024 10:58:11 +0800 Subject: [PATCH 35/49] Fix available rider filter to also consider flag_active and current_job_order #800 --- src/Entity/Hub.php | 4 +++- .../HubFilter/Filters/RiderAvailabilityHubFilter.php | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Entity/Hub.php b/src/Entity/Hub.php index da3a44b9..7fba62c4 100644 --- a/src/Entity/Hub.php +++ b/src/Entity/Hub.php @@ -114,7 +114,9 @@ class Hub public function getAvailableRiders() { $crit = Criteria::create(); - $crit->where(Criteria::expr()->eq('flag_available', true)); + $crit->where(Criteria::expr()->eq('flag_available', true)) + ->where(Criteria::expr()->eq('flag_active', true)) + ->where(Criteria::expr()->eq('current_job_order', null)); return $this->riders->matching($crit); } diff --git a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php index 0e46e50b..fadafa2a 100644 --- a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php +++ b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php @@ -31,7 +31,7 @@ class RiderAvailabilityHubFilter extends BaseHubFilter implements HubFilterInter // check customer class if (!empty($params['customer_class']) && $params['customer_class'] == CustomerClassification::VIP) { - error_log("INVENTORY CHECK " . $this->getJOID() . ": VIP CLASS"); + error_log("RIDER CHECK " . $this->getJOID() . ": VIP CLASS"); return $hubs; } @@ -40,8 +40,10 @@ class RiderAvailabilityHubFilter extends BaseHubFilter implements HubFilterInter foreach ($hubs as $hub_data) { $hub = $hub_data['hub']; + $available_riders = count($hub->getAvailableRiders()); // check we have available riders - if ($hub->getAvailableRiders() === 0) { + error_log("TOTAL RIDERS: " . $available_riders); + if ($available_riders === 0) { // if we have a JO, create rejection record and notify if (!empty($jo_id)) { // create rejection report entry From bff1f7ff73169375aaa54ee8e74ef7f8d12ee3be Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 27 Jun 2024 11:31:02 +0800 Subject: [PATCH 36/49] Add null check to battery inventory entries from motiv #800 --- src/Service/HubFilter/Filters/InventoryHubFilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/HubFilter/Filters/InventoryHubFilter.php b/src/Service/HubFilter/Filters/InventoryHubFilter.php index 9bcc883c..beef4199 100644 --- a/src/Service/HubFilter/Filters/InventoryHubFilter.php +++ b/src/Service/HubFilter/Filters/InventoryHubFilter.php @@ -93,7 +93,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 ($branch['Quantity'] < $params['items'][$branch['SapCode']] && + if (!isset($params['items'][$branch['SapCode']]) || $branch['Quantity'] < $params['items'][$branch['SapCode']] && !isset($hubs_to_filter[$branch['BranchCode']]) ) { error_log("FILTERING BRANCH WITH NO INVENTORY: " . $branch['BranchCode']); From 57b4227da93f275ea6a8f753f207c2a33cfb39b5 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Sun, 30 Jun 2024 11:47:23 +0800 Subject: [PATCH 37/49] Add more requested JO rejection reasons #800 --- src/Ramcar/JORejectionReason.php | 36 ++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/Ramcar/JORejectionReason.php b/src/Ramcar/JORejectionReason.php index cedaf60a..37d30c44 100644 --- a/src/Ramcar/JORejectionReason.php +++ b/src/Ramcar/JORejectionReason.php @@ -4,17 +4,24 @@ namespace App\Ramcar; class JORejectionReason extends NameValue { - const ADMINISTRATIVE = 'administrative'; - const NO_STOCK_SALES = 'no_stock_sales'; - const NO_STOCK_SERVICE = 'no_stock_service'; - const LINE_NO_ANSWER = 'line_no_answer'; - const LINE_BUSY = 'line_busy'; - const NO_RIDER_AVAILABLE = 'no_rider_available'; - const NO_RIDER_IN_TRANSIT = 'no_rider_in_transit'; - const REFUSAL = 'refusal'; - const STORE_CLOSED = 'store_closed'; - const NO_CREDIT_CARD = 'no_credit_card'; - const DISCOUNT = 'discount'; + const ADMINISTRATIVE = 'administrative'; + const NO_STOCK_SALES = 'no_stock_sales'; + const NO_STOCK_SERVICE = 'no_stock_service'; + const LINE_NO_ANSWER = 'line_no_answer'; + const LINE_BUSY = 'line_busy'; + const NO_RIDER_AVAILABLE = 'no_rider_available'; + const NO_RIDER_IN_TRANSIT = 'no_rider_in_transit'; + const REFUSAL = 'refusal'; + const STORE_CLOSED = 'store_closed'; + const NO_CREDIT_CARD = 'no_credit_card'; + const DISCOUNT = 'discount'; + const STORE_CLOSED_SCHEDULED = 'store_closed_scheduled'; + const STORE_CLOSED_HALF_DAY = 'store_closed_half_day'; + const STORE_CLOSED_NO_ADVISE = 'store_closed_no_advise'; + const PRIORITY_HUB_WTY_CLAIM = 'priority_hub_wty_claim'; + const PRIORITY_HUB_JUMPSTART = 'priority_hub_jumpstart'; + const PRIORITY_HUB_RESQ_REQUEST = 'priority_hub_resq_req'; + const CUSTOMER_REQUEST = 'customer_request'; const COLLECTION = [ 'administrative' => 'ADMINISTRATIVE', @@ -28,6 +35,13 @@ class JORejectionReason extends NameValue 'store_closed' => 'STORE CLOSED', 'no_credit_card' => 'NO CREDIT CARD PAYMENT / NO TERMINAL', 'discount' => 'DISCOUNT', + 'store_closed_scheduled' => 'STORE CLOSED (ON SCHEDULE)', + 'store_closed_half_day' => 'STORE CLOSED (HALF DAY)', + 'store_closed_no_advise' => 'STORE CLOSED (NO ADVISE)', + 'priority_hub_wty_claim' => 'PRIORITY HUB OUTLET FOR WARRANTY CLAIM', + 'priority_hub_jumpstart' => 'PRIORITY HUB OUTLET FOR JUMPSTART', + 'priority_hub_resq_req' => 'PRIORITY HUB OUTLET FOR RESQ-Q REQUEST', + 'customer_request' => 'CUSTOMER REQUEST', ]; const BLACKLIST = [ From 8992fdeec0af1e5fb928a08d8d4a0884c5c4e16c Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 3 Jul 2024 13:56:27 +0800 Subject: [PATCH 38/49] Add hub rejection log file #800 --- src/Service/HubFilter/BaseHubFilter.php | 19 +++++++++++++++++-- .../HubFilter/Filters/InventoryHubFilter.php | 3 ++- .../Filters/RiderAvailabilityHubFilter.php | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Service/HubFilter/BaseHubFilter.php b/src/Service/HubFilter/BaseHubFilter.php index 36b72363..44ff4c2f 100644 --- a/src/Service/HubFilter/BaseHubFilter.php +++ b/src/Service/HubFilter/BaseHubFilter.php @@ -65,7 +65,7 @@ class BaseHubFilter $this->hub_filter_logger->logFilteredHub($hub, $this->getID(), $this->getJOID(), $this->getCustomerID()); } - protected function createRejectionEntry($jo_id, $hub, $remarks = ""): JORejection + protected function createRejectionEntry($jo_id, $hub, $reason, $remarks = ""): JORejection { $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); @@ -73,12 +73,27 @@ class BaseHubFilter $robj->setDateCreate(new DateTime()) ->setHub($hub) ->setJobOrder($jo) - ->setReason(JORejectionReason::NO_STOCK_SALES) + ->setReason($reason) ->setRemarks(implode(" ", ["Automatically filtered by hub selector.", $remarks])); $this->em->persist($robj); $this->em->flush(); + // log to file + $filename = '/../../var/log/hub_rejection.log'; + $date = date("Y-m-d H:i:s"); + + // build log entry + $entry = implode("", [ + "[JO: " . $jo_id . "]", + "[HUB:" . $hub->getID() . "]", + "[" . $date . "]", + $reason, + $remarks ? " - " . $remarks : "", + ]); + + @file_put_contents(__DIR__ . $filename, $entry, FILE_APPEND); + return $robj; } diff --git a/src/Service/HubFilter/Filters/InventoryHubFilter.php b/src/Service/HubFilter/Filters/InventoryHubFilter.php index beef4199..2a23e346 100644 --- a/src/Service/HubFilter/Filters/InventoryHubFilter.php +++ b/src/Service/HubFilter/Filters/InventoryHubFilter.php @@ -126,7 +126,8 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface $robj = $this->createRejectionEntry( $jo_id, $hub, - "SKU(s): " . $battery_string + JORejectionReason::NO_STOCK_SALES, + "SKU(s): " . $battery_string, ); // build SMS message diff --git a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php index fadafa2a..e969dcb2 100644 --- a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php +++ b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php @@ -47,7 +47,7 @@ class RiderAvailabilityHubFilter extends BaseHubFilter implements HubFilterInter // if we have a JO, create rejection record and notify if (!empty($jo_id)) { // create rejection report entry - $robj = $this->createRejectionEntry($jo_id, $hub); + $robj = $this->createRejectionEntry($jo_id, $hub, JORejectionReason::NO_RIDER_AVAILABLE); // build SMS message $this->sendSMSMessage( From a335f84a13625c906d08b0b2cd3914561d51338d Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 3 Jul 2024 14:01:16 +0800 Subject: [PATCH 39/49] Fix hub filter log location #800 --- src/Service/HubFilter/BaseHubFilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/HubFilter/BaseHubFilter.php b/src/Service/HubFilter/BaseHubFilter.php index 44ff4c2f..81d1f87c 100644 --- a/src/Service/HubFilter/BaseHubFilter.php +++ b/src/Service/HubFilter/BaseHubFilter.php @@ -80,7 +80,7 @@ class BaseHubFilter $this->em->flush(); // log to file - $filename = '/../../var/log/hub_rejection.log'; + $filename = '/../../../var/log/hub_rejection.log'; $date = date("Y-m-d H:i:s"); // build log entry From 9b6313995731c3044600f75be64a22a0c5fb1838 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 3 Jul 2024 23:14:38 +0800 Subject: [PATCH 40/49] Fix file logging format for hub filtering #800 --- src/Service/HubFilter/BaseHubFilter.php | 40 ++++++++++--------- .../HubFilter/Filters/InventoryHubFilter.php | 4 +- .../Filters/RiderAvailabilityHubFilter.php | 5 ++- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/Service/HubFilter/BaseHubFilter.php b/src/Service/HubFilter/BaseHubFilter.php index 81d1f87c..3ca4bf85 100644 --- a/src/Service/HubFilter/BaseHubFilter.php +++ b/src/Service/HubFilter/BaseHubFilter.php @@ -63,11 +63,26 @@ class BaseHubFilter public function log(Hub $hub): void { $this->hub_filter_logger->logFilteredHub($hub, $this->getID(), $this->getJOID(), $this->getCustomerID()); + + // log to file + $filename = '/../../../var/log/hub_rejection.log'; + $date = date("Y-m-d H:i:s"); + + // build log entry + $entry = implode("", [ + "[JO: " . $this->getJOID() . "]", + "[" . $date . "]", + "[" . $this->getID() . "]", + " " . $hub->getName() . " (ID: " . $hub->getID() . ")", + "\r\n", + ]); + + @file_put_contents(__DIR__ . $filename, $entry, FILE_APPEND); } - protected function createRejectionEntry($jo_id, $hub, $reason, $remarks = ""): JORejection + protected function createRejectionEntry($hub, $reason, $remarks = ""): JORejection { - $jo = $this->em->getRepository(JobOrder::class)->find($jo_id); + $jo = $this->em->getRepository(JobOrder::class)->find($this->getJOID()); $robj = new JORejection(); $robj->setDateCreate(new DateTime()) @@ -79,26 +94,13 @@ class BaseHubFilter $this->em->persist($robj); $this->em->flush(); - // log to file - $filename = '/../../../var/log/hub_rejection.log'; - $date = date("Y-m-d H:i:s"); - - // build log entry - $entry = implode("", [ - "[JO: " . $jo_id . "]", - "[HUB:" . $hub->getID() . "]", - "[" . $date . "]", - $reason, - $remarks ? " - " . $remarks : "", - ]); - - @file_put_contents(__DIR__ . $filename, $entry, FILE_APPEND); - return $robj; } - protected function sendSMSMessage($hub, $jo_id, $order_date, $service_type, $rejection, $reason = "", $remarks = ""): void + protected function sendSMSMessage($hub, $order_date, $service_type, $rejection, $reason = "", $remarks = ""): void { + $jo_id = $this->getJOID(); + // 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() ->select('count(r)') @@ -126,7 +128,7 @@ class BaseHubFilter 'Remarks: ' . $remarks . "\n" . 'Type of Service: ' . ServiceType::getName($service_type); - error_log("SENDING SMS MESsAGE:\r\n" . $message); + error_log("SENDING SMS MESSAGE:\r\n" . $message); // send SMS message to hub $this->rt->sendSMS( diff --git a/src/Service/HubFilter/Filters/InventoryHubFilter.php b/src/Service/HubFilter/Filters/InventoryHubFilter.php index 2a23e346..b01b7ff0 100644 --- a/src/Service/HubFilter/Filters/InventoryHubFilter.php +++ b/src/Service/HubFilter/Filters/InventoryHubFilter.php @@ -121,10 +121,11 @@ 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( - $jo_id, $hub, JORejectionReason::NO_STOCK_SALES, "SKU(s): " . $battery_string, @@ -133,7 +134,6 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface // build SMS message $this->sendSMSMessage( $hub, - $jo_id, $params['order_date'], $params['service_type'], $robj, diff --git a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php index e969dcb2..d2697feb 100644 --- a/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php +++ b/src/Service/HubFilter/Filters/RiderAvailabilityHubFilter.php @@ -45,14 +45,15 @@ 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($jo_id, $hub, JORejectionReason::NO_RIDER_AVAILABLE); + $robj = $this->createRejectionEntry($hub, JORejectionReason::NO_RIDER_AVAILABLE); // build SMS message $this->sendSMSMessage( $hub, - $jo_id, $params['order_date'], $params['service_type'], $robj, From 23d814d1e9190c5947735ee097c0124f90eb743f Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 3 Jul 2024 23:41:44 +0800 Subject: [PATCH 41/49] Add extra logs to hub filtering #800 --- src/Service/HubFilter/BaseHubFilter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Service/HubFilter/BaseHubFilter.php b/src/Service/HubFilter/BaseHubFilter.php index 3ca4bf85..81f75b7d 100644 --- a/src/Service/HubFilter/BaseHubFilter.php +++ b/src/Service/HubFilter/BaseHubFilter.php @@ -31,6 +31,10 @@ class BaseHubFilter $this->em = $em; $this->rt = $rt; $this->trans = $trans; + + error_log("-------------------"); + error_log("HUB FILTER RUNNING: " . $this->getID()); + error_log("-------------------"); } public function getID(): string From a10f58e4258eca00e3cd7caa446ac3cda26d1ada Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 4 Jul 2024 12:57:27 +0800 Subject: [PATCH 42/49] Add filtering for JOs with VIP transaction origin #800 --- .../CustomerAppAPI/JobOrderController.php | 1 + src/Controller/JobOrderController.php | 2 +- src/Ramcar/HubCriteria.php | 13 +++++++++++++ .../HubFilter/Filters/InventoryHubFilter.php | 5 ++++- src/Service/HubSelector.php | 2 ++ src/Service/JobOrderHandler/ResqJobOrderHandler.php | 2 ++ 6 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Controller/CustomerAppAPI/JobOrderController.php b/src/Controller/CustomerAppAPI/JobOrderController.php index a6fc8baa..4f5c95ae 100644 --- a/src/Controller/CustomerAppAPI/JobOrderController.php +++ b/src/Controller/CustomerAppAPI/JobOrderController.php @@ -733,6 +733,7 @@ class JobOrderController extends ApiController // set job order info $hub_criteria->setJobOrderId($jo->getID()) ->setJoType($jo->getServiceType()) + ->setJoOrigin($jo->getSource()) ->setCustomerClass($cust->getCustomerClassification()) ->setOrderDate($jo->getDateCreate()) ->setServiceType($jo->getServiceType()); diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index 17c62a45..29ef66d5 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -761,7 +761,7 @@ class JobOrderController extends Controller $lat = $req->request->get('coord_lat', 0); $price_tier = 0; - if (($lng != 0) && ($lat != 0)) + if (!empty($lng) && !empty($lat)) { $coordinates = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); $price_tier = $pt_manager->getPriceTier($coordinates); diff --git a/src/Ramcar/HubCriteria.php b/src/Ramcar/HubCriteria.php index 6dff2743..3f7668d5 100644 --- a/src/Ramcar/HubCriteria.php +++ b/src/Ramcar/HubCriteria.php @@ -24,6 +24,7 @@ class HubCriteria protected $customer_class; // customer class protected $order_date; // date JO was created protected $service_type; // service type of JO + protected $jo_origin; // origin of JO public function __construct() { @@ -43,6 +44,7 @@ class HubCriteria $this->customer_class = null; $this->order_date = new DateTime(); $this->service_type = null; + $this->jo_origin = null; } public function setPoint(Point $point) @@ -222,5 +224,16 @@ class HubCriteria { return $this->service_type; } + + public function setJoOrigin($jo_origin) + { + $this->jo_origin = $jo_origin; + return $this; + } + + public function getJoOrigin() + { + return $this->jo_origin; + } } diff --git a/src/Service/HubFilter/Filters/InventoryHubFilter.php b/src/Service/HubFilter/Filters/InventoryHubFilter.php index b01b7ff0..d7526d0f 100644 --- a/src/Service/HubFilter/Filters/InventoryHubFilter.php +++ b/src/Service/HubFilter/Filters/InventoryHubFilter.php @@ -15,6 +15,7 @@ use App\Service\InventoryManager; use App\Service\HubFilterLogger; use App\Service\RisingTideGateway; use App\Entity\Battery; +use App\Ramcar\TransactionOrigin; class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface { @@ -33,6 +34,7 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface 'flag_inventory_check', 'customer_class', 'jo_type', + 'jo_origin', 'order_date', 'service_type', 'items', @@ -48,7 +50,8 @@ class InventoryHubFilter extends BaseHubFilter implements HubFilterInterface } // check customer class - if (!empty($params['customer_class']) && $params['customer_class'] == CustomerClassification::VIP) { + if ((!empty($params['customer_class']) && $params['customer_class'] == CustomerClassification::VIP) || + $params['jo_origin'] === TransactionOrigin::VIP) { error_log("INVENTORY CHECK " . $this->getJOID() . ": VIP CLASS"); return $hubs; } diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index 89265c66..e706cc84 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -67,6 +67,7 @@ class HubSelector $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(); @@ -87,6 +88,7 @@ class HubSelector '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, diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 025239b7..6c8c979f 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -2594,6 +2594,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $customer_id = $obj->getCustomer()->getID(); $hub_criteria->setJobOrderId($jo_id) + ->setJoOrigin($obj->getSource()) ->setCustomerId($customer_id); $hubs = $hub_selector->find($hub_criteria); @@ -2941,6 +2942,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $customer_id = $cust->getID(); $hub_criteria->setJobOrderId($jo_id) + ->setJoOrigin($obj->getSource()) ->setCustomerId($customer_id); $hubs = $hub_selector->find($hub_criteria); From e1103cf108a7762ce73357b0f6562a6bcddb86c0 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 4 Jul 2024 15:41:01 +0800 Subject: [PATCH 43/49] Move enabled filters list to env #800 --- config/services.yaml | 1 + src/Service/HubSelector.php | 18 ++++++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index 2bb23c98..6af4a5d3 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -16,6 +16,7 @@ parameters: android_app_version: "%env(ANDROID_APP_VERSION)%" ios_app_version: "%env(IOS_APP_VERSION)%" insurance_premiums_banner_url: "%env(INSURANCE_PREMIUMS_BANNER_URL)%" + enabled_hub_filters: "%env(ENABLED_HUB_FILTERS)%" services: # default configuration for services in *this* file diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index e706cc84..e5c3b5f1 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -39,18 +39,16 @@ class HubSelector $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', - ]; + $fnames = explode(",", $this->container->getParameter('enabled_hub_filters')); + $enabled_filters = []; + + foreach ($fnames as $filter) { + $enabled_filters[] = 'App\\Service\\HubFilter\\Filters\\' . $filter; + } + + return $enabled_filters; } public function find(HubCriteria $criteria) From 6139e649fbdb6098645d8fcd9e3024762484072f Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 4 Jul 2024 16:10:02 +0800 Subject: [PATCH 44/49] Add logging for closest hubs selected #800 --- src/Service/HubSelector.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Service/HubSelector.php b/src/Service/HubSelector.php index e5c3b5f1..6207248d 100644 --- a/src/Service/HubSelector.php +++ b/src/Service/HubSelector.php @@ -168,6 +168,9 @@ class HubSelector 'jo_count' => 0, 'inventory' => 0, ]; + + // log to file + $this->logClosestHubResult($jo_id, $row[0], $dist, $limit_distance); } else { @@ -220,5 +223,23 @@ class HubSelector // 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); + } } From f8d90cbdcdd012fae38c8fdfc52e25d6a837739a Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Thu, 4 Jul 2024 17:06:46 +0800 Subject: [PATCH 45/49] Fix typo on ResqJobOrderHandler properties #800 --- src/Service/JobOrderHandler/ResqJobOrderHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 6c8c979f..9746e5a5 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -95,7 +95,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface protected $hub_dist; protected $hub_geofence; protected $cust_distance_limit; - protected $hub_filter_enable; + protected $hub_filter_enabled; protected $jo_manager; protected $pt_manager; From 8da5381a1c937966a1e35faf0f86eb8a1179c62d Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 5 Jul 2024 15:54:14 +0800 Subject: [PATCH 46/49] Enable inventory retrieval if inventory checks are disabled #800 --- .../JobOrderHandler/ResqJobOrderHandler.php | 115 +++++++++++------- 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 9746e5a5..de1a9ecd 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -3026,54 +3026,61 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface //error_log("TOTAL HUBS FOUND WITH BRANCH CODE: " . count($inv_data)); - // get battery (if any) - /* - $skus = []; - $invoice = $obj->getInvoice(); - $inv_items = $invoice->getItems(); - foreach ($inv_items as $inv_item) - { - $batt = $inv_item->getBattery(); - if ($batt == null) - continue; + // get all enabled filters + $enabled_filter_str = $_ENV['ENABLED_HUB_FILTERS']; + $enabled_filters = explode(",", $enabled_filter_str); - $skus[] = $batt->getSapCode(); - } + // if inventory filter is disabled, fetch inventory here + if (!in_array('InventoryHubFilter', $enabled_filters) || $this->skipInventoryCheck($obj->getCoordinates())) { + error_log("NO INVENTORY CHECKS, GETTING INVENTORY FOR JO " . $obj->getID()); - // get inventory - $mres = $motiv->getInventory($branch_codes, $skus); - $x = 0; - - error_log("TOTAL RESULTS FROM MOTIV: " . count($mres) . " OUT OF " . count($branch_codes) . " BRANCH CODES AND " . count($skus) . " SKUS"); - - foreach ($mres as $mres_item) - { - // check if we have a valid response from motiv, ignore otherwise - if (isset($mres_item['BranchCode'])) + // get battery (if any) + $skus = []; + $invoice = $obj->getInvoice(); + $inv_items = $invoice->getItems(); + foreach ($inv_items as $inv_item) { - $bcode = $mres_item['BranchCode']; - $inv_count = $mres_item['Quantity']; - if (isset($inv_data[$bcode])) - { - $hub_id = $inv_data[$bcode]['hub_id']; + $batt = $inv_item->getBattery(); + if ($batt == null) + continue; - $params['hubs'][$hub_id]['inventory'] = $inv_count; - - error_log("SETTING HUB " . $hub_id . " INVENTORY TO " . $inv_count); - $x++; - } else { - error_log("CANNOT FIND BCODE FOR " . $bcode); - } - } else { - error_log("CANNOT FIND BCODE FOR RESULT: " . print_r($mres_item, true)); + $skus[] = $batt->getSapCode(); } + + // get inventory + $mres = $motiv->getInventory($branch_codes, $skus); + $x = 0; + + error_log("TOTAL RESULTS FROM MOTIV: " . count($mres) . " OUT OF " . count($branch_codes) . " BRANCH CODES AND " . count($skus) . " SKUS"); + + foreach ($mres as $mres_item) + { + // check if we have a valid response from motiv, ignore otherwise + if (isset($mres_item['BranchCode'])) + { + $bcode = $mres_item['BranchCode']; + $inv_count = $mres_item['Quantity']; + if (isset($inv_data[$bcode])) + { + $hub_id = $inv_data[$bcode]['hub_id']; + + $params['hubs'][$hub_id]['inventory'] = $inv_count; + + error_log("SETTING HUB " . $hub_id . " INVENTORY TO " . $inv_count); + $x++; + } else { + error_log("CANNOT FIND BCODE FOR " . $bcode); + } + } else { + error_log("CANNOT FIND BCODE FOR RESULT: " . print_r($mres_item, true)); + } + } + + error_log("SET QUANTITY OF " . $x . " HUBS TO NON ZERO"); + + // error_log(print_r($mres, true)); } - - error_log("SET QUANTITY OF " . $x . " HUBS TO NON ZERO"); - - // error_log(print_r($mres, true)); - */ - + $params['obj'] = $obj; // get template to display $params['template'] = $this->getTwigTemplate('jo_open_hub_form'); @@ -4349,4 +4356,28 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $this->rt->sendSMS($phone_number, $this->translator->trans('message.battery_brand_allcaps'), $msg); } + protected function skipInventoryCheck(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['no_inventory'])) { + return true; + } + } + + // filter is in place + return false; + } } From e7d8af516e7b49f1decea7e410c009e6d6fc3d73 Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 9 Jul 2024 16:55:58 +0800 Subject: [PATCH 47/49] Temporarily disable hub filter area checks #800 --- .../JobOrderHandler/ResqJobOrderHandler.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index de1a9ecd..b654a85f 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -2564,15 +2564,16 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // for the rest of the HubCriteria fields if ($this->hub_filter_enabled == 'true') { + // TODO: allow this to be disabled via env or CRM. commenting out for now // error_log('hub filter is enabled'); - if ($this->hub_geofence->isCovered($long, $lat)) - { + //if ($this->hub_geofence->isCovered($long, $lat)) + //{ // if true, set other values for HubCriteria // error_log('Area is covered by hub filtering'); $hub_criteria->setLimitDistance($this->cust_distance_limit) ->setPaymentMethod($obj->getModeOfPayment()) ->setRoundRobin(true); - } + //} } // check if emergency or not @@ -2915,14 +2916,15 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $cust = $obj->getCustomer(); $hub_criteria->setCustomerClass($cust->getCustomerClassification()); - if ($this->hub_geofence->isCovered($long, $lat)) - { + // TODO: allow this to be disabled via env or CRM. commenting out for now + //if ($this->hub_geofence->isCovered($long, $lat)) + //{ // if true, set other values for HubCriteria // error_log('Area is covered by hub'); $hub_criteria->setLimitDistance($this->cust_distance_limit) ->setPaymentMethod($obj->getModeOfPayment()) ->setRoundRobin(true); - } + //} // check if emergency or not $willing_to_wait = $obj->getWillWait(); From 5f2ed34d8cfbf91be9437a348aba9a2bf540821f Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Fri, 12 Jul 2024 14:17:59 +0800 Subject: [PATCH 48/49] Disable round robin filter for emergency JOs #800 --- src/Service/JobOrderHandler/ResqJobOrderHandler.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index b654a85f..2ad24546 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -2582,8 +2582,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface { // reset distance limit if emergency //TODO: move to .env the emergency distance limit - $hub_criteria->setLimitDistance(500); - $hub_criteria->setEmergency(true); + $hub_criteria->setLimitDistance(500) + ->setEmergency(true) + ->setPaymentMethod(null) + ->setRoundRobin(false); } // set filter flags for inventory and available riders @@ -2931,8 +2933,10 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface if ($willing_to_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT) { //TODO: move to .env the emergency distance limit - $hub_criteria->setLimitDistance(500); - $hub_criteria->setEmergency(true); + $hub_criteria->setLimitDistance(500) + ->setEmergency(true) + ->setPaymentMethod(null) + ->setRoundRobin(false); } // set filter flags for inventory and available riders From a66d16a58bbda656e29a26baa7429ca6186a173d Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Tue, 16 Jul 2024 16:10:29 +0800 Subject: [PATCH 49/49] Restored hub filter area functionality #800 --- src/Service/JobOrderHandler/ResqJobOrderHandler.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 2ad24546..b2d6fd68 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -2566,14 +2566,14 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface { // TODO: allow this to be disabled via env or CRM. commenting out for now // error_log('hub filter is enabled'); - //if ($this->hub_geofence->isCovered($long, $lat)) - //{ + if ($this->hub_geofence->isCovered($long, $lat)) + { // if true, set other values for HubCriteria // error_log('Area is covered by hub filtering'); $hub_criteria->setLimitDistance($this->cust_distance_limit) ->setPaymentMethod($obj->getModeOfPayment()) ->setRoundRobin(true); - //} + } } // check if emergency or not @@ -2919,14 +2919,14 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $hub_criteria->setCustomerClass($cust->getCustomerClassification()); // TODO: allow this to be disabled via env or CRM. commenting out for now - //if ($this->hub_geofence->isCovered($long, $lat)) - //{ + if ($this->hub_geofence->isCovered($long, $lat)) + { // if true, set other values for HubCriteria // error_log('Area is covered by hub'); $hub_criteria->setLimitDistance($this->cust_distance_limit) ->setPaymentMethod($obj->getModeOfPayment()) ->setRoundRobin(true); - //} + } // check if emergency or not $willing_to_wait = $obj->getWillWait();