diff --git a/.env.dist b/.env.dist index 9f173e4b..29a870ff 100644 --- a/.env.dist +++ b/.env.dist @@ -63,3 +63,15 @@ COUNTRY_CODE=+insert_country_code_here # dashboard DASHBOARD_ENABLE=set_to_true_or_false + +# auth token for Inventory API +INVENTORY_API_URL=insert_api_url_here +INVENTORY_API_OCP=insert_ocp_text_here +INVENTORY_API_AUTH_TOKEN_PREFIX=Bearer +INVENTORY_API_AUTH_TOKEN=insert_auth_token_here + +# API logging +API_LOGGING=set_to_true_or_false + +# customer distance limit in km +CUST_DISTANCE_LIMIT=set_to_number diff --git a/catalyst/api-bundle/Command/TestAPICommand.php b/catalyst/api-bundle/Command/TestAPICommand.php index 7e959808..952b98b6 100644 --- a/catalyst/api-bundle/Command/TestAPICommand.php +++ b/catalyst/api-bundle/Command/TestAPICommand.php @@ -64,7 +64,7 @@ class TestAPICommand extends Command 'start' => '1', ]; - $api->get('/capi/warranties', $params); + //$api->get('/capi/warranties', $params); // warranty find @@ -123,7 +123,7 @@ class TestAPICommand extends Command // privacy policy $privacy_policy_id = 2; - $api->get('/capi/privacy_policy/' . $privacy_policy_id ); + //$api->get('/capi/privacy_policy/' . $privacy_policy_id ); // register new customer $params = [ @@ -137,6 +137,19 @@ class TestAPICommand extends Command 'v_condition' => 'new', 'v_fuel_type' => 'gas', ]; - $api->post('/capi/quick_registration', $params); + //$api->post('/capi/quick_registration', $params); + + // get warranties given list of serial numbers + $serial_list = [ + 'AJ34LJADR12134LKJM4', + 'AJ34LJADR12134LKJL5', + 'test', + ]; + + $params = [ + 'serial_list' => $serial_list, + ]; + + $api->post('/capi/warranties_list', $params); } } diff --git a/config/acl.yaml b/config/acl.yaml index 83cdedad..653fe458 100644 --- a/config/acl.yaml +++ b/config/acl.yaml @@ -268,6 +268,8 @@ access_keys: label: Walk-in - id: jo_walkin.edit label: Walk-in Edit + - id: jo_autoassign.test + label: Autoassign Test - id: support label: Customer Support Access diff --git a/config/api_acl.yaml b/config/api_acl.yaml index 2ff18704..a7ad72d5 100644 --- a/config/api_acl.yaml +++ b/config/api_acl.yaml @@ -20,6 +20,8 @@ access_keys: label: Delete - id: warranty.set.privacypolicy label: Set Privacy Policy + - id: warranty.list.serial + label: List by Serial - id: batterybrand label: Battery Brand Access acls: diff --git a/config/cmb.services.yaml b/config/cmb.services.yaml index 46e348b9..cd07558d 100644 --- a/config/cmb.services.yaml +++ b/config/cmb.services.yaml @@ -65,6 +65,7 @@ services: arguments: $em: "@doctrine.orm.entity_manager" $gmaps_api_key: "%env(GMAPS_API_KEY)%" + $cust_dist_limit: "%env(CUST_DISTANCE_LIMIT)%" App\Service\RisingTideGateway: arguments: @@ -257,3 +258,7 @@ services: - name: 'doctrine.event_listener' event: 'postPersist' + # API logging + App\EventSubscriber\LogSubscriber: + arguments: + $api_log_flag: "%env(API_LOGGING)%" diff --git a/config/menu.yaml b/config/menu.yaml index 9f28b315..b0096cf5 100644 --- a/config/menu.yaml +++ b/config/menu.yaml @@ -37,10 +37,6 @@ main_menu: acl: rider.list label: Riders parent: logistics - - id: service_charge_list - acl: service_charge.list - label: Service Charges - parent: logistics - id: battery acl: battery.menu @@ -102,13 +98,21 @@ main_menu: acl: joborder.menu label: Job Order icon: flaticon-calendar-3 - - id: jo_onestep_form - acl: jo_onestep.form - label: One-step Process + - id: jo_in + acl: jo_in.list + label: Incoming parent: joborder - - id: jo_walkin_form - acl: jo_walkin.form - label: Walk-in + - id: jo_proc + acl: jo_proc.list + label: Dispatch + parent: joborder + - id: jo_assign + acl: jo_assign.list + label: Rider Assignment + parent: joborder + - id: jo_fulfill + acl: jo_fulfill.list + label: Fulfillment parent: joborder - id: jo_open acl: jo_open.list diff --git a/config/resq.services.yaml b/config/resq.services.yaml index b487ed82..1e7797b0 100644 --- a/config/resq.services.yaml +++ b/config/resq.services.yaml @@ -65,6 +65,7 @@ services: arguments: $em: "@doctrine.orm.entity_manager" $gmaps_api_key: "%env(GMAPS_API_KEY)%" + $cust_dist_limit: "%env(CUST_DISTANCE_LIMIT)%" App\Service\RisingTideGateway: arguments: @@ -256,3 +257,15 @@ services: - name: 'doctrine.event_listener' event: 'postPersist' + # inventory manager + App\Service\InventoryManager: + arguments: + $api_url: "%env(INVENTORY_API_URL)%" + $api_ocp_key: "%env(INVENTORY_API_OCP)%" + $api_auth_prefix: "%env(INVENTORY_API_AUTH_TOKEN_PREFIX)%" + $api_auth_token: "%env(INVENTORY_API_AUTH_TOKEN)%" + + # API logging + App\EventSubscriber\LogSubscriber: + arguments: + $api_log_flag: "%env(API_LOGGING)%" diff --git a/config/routes/capi.yaml b/config/routes/capi.yaml index ee3c8668..0c2e5ac1 100644 --- a/config/routes/capi.yaml +++ b/config/routes/capi.yaml @@ -108,6 +108,12 @@ capi_warranty_privacy_policy: controller: App\Controller\CAPI\WarrantyController::setPrivacyPolicy methods: [POST] +# get list of warranties given list of serials +capi_warranty_get_warranties_from_serials: + path: /capi/warranties_list + controller: App\Controller\CAPI\WarrantyController::getWarrantiesBySerialList + methods: [POST] + # customer vehicle api # find customer vehicle by id diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml index d5cd8dfc..f239402d 100644 --- a/config/routes/job_order.yaml +++ b/config/routes/job_order.yaml @@ -226,3 +226,12 @@ jo_walkin_edit_submit: controller: App\Controller\JobOrderController::walkInEditSubmit methods: [POST] +jo_autoassign: + path: /job-order/autoassign + controller: App\Controller\JobOrderController::autoAssignForm + methods: [GET] + +jo_autoassign_test_submit: + path: /job-order/autoassign + controller: App\Controller\JobOrderController::autoAssignSubmit + methods: [POST] diff --git a/config/services.yaml b/config/services.yaml index 1a72a956..fafcfdd8 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -65,6 +65,7 @@ services: arguments: $em: "@doctrine.orm.entity_manager" $gmaps_api_key: "%env(GMAPS_API_KEY)%" + $cust_dist_limit: "%env(CUST_DISTANCE_LIMIT)%" App\Service\RisingTideGateway: arguments: @@ -158,62 +159,41 @@ services: tags: - { name: kernel.event_listener, event: kernel.controller, method: onKernelController } - # CMB invoice generator - App\Service\InvoiceGenerator\CMBInvoiceGenerator: ~ - - # Resq invoice generator - #App\Service\InvoiceGenerator\ResqInvoiceGenerator: ~ + # invoice generator + App\Service\InvoiceGenerator\ResqInvoiceGenerator: ~ # invoice generator interface - App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\CMBInvoiceGenerator" - #App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\ResqInvoiceGenerator" + App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\ResqInvoiceGenerator" - # Resq job order generator - #App\Service\JobOrderHandler\ResqJobOrderHandler: - # arguments: - # $country_code: "%env(COUNTRY_CODE)%" - - # CMB job order generator - App\Service\JobOrderHandler\CMBJobOrderHandler: + # job order generator + App\Service\JobOrderHandler\ResqJobOrderHandler: arguments: - $country_code: "%env(COUNTRY_CODE)%" + $country_code: "%env(COUNTRY_CODE)%" #job order generator interface - App\Service\JobOrderHandlerInterface: "@App\\Service\\JobOrderHandler\\CMBJobOrderHandler" - #App\Service\JobOrderHandlerInterface: "@App\\Service\\JobOrderHandler\\ResqJobOrderHandler" + App\Service\JobOrderHandlerInterface: "@App\\Service\\JobOrderHandler\\ResqJobOrderHandler" - # CMB customer generator - App\Service\CustomerHandler\CMBCustomerHandler: - arguments: - $country_code: "%env(COUNTRY_CODE)%" - - # Resq customer generator - #App\Service\CustomerHandler\ResqCustomerHandler: - # arguments: - # $country_code: "%env(COUNTRY_CODE)%" - - # customer generator interface - App\Service\CustomerHandlerInterface: "@App\\Service\\CustomerHandler\\CMBCustomerHandler" - #App\Service\CustomerHandlerInterface: "@App\\Service\\CustomerHandler\\ResqCustomerHandler" - - # rider assignment - #App\Service\RiderAssignmentHandler\CMBRiderAssignmentHandler: ~ - - # rider assignment interface - App\Service\RiderAssignmentHandlerInterface: "@App\\Service\\RiderAssignmentHandler\\CMBRiderAssignmentHandler" - - # rider API service - App\Service\RiderAPIHandler\CMBRiderAPIHandler: + # customer generator + App\Service\CustomerHandler\ResqCustomerHandler: arguments: $country_code: "%env(COUNTRY_CODE)%" - #App\Service\RiderAPIHandler\ResqRiderAPIHandler: - # arguments: - # $country_code: "%env(COUNTRY_CODE)%" + # customer generator interface + App\Service\CustomerHandlerInterface: "@App\\Service\\CustomerHandler\\ResqCustomerHandler" - # rider API interface - App\Service\RiderAPIHandlerInterface: "@App\\Service\\RiderAPIHandler\\CMBRiderAPIHandler" + # rider assignment + App\Service\RiderAssignmentHandler\ResqRiderAssignmentHandler: ~ + # rider assignment interface + App\Service\RiderAssignmentHandlerInterface: "@App\\Service\\RiderAssignmentHandler\\ResqRiderAssignmentHandler" + + # rider API service + App\Service\RiderAPIHandler\ResqRiderAPIHandler: + arguments: + $country_code: "%env(COUNTRY_CODE)%" + + App\Service\RiderAPIHandlerInterface: "@App\\Service\\RiderAPIHandler\\ResqRiderAPIHandler" + # map manager #App\Service\GISManager\Bing: ~ App\Service\GISManager\OpenStreet: ~ @@ -276,3 +256,16 @@ services: event: 'onFlush' - name: 'doctrine.event_listener' event: 'postPersist' + + # inventory manager + App\Service\InventoryManager: + arguments: + $api_url: "%env(INVENTORY_API_URL)%" + $api_ocp_key: "%env(INVENTORY_API_OCP)%" + $api_auth_prefix: "%env(INVENTORY_API_AUTH_TOKEN_PREFIX)%" + $api_auth_token: "%env(INVENTORY_API_AUTH_TOKEN)%" + + # API logging + App\EventSubscriber\LogSubscriber: + arguments: + $api_log_flag: "%env(API_LOGGING)%" diff --git a/src/Command/AdjustLongLatCommand.php b/src/Command/AdjustLongLatCommand.php index 62405269..86e4fc0f 100644 --- a/src/Command/AdjustLongLatCommand.php +++ b/src/Command/AdjustLongLatCommand.php @@ -46,5 +46,7 @@ class AdjustLongLatCommand extends Command } $em->flush(); + + return 0; } } diff --git a/src/Command/ComputeWarrantyExpiryDateCommand.php b/src/Command/ComputeWarrantyExpiryDateCommand.php index cffc24ef..fa0fdc14 100644 --- a/src/Command/ComputeWarrantyExpiryDateCommand.php +++ b/src/Command/ComputeWarrantyExpiryDateCommand.php @@ -71,5 +71,7 @@ class ComputeWarrantyExpiryDateCommand extends Command $this->em->clear(); } + + return 0; } } diff --git a/src/Command/CreateCustomerFromWarrantyCommand.php b/src/Command/CreateCustomerFromWarrantyCommand.php index 42b2fe73..416ee514 100644 --- a/src/Command/CreateCustomerFromWarrantyCommand.php +++ b/src/Command/CreateCustomerFromWarrantyCommand.php @@ -290,6 +290,8 @@ class CreateCustomerFromWarrantyCommand extends Command //$output->writeln('Total warranties with no mobile number: ' . $total_inv_warr); $output->writeln('Total customers added: ' . $total_cust_added); $output->writeln('Total customer vehicles added: ' . $total_cv_added); + + return 0; } protected function getDefaultVehicle() diff --git a/src/Command/FulfillOldJobOrderCommand.php b/src/Command/FulfillOldJobOrderCommand.php index 2a4132cc..dcf9d761 100644 --- a/src/Command/FulfillOldJobOrderCommand.php +++ b/src/Command/FulfillOldJobOrderCommand.php @@ -22,9 +22,9 @@ class FulfillOldJobOrderCommand extends Command { protected $em; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; parent::__construct(); } @@ -76,5 +76,7 @@ class FulfillOldJobOrderCommand extends Command } $em->flush(); + + return 0; } } diff --git a/src/Command/FulfillPendingJobOrderCommand.php b/src/Command/FulfillPendingJobOrderCommand.php index 997dd62e..1ff47e8e 100644 --- a/src/Command/FulfillPendingJobOrderCommand.php +++ b/src/Command/FulfillPendingJobOrderCommand.php @@ -22,9 +22,9 @@ class FulfillPendingJobOrderCommand extends Command { protected $em; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; parent::__construct(); } @@ -79,5 +79,7 @@ class FulfillPendingJobOrderCommand extends Command } $em->flush(); + + return 0; } } diff --git a/src/Command/GenerateBatteryCompatibilityCommand.php b/src/Command/GenerateBatteryCompatibilityCommand.php index c4eea3a3..e4ca2d6c 100644 --- a/src/Command/GenerateBatteryCompatibilityCommand.php +++ b/src/Command/GenerateBatteryCompatibilityCommand.php @@ -21,9 +21,9 @@ class GenerateBatteryCompatibilityCommand extends Command protected $em; protected $vmfg_index; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; parent::__construct(); } @@ -100,5 +100,7 @@ class GenerateBatteryCompatibilityCommand extends Command fwrite($json_file, json_encode($this->vmfg_index, JSON_PRETTY_PRINT)); fclose($json_file); + return 0; + } } diff --git a/src/Command/GenerateWarrantyFromJobOrderCommand.php b/src/Command/GenerateWarrantyFromJobOrderCommand.php index 1d3f100b..23f436c4 100644 --- a/src/Command/GenerateWarrantyFromJobOrderCommand.php +++ b/src/Command/GenerateWarrantyFromJobOrderCommand.php @@ -211,5 +211,7 @@ class GenerateWarrantyFromJobOrderCommand extends Command $em->detach($row[0]); $em->clear(); } + + return 0; } } diff --git a/src/Command/ImportBatteryPriceCommand.php b/src/Command/ImportBatteryPriceCommand.php index 592720bf..f7395a6e 100644 --- a/src/Command/ImportBatteryPriceCommand.php +++ b/src/Command/ImportBatteryPriceCommand.php @@ -30,9 +30,9 @@ class ImportBatteryPriceCommand extends Command protected $em; protected $size_index; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; $this->size_index = []; parent::__construct(); @@ -174,5 +174,7 @@ class ImportBatteryPriceCommand extends Command } $em->flush(); + + return 0; } } diff --git a/src/Command/ImportCMBBatteryDataCommand.php b/src/Command/ImportCMBBatteryDataCommand.php index 7fbee55a..79e84571 100644 --- a/src/Command/ImportCMBBatteryDataCommand.php +++ b/src/Command/ImportCMBBatteryDataCommand.php @@ -170,6 +170,8 @@ class ImportCMBBatteryDataCommand extends Command } } + + return 0; } protected function addBatteryManufacturer($name) diff --git a/src/Command/ImportCMBBatteryTradeInPriceCommand.php b/src/Command/ImportCMBBatteryTradeInPriceCommand.php index 03f594da..b75f9ebb 100644 --- a/src/Command/ImportCMBBatteryTradeInPriceCommand.php +++ b/src/Command/ImportCMBBatteryTradeInPriceCommand.php @@ -92,9 +92,9 @@ class ImportCMBBatteryTradeInPriceCommand extends Command { error_log('Cannot find battery size ' . $size); } - - } + + return 0; } protected function loadBatterySizes() diff --git a/src/Command/ImportCMBVehicleCompatibilityCommand.php b/src/Command/ImportCMBVehicleCompatibilityCommand.php index 81a5a828..4034aec8 100644 --- a/src/Command/ImportCMBVehicleCompatibilityCommand.php +++ b/src/Command/ImportCMBVehicleCompatibilityCommand.php @@ -289,6 +289,8 @@ class ImportCMBVehicleCompatibilityCommand extends Command $row_num++; } + + return 0; } protected function addVehicleManufacturer($name) diff --git a/src/Command/ImportCustomerCommand.php b/src/Command/ImportCustomerCommand.php index 4c0e75e7..e2c55832 100644 --- a/src/Command/ImportCustomerCommand.php +++ b/src/Command/ImportCustomerCommand.php @@ -479,5 +479,7 @@ class ImportCustomerCommand extends Command } fclose($cust_file); fclose($cv_file); + + return 0; } } diff --git a/src/Command/ImportKMLFileCommand.php b/src/Command/ImportKMLFileCommand.php index 4d2e71a2..64bd2b7d 100644 --- a/src/Command/ImportKMLFileCommand.php +++ b/src/Command/ImportKMLFileCommand.php @@ -35,5 +35,7 @@ class ImportKMLFileCommand extends Command $kml_file = $input->getArgument('file'); $this->importer->getMapData($kml_file); + + return 0; } } diff --git a/src/Command/ImportLegacyJobOrderCommand.php b/src/Command/ImportLegacyJobOrderCommand.php index c0edfc80..bf5b3d52 100644 --- a/src/Command/ImportLegacyJobOrderCommand.php +++ b/src/Command/ImportLegacyJobOrderCommand.php @@ -44,9 +44,9 @@ class ImportLegacyJobOrderCommand extends Command protected $row_max_field_size; protected $data_max_field_size; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; $this->loadBatteryModels(); $this->loadBatterySizes(); @@ -544,6 +544,8 @@ class ImportLegacyJobOrderCommand extends Command fclose($warr_outfile); fclose($jo_outfile); fclose($jorow_outfile); + + return 0; } protected function initializeMaxFieldCounters() diff --git a/src/Command/ImportOutletsCommand.php b/src/Command/ImportOutletsCommand.php index 2de7a80e..158929b8 100644 --- a/src/Command/ImportOutletsCommand.php +++ b/src/Command/ImportOutletsCommand.php @@ -7,6 +7,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; + use Doctrine\ORM\EntityManagerInterface; use App\Entity\Outlet; @@ -17,11 +18,11 @@ use DateTime; class ImportOutletsCommand extends Command { - private $object_manager; + private $em; - public function __construct(EntityManagerInterface $om, ValidatorInterface $validator) + public function __construct(EntityManagerInterface $em, ValidatorInterface $validator) { - $this->object_manager = $om; + $this->em = $em; $this->validator = $validator; parent::__construct(); @@ -60,7 +61,7 @@ class ImportOutletsCommand extends Command } // get entity manager - $em = $this->object_manager; + $em = $this->em; // row counter $row = 1; @@ -167,7 +168,8 @@ class ImportOutletsCommand extends Command { $output->writeln("Errors occurred! Please check the CSV file is in the proper format."); } - + + return 0; } protected function setObject($obj, $fields) diff --git a/src/Command/ImportPartnersCommand.php b/src/Command/ImportPartnersCommand.php index e46a5aa2..facc74df 100644 --- a/src/Command/ImportPartnersCommand.php +++ b/src/Command/ImportPartnersCommand.php @@ -34,9 +34,9 @@ class ImportPartnersCommand extends Command protected $em; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; parent::__construct(); } @@ -163,6 +163,7 @@ class ImportPartnersCommand extends Command $row_num++; } + return 0; } } diff --git a/src/Command/ImportSAPBatteryCommand.php b/src/Command/ImportSAPBatteryCommand.php index 91c14cba..439cb230 100644 --- a/src/Command/ImportSAPBatteryCommand.php +++ b/src/Command/ImportSAPBatteryCommand.php @@ -7,6 +7,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; + use Doctrine\ORM\EntityManagerInterface; use App\Entity\SAPBattery; @@ -37,12 +38,14 @@ class ImportSAPBatteryCommand extends Command $this->setName('sap_battery:import') ->setDescription('Import a CSV file with SAP battery data.') ->setHelp('Creates SAP battery data entries off imported CSV.') - ->addArgument('file', InputArgument::REQUIRED, 'Path to the CSV file.'); + ->addArgument('file', InputArgument::REQUIRED, 'Path to the CSV file.') + ->addArgument('output_file', InputArgument::REQUIRED, 'Path to the output CSV file.'); } protected function execute(InputInterface $input, OutputInterface $output) { $csv_file = $input->getArgument('file'); + $report_file = $input->getArgument('output_file'); // CSV column order: // 0 - brand @@ -68,6 +71,9 @@ class ImportSAPBatteryCommand extends Command // has error? $has_error = false; + $dupe_brands = []; + $dupe_sizes = []; + $dupe_batteries = []; // loop through rows and build hashes while (($fields = fgetcsv($handle)) !== false) { @@ -77,7 +83,7 @@ class ImportSAPBatteryCommand extends Command // clean up fields $clean_brand = trim($fields[0]); - $clean_sku = trim($fields[1]); + $clean_sku = strtoupper(trim($fields[1])); $clean_size = trim($fields[2]); $output->writeln("Parsing $clean_sku..."); @@ -92,6 +98,11 @@ class ImportSAPBatteryCommand extends Command $this->batt_brand_hash[$clean_brand] = $brand; } + else + { + // add to duplicate list + $dupe_brands[] = $clean_brand; + } // size hash if (!isset($this->batt_size_hash[$clean_size])) @@ -103,6 +114,10 @@ class ImportSAPBatteryCommand extends Command $this->batt_size_hash[$clean_size] = $size; } + else + { + $dupe_sizes[] = $clean_size; + } // battery hash if (!isset($this->battery_hash[$clean_sku])) @@ -110,14 +125,28 @@ class ImportSAPBatteryCommand extends Command // create battery entry $battery = new SAPBattery(); $battery->setID($clean_sku) - ->setSize($size_hash[$clean_size]) - ->setBrand($brand_hash[$clean_brand]); + ->setSize($this->batt_size_hash[$clean_size]) + ->setBrand($this->batt_brand_hash[$clean_brand]); $em->persist($battery); } + else + { + $dupe_batteries[] = $clean_sku; + } } $em->flush(); + + // write dupes report + if ((count($dupe_brands) > 0) || + (count($dupe_sizes) > 0) || + (count($dupe_batteries) > 0)) + { + $this->writeDupeReport($report_file, $dupe_brands, $dupe_sizes, $dupe_batteries); + } + + return 0; } protected function initBatteryHash() @@ -146,4 +175,40 @@ class ImportSAPBatteryCommand extends Command foreach ($brands as $brand) $this->batt_brand_hash[$brand->getName()] = $brand; } + + protected function writeDupeReport($report_file, $brands, $sizes, $batts) + { + try + { + $fh = fopen($report_file, "w"); + } + catch (Exception $e) + { + throw new Exception('The file "' . $report_file . '" could be opened.'); + } + + fputs($fh, 'Brands not added: ' . "\n"); + foreach ($brands as $brand) + { + fputs($fh, $brand, strlen($brand)); + fputs($fh, "\n"); + } + + fputs($fh, 'Sizes not added: ' . "\n"); + foreach ($sizes as $size) + { + fputs($fh, $size, strlen($size)); + fputs($fh, "\n"); + } + + fputs($fh, 'SKUs not added: ' . "\n"); + foreach ($batts as $batt) + { + fputs($fh, $batt, strlen($batt)); + fputs($fh, "\n"); + } + + fclose($fh); + + } } diff --git a/src/Command/ImportVehicleBatteryCommand.php b/src/Command/ImportVehicleBatteryCommand.php index 73628894..79ee07dc 100644 --- a/src/Command/ImportVehicleBatteryCommand.php +++ b/src/Command/ImportVehicleBatteryCommand.php @@ -35,9 +35,9 @@ class ImportVehicleBatteryCommand extends Command protected $batt_index; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; $this->vmfg_index = []; $this->v_index = []; $this->batt_index = []; @@ -221,6 +221,6 @@ class ImportVehicleBatteryCommand extends Command $em->flush(); - // print_r($batteries); + return 0; } } diff --git a/src/Command/ImportVehicleCompatibilityCommand.php b/src/Command/ImportVehicleCompatibilityCommand.php index 1c39862c..14517885 100644 --- a/src/Command/ImportVehicleCompatibilityCommand.php +++ b/src/Command/ImportVehicleCompatibilityCommand.php @@ -28,9 +28,9 @@ class ImportVehicleCompatibilityCommand extends Command protected $bsize_index; protected $b_index; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; $this->vmfg_index = []; $this->v_index = []; $this->b_index = []; @@ -317,6 +317,6 @@ class ImportVehicleCompatibilityCommand extends Command $em->flush(); - // print_r($batteries); + return 0; } } diff --git a/src/Command/MergeDuplicateVehiclesCommand.php b/src/Command/MergeDuplicateVehiclesCommand.php index 93fa94ad..79b7ce52 100644 --- a/src/Command/MergeDuplicateVehiclesCommand.php +++ b/src/Command/MergeDuplicateVehiclesCommand.php @@ -15,9 +15,9 @@ class MergeDuplicateVehiclesCommand extends Command { protected $em; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; parent::__construct(); } @@ -161,8 +161,7 @@ class MergeDuplicateVehiclesCommand extends Command $em->flush(); } } - - // print_r($dupes); + return 0; } } diff --git a/src/Command/RefreshJobOrderCacheCommand.php b/src/Command/RefreshJobOrderCacheCommand.php index 0e314c56..99fcb331 100644 --- a/src/Command/RefreshJobOrderCacheCommand.php +++ b/src/Command/RefreshJobOrderCacheCommand.php @@ -64,5 +64,7 @@ class RefreshJobOrderCacheCommand extends Command { $this->jo_cache->addActiveJobOrder($jo); } + + return 0; } } diff --git a/src/Command/ReportRiderTime.php b/src/Command/ReportRiderTime.php index e8825b5b..62a2a8e6 100644 --- a/src/Command/ReportRiderTime.php +++ b/src/Command/ReportRiderTime.php @@ -142,5 +142,7 @@ class ReportRiderTime extends Command } fclose($fh); + + return 0; } } diff --git a/src/Command/SeedRiderSessionsCommand.php b/src/Command/SeedRiderSessionsCommand.php index cc7dc26d..e1c44954 100644 --- a/src/Command/SeedRiderSessionsCommand.php +++ b/src/Command/SeedRiderSessionsCommand.php @@ -55,5 +55,7 @@ class SeedRiderSessionsCommand extends Command $this->redis->set($redis_key, $rider_id); } } + + return 0; } } diff --git a/src/Command/SetCustomerPrivacyPolicyCommand.php b/src/Command/SetCustomerPrivacyPolicyCommand.php index 9cb13f4b..6700b8e6 100644 --- a/src/Command/SetCustomerPrivacyPolicyCommand.php +++ b/src/Command/SetCustomerPrivacyPolicyCommand.php @@ -22,10 +22,10 @@ class SetCustomerPrivacyPolicyCommand extends Command private $policy_third_party_id; private $policy_mobile_id; - public function __construct(EntityManagerInterface $om, $policy_promo, + public function __construct(EntityManagerInterface $em, $policy_promo, $policy_third_party, $policy_mobile) { - $this->em = $om; + $this->em = $em; $this->policy_promo_id = $policy_promo; $this->policy_third_party_id = $policy_third_party; @@ -80,5 +80,7 @@ class SetCustomerPrivacyPolicyCommand extends Command } $this->em->flush(); + + return 0; } } diff --git a/src/Command/TestGeneralSearchCommand.php b/src/Command/TestGeneralSearchCommand.php index 0696fb86..c5d8d335 100644 --- a/src/Command/TestGeneralSearchCommand.php +++ b/src/Command/TestGeneralSearchCommand.php @@ -66,5 +66,7 @@ class TestGeneralSearchCommand extends Command echo "Last Name: " . $warranty->getLastName() . "\n"; echo "First Name: " . $warranty->getFirstName() . "\n"; } + + return 0; } } diff --git a/src/Command/TestGeofenceCommand.php b/src/Command/TestGeofenceCommand.php index 1397f08f..a4bdff69 100644 --- a/src/Command/TestGeofenceCommand.php +++ b/src/Command/TestGeofenceCommand.php @@ -40,5 +40,7 @@ class TestGeofenceCommand extends Command echo "In geofence\n"; else echo "NOT in geofence\n"; + + return 0; } } diff --git a/src/Command/TestHubCounterCommand.php b/src/Command/TestHubCounterCommand.php index e08c11dd..f9ba5eee 100644 --- a/src/Command/TestHubCounterCommand.php +++ b/src/Command/TestHubCounterCommand.php @@ -35,5 +35,7 @@ class TestHubCounterCommand extends Command $available_rc = $this->hc->getAvailableRiderCount($hub_id); echo "Available Riders in Hub: " . $available_rc . "\n"; + + return 0; } } diff --git a/src/Command/TestJobOrderManagerCommand.php b/src/Command/TestJobOrderManagerCommand.php index 4beeea46..7fefe37f 100644 --- a/src/Command/TestJobOrderManagerCommand.php +++ b/src/Command/TestJobOrderManagerCommand.php @@ -36,5 +36,7 @@ class TestJobOrderManagerCommand extends Command echo "Job Order successfully updated" . "\n"; else echo "Job Order not updated" . "\n"; + + return 0; } } diff --git a/src/Command/TestRegAPNSCommand.php b/src/Command/TestRegAPNSCommand.php index 52ec7d76..aeb7ad6a 100644 --- a/src/Command/TestRegAPNSCommand.php +++ b/src/Command/TestRegAPNSCommand.php @@ -34,5 +34,7 @@ class TestRegAPNSCommand extends Command { $push_id = $input->getArgument('push_id'); $this->apns->sendReg($push_id, 'Test Delay', 'Body'); + + return 0; } } diff --git a/src/Command/TestRiderTrackerCommand.php b/src/Command/TestRiderTrackerCommand.php index 7b26a9ed..c975c16e 100644 --- a/src/Command/TestRiderTrackerCommand.php +++ b/src/Command/TestRiderTrackerCommand.php @@ -42,5 +42,7 @@ class TestRiderTrackerCommand extends Command { echo "Invalid rider id." . "\n"; } + + return 0; } } diff --git a/src/Command/UpdateCustomerVehicleBatteryCommand.php b/src/Command/UpdateCustomerVehicleBatteryCommand.php index 6726c2f0..3b858711 100644 --- a/src/Command/UpdateCustomerVehicleBatteryCommand.php +++ b/src/Command/UpdateCustomerVehicleBatteryCommand.php @@ -18,9 +18,9 @@ class UpdateCustomerVehicleBatteryCommand extends Command protected $em; protected $custvehicle_hash; - public function __construct(EntityManagerInterface $om) + public function __construct(EntityManagerInterface $em) { - $this->em = $om; + $this->em = $em; $this->custvehicle_hash = []; parent::__construct(); @@ -63,5 +63,7 @@ class UpdateCustomerVehicleBatteryCommand extends Command } } } + + return 0; } } diff --git a/src/Command/UpdateCustomerVehicleWarrantyCommand.php b/src/Command/UpdateCustomerVehicleWarrantyCommand.php index 98fec1cb..20fad9b1 100644 --- a/src/Command/UpdateCustomerVehicleWarrantyCommand.php +++ b/src/Command/UpdateCustomerVehicleWarrantyCommand.php @@ -94,5 +94,7 @@ class UpdateCustomerVehicleWarrantyCommand extends Command $this->em->detach($row[0]); } + + return 0; } } diff --git a/src/Command/UploadCleanupCommand.php b/src/Command/UploadCleanupCommand.php index 03831ee9..0bb98353 100644 --- a/src/Command/UploadCleanupCommand.php +++ b/src/Command/UploadCleanupCommand.php @@ -7,6 +7,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Exception\IOExceptionInterface; + use Doctrine\ORM\EntityManagerInterface; use App\Entity\Rider; @@ -18,11 +19,11 @@ use DirectoryIterator; class UploadCleanupCommand extends Command { private $encoder_factory; - private $object_manager; + private $em; - public function __construct(EntityManagerInterface $om, Filesystem $fs, FileUploader $uploader) + public function __construct(EntityManagerInterface $em, Filesystem $fs, FileUploader $uploader) { - $this->object_manager = $om; + $this->em = $em; $this->filesystem = $fs; $this->uploader = $uploader; @@ -41,7 +42,7 @@ class UploadCleanupCommand extends Command $output->writeln('Retrieving file whitelist...'); // get all image filenames - $em = $this->object_manager; + $em = $this->em; $riders = $em->getRepository(Rider::class)->findAll(); $whitelist = ['.gitkeep']; @@ -95,5 +96,7 @@ class UploadCleanupCommand extends Command $output->writeln('Deleted ' . $i . ' orphaned file(s). Done!'); else $output->writeln('No files found. Done!'); + + return 0; } } diff --git a/src/Command/UserCreateCommand.php b/src/Command/UserCreateCommand.php index 025086af..697f66fe 100644 --- a/src/Command/UserCreateCommand.php +++ b/src/Command/UserCreateCommand.php @@ -8,6 +8,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; + use Doctrine\ORM\EntityManagerInterface; use App\Entity\User; @@ -16,12 +17,12 @@ use App\Entity\Role; class UserCreateCommand extends Command { private $encoder_factory; - private $object_manager; + private $em; - public function __construct(EncoderFactoryInterface $ef, EntityManagerInterface $om) + public function __construct(EncoderFactoryInterface $ef, EntityManagerInterface $em) { $this->encoder_factory = $ef; - $this->object_manager = $om; + $this->em = $em; parent::__construct(); } @@ -49,7 +50,7 @@ class UserCreateCommand extends Command // get entity manager // $em = $this->getContainer()->get('doctrine')->getEntityManager(); - $em = $this->object_manager; + $em = $this->em; // build user $user->setUsername($username) @@ -66,5 +67,7 @@ class UserCreateCommand extends Command $em->persist($user); $em->flush(); + + return 0; } } diff --git a/src/Controller/APIController.php b/src/Controller/APIController.php index 9765ad8f..eec92bf7 100644 --- a/src/Controller/APIController.php +++ b/src/Controller/APIController.php @@ -29,6 +29,9 @@ use App\Service\RisingTideGateway; use App\Service\MQTTClient; use App\Service\GeofenceTracker; use App\Service\RiderTracker; +use App\Service\MapTools; +use App\Service\InventoryManager; +use App\Service\RiderAssignmentHandlerInterface; use App\Entity\MobileSession; use App\Entity\Customer; @@ -45,12 +48,14 @@ use App\Entity\Service; use App\Entity\Partner; use App\Entity\Review; use App\Entity\PrivacyPolicy; +use App\Entity\Hub; use DateTime; +use DateInterval; use Exception; // mobile API -class APIController extends Controller +class APIController extends Controller implements LoggedController { protected $session; @@ -817,7 +822,9 @@ class APIController extends Controller return $res->getReturnResponse(); } - public function requestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo) + public function requestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo, + MapTools $map_tools, InventoryManager $im, MQTTClient $mclient, + RiderAssignmentHandlerInterface $rah) { // check required parameters and api key $required_params = [ @@ -982,16 +989,87 @@ class APIController extends Controller $invoice = $ic->generateInvoice($icrit); $jo->setInvoice($invoice); + // assign hub and rider + if (($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) || + ($jo->getServicetype() == ServiceType::BATTERY_REPLACEMENT_WARRANTY)) + { + // get nearest hub + // $nearest_hub = $this->findNearestHubWithInventory($jo, $batt, $em, $map_tools, $im); + $nearest_hub = $this->findNearestHub($jo, $em, $map_tools); + } + else + { + $nearest_hub = $this->findNearestHub($jo, $em, $map_tools); + } + + if (!empty($nearest_hub)) + { + // assign rider + $available_riders = $nearest_hub->getAvailableRiders(); + if (count($available_riders) > 0) + { + $assigned_rider = null; + if (count($available_riders) > 1) + { + // TODO: the setting of riders into an array + // will no longer be necessary when the contents + // of randomizeRider changes + $riders = []; + foreach ($available_riders as $rider) + { + $riders[] = $rider; + } + + $assigned_rider = $this->randomizeRider($riders); + } + else + $assigned_rider = $available_riders[0]; + + $jo->setHub($nearest_hub); + $jo->setRider($assigned_rider); + $jo->setStatus(JOStatus::ASSIGNED); + + $assigned_rider->setAvailable(false); + } + } + $em->persist($jo); $em->persist($invoice); - // add event log + // add event log for JO $event = new JOEvent(); $event->setDateHappen(new DateTime()) ->setTypeID(JOEventType::CREATE) ->setJobOrder($jo); $em->persist($event); + // check JO status + if ($jo->getStatus() == JOStatus::ASSIGNED) + { + // add event logs for hub and rider assignments + $hub_assign_event = new JOEvent(); + $hub_assign_event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::HUB_ASSIGN) + ->setJobOrder($jo); + + $em->persist($hub_assign_event); + + $rider_assign_event = new JOEvent(); + $rider_assign_event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::RIDER_ASSIGN) + ->setJobOrder($jo); + + $em->persist($rider_assign_event); + + // user mqtt event + $payload = [ + 'event' => 'outlet_assign' + ]; + $mclient->sendEvent($jo, $payload); + + $rah->assignJobOrder($jo, $jo->getRider()); + } + $em->flush(); // make invoice json data @@ -2213,4 +2291,177 @@ class APIController extends Controller return $cust; } + protected function findNearestHub($jo, EntityManagerInterface $em, MapTools $map_tools) + { + // get the nearest 10 hubs + $selected_hub = null; + $hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10, date("H:i:s")); + + $nearest_hubs_with_distance = []; + $nearest_branch_codes = []; + foreach ($hubs as $hub) + { + $nearest_hubs_with_distance[] = $hub; + //if (!empty($hub['hub']->getBranchCode())) + // $nearest_branch_codes[] = $hub['hub']->getBranchCode(); + } + + // check if nearest hubs have branch codes + //if (count($nearest_branch_codes) == 0) + // return $selected_hub; + + // assume all 10 have stock + // find the nearest hub with available riders + $nearest = null; + foreach ($nearest_hubs_with_distance as $nhd) + { + // get number of available riders + $count_riders = count($nhd['hub']->getAvailableRiders()); + + // get number of advance orders in the next 3 hours + $time_now = new DateTime(); + $date_end = new DateTime(); + $date_end->add(new DateInterval('PT2H')); + + // NOTE: get advance orders via query + // get JOs assigned to hub that are advance orders and scheduled within X hours with + // for rider assignment status + $query = $em->createQuery('select count(jo) from App\Entity\JobOrder jo where jo.hub = :hub and jo.flag_advance = true and jo.date_schedule <= :date_end and jo.status = :status'); + $count_advance_orders = $query->setParameters([ + 'hub' => $nhd['hub'], + 'date_end' => $date_end, + 'status' => JOStatus::RIDER_ASSIGN, + ]) + ->setMaxResults(1) + ->getSingleScalarResult(); + + error_log('HUB - ' . $nhd['hub']->getID()); + error_log('RIDER COUNT - ' . $count_riders); + error_log('ADVANCE ORDER COUNT - ' . $count_advance_orders); + + // if (count($nhd['hub']->getAvailableRiders()) > 0) + // if we have more riders than we have advance orders + if ($count_riders - $count_advance_orders > 0) + { + if (empty($nearest)) + $nearest = $nhd; + else + { + if ($nhd['distance'] < $nearest['distance']) + $nearest = $nhd; + } + } + } + + $selected_hub = $nearest['hub']; + + return $selected_hub; + } + + protected function findNearestHubWithInventory($jo, Battery $batt, EntityManagerInterface $em, + MapTools $map_tools, InventoryManager $im) + { + // get the nearest 10 hubs + $selected_hub = null; + $hubs = $map_tools->getClosestOpenHubs($jo->getCoordinates(), 10, date("H:i:s")); + + $nearest_hubs_with_distance = []; + $nearest_branch_codes = []; + foreach ($hubs as $hub) + { + $nearest_hubs_with_distance[] = $hub; + //if (!empty($hub['hub']->getBranchCode())) + // $nearest_branch_codes[] = $hub['hub']->getBranchCode(); + } + + // check if nearest hubs have branch codes + //if (count($nearest_branch_codes) == 0) + // return $selected_hub; + + // assume all 10 have stock + // find the nearest hub with available riders + $nearest = null; + foreach ($nearest_hubs_with_distance as $nhd) + { + if (count($nhd['hub']->getAvailableRiders()) > 0) + { + if (empty($nearest)) + $nearest = $nhd; + else + { + if ($nhd['distance'] < $nearest['distance']) + $nearest = $nhd; + } + } + } + + $selected_hub = $nearest['hub']; + + // uncomment this snippet when inventory check becomes active + // get battery sku + /* + if ($batt != null) + { + $skus[] = $batt->getSAPCode(); + + // api call to check inventory + // pass the list of branch codes of nearest hubs and the skus + // go through returned list of branch codes + // bypass inventory check for now + // $hubs_with_inventory = $im->getBranchesInventory($nearest_branch_codes, $skus); + if (!empty($hubs_with_inventory)) + { + $nearest = []; + $flag_hub_found = false; + foreach ($hubs_with_inventory as $hub_with_inventory) + { + // find hub according to branch code + $found_hub = $em->getRepository(Hub::class)->findOneBy(['branch_code' => $hub_with_inventory['BranchCode']]); + if ($found_hub != null) + { + // check rider availability + if (count($found_hub->getAvailableRiders()) > 0) + { + // check against nearest hubs with distance + foreach ($nearest_hubs_with_distance as $nhd) + { + // get distance of hub from location, compare with $nearest. if less, replace nearest + if ($found_hub->getID() == $nhd['hub']->getID()) + { + if (empty($nearest)) + { + $nearest = $nhd; + $flag_hub_found = true; + } + else + { + if ($nhd['distance'] < $nearest['distance']) + { + $nearest = $nhd; + $flag_hub_found = true; + } + } + } + } + } + } + } + $selected_hub = $nearest['hub']; + } + } */ + return $selected_hub; + } + + protected function randomizeRider($riders) + { + // TODO: get redis to track the sales per rider per day + // check the time they came in + // for now, randomize the rider + $selected_index = array_rand($riders); + + $selected_rider = $riders[$selected_index]; + + return $selected_rider; + } + } diff --git a/src/Controller/CAPI/WarrantyController.php b/src/Controller/CAPI/WarrantyController.php index 83ecf74a..e5f5437e 100644 --- a/src/Controller/CAPI/WarrantyController.php +++ b/src/Controller/CAPI/WarrantyController.php @@ -502,6 +502,48 @@ class WarrantyController extends APIController } + public function getWarrantiesBySerialList(Request $req, EntityManagerInterface $em) + { + $this->denyAccessUnlessGranted('warranty.list.serial', null, 'No access.'); + + error_log('getWarrantiesBySerialList'); + // required parameters + $params = [ + 'serial_list', + ]; + + $msg = $this->checkRequiredParameters($req, $params); + error_log('msg - ' . $msg); + if ($msg) + return new APIResponse(false, $msg); + + $serial_list = $req->request->get('serial_list'); + + if (empty($serial_list)) + return new APIResponse(false, 'Empty serial numbers list.'); + + $warranty_list = []; + $warr_not_found_list = []; + foreach ($serial_list as $serial) + { + $clean_serial = $this->cleanSerial($serial); + $warr = $em->getRepository(Warranty::class)->findOneBy(['serial' => $clean_serial]); + + if ($warr == null) + $warr_not_found_list[] = $clean_serial; + else + $warranty_list[] = $this->generateWarrantyData($warr); + } + + + $data = [ + 'warranties_found' => $warranty_list, + 'warranties_not_found' => $warr_not_found_list, + ]; + + return new APIResponse(true, 'Warranties found.', $data); + } + protected function getCustomerFromMobile($em, $warranty) { $w_mobile = $warranty->getMobileNumber(); diff --git a/src/Controller/CustomerController.php b/src/Controller/CustomerController.php index 7421fec0..17f8e7bc 100644 --- a/src/Controller/CustomerController.php +++ b/src/Controller/CustomerController.php @@ -181,7 +181,10 @@ class CustomerController extends Controller public function getCustomerVehicles(Request $req, CustomerHandlerInterface $cust_handler) { - if (!$this->isGranted('jo_in.list')) { + if (!(($this->isGranted('jo_onestep.form')) || + ($this->isGranted('jo_walkin.form')) || + ($this->isGranted('jo_in.list')))) + { $exception = $this->createAccessDeniedException('No access.'); throw $exception; } diff --git a/src/Controller/HubController.php b/src/Controller/HubController.php index e1d41525..d6334c80 100644 --- a/src/Controller/HubController.php +++ b/src/Controller/HubController.php @@ -150,7 +150,9 @@ class HubController extends Controller ->setContactNumbers($req->request->get('contact_nums')) ->setTimeOpen($time_open) ->setTimeClose($time_close) - ->setCoordinates($point); + ->setCoordinates($point) + ->setBranchCode($req->request->get('branch_code', '')) + ->setStatusOpen($req->request->get('status_open') ?? false); } protected function setQueryFilters($datatable, QueryBuilder $query) diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index 1d1d84c1..d8866203 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -5,6 +5,7 @@ namespace App\Controller; use App\Ramcar\JOStatus; use App\Ramcar\InvoiceCriteria; use App\Ramcar\CMBServiceType; +use App\Ramcar\ServiceType; use App\Entity\CustomerVehicle; use App\Entity\Promo; @@ -20,6 +21,7 @@ use App\Service\GISManagerInterface; use App\Service\MapTools; use App\Service\MQTTClient; use App\Service\APNSClient; +use App\Service\InventoryManager; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -96,7 +98,9 @@ class JobOrderController extends Controller $this->denyAccessUnlessGranted('jo_open.edit', null, 'No access.'); $error_array = []; - $error_array = $jo_handler->generateJobOrder($req, $id); + $result = $jo_handler->openEditJobOrder($req, $id); + + $error_array = $result['error_array']; // check if any errors were found if (!empty($error_array)) { @@ -145,7 +149,9 @@ class JobOrderController extends Controller // initialize error list $error_array = []; $id = -1; - $error_array = $jo_handler->generateJobOrder($req, $id); + $result = $jo_handler->generateJobOrder($req, $id); + + $error_array = $result['error_array']; // check if any errors were found if (!empty($error_array)) { @@ -933,12 +939,18 @@ class JobOrderController extends Controller /** * @ParamConverter("jo", class="App\Entity\JobOrder") */ - public function popupInfo(JobOrder $jo) + public function popupInfo(JobOrder $jo, JobOrderHandlerInterface $jo_handler) { if ($jo == null) return new Response('No job order data'); - return $this->render('job-order/popup.html.twig', [ 'jo' => $jo ]); + // get the right template + $template = $jo_handler->getTwigTemplate('jo_popup'); + + $params['jo'] = $jo; + + // response + return $this->render($template, $params); } /** @@ -1053,5 +1065,177 @@ class JobOrderController extends Controller ]); } + /** + * @Menu(selected="jo_autoassign") + */ + public function autoAssignForm(GISManagerInterface $gis, JobOrderHandlerInterface $jo_handler) + { + $this->denyAccessUnlessGranted('jo_autoassign.test', null, 'No access.'); + + $params = $jo_handler->initializeIncomingForm(); + + $params['submit_url'] = $this->generateUrl('jo_autoassign_test_submit'); + $params['return_url'] = $this->generateUrl('jo_autoassign'); + $params['map_js_file'] = $gis->getJSJOFile(); + + $template = 'job-order/autoassign.form.html.twig'; + + // response + return $this->render($template, $params); + } + + public function autoAssignSubmit(Request $req, JobOrderHandlerInterface $jo_handler, + EntityManagerInterface $em, MapTools $map_tools, + InventoryManager $im) + { + $this->denyAccessUnlessGranted('jo_autoassign.test', null, 'No access.'); + + // initialize error list + $error_array = []; + $id = -1; + $result = $jo_handler->generateJobOrder($req, $id); + + $error_array = $result['error_array']; + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // get job order + $jo = $result['job_order']; + + if (($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) || + ($jo->getServicetype() == ServiceType::BATTERY_REPLACEMENT_WARRANTY)) + $this->autoAssignHubAndRider($jo, $em, $map_tools, $im); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + + } + + protected function autoAssignHubAndRider($jo, EntityManagerInterface $em, + MapTools $map_tools, InventoryManager $im) + { + // get the nearest 10 hubs + // TODO: move this snippet to a function + $hubs = $map_tools->getClosestHubs($jo->getCoordinates(), 10, date("H:i:s")); + + $nearest_hubs = []; + $nearest_hubs_with_distance = []; + + foreach ($hubs as $hub) + { + $nearest_hubs_with_distance[] = $hub; + // TODO: uncomment this when we have branch codes in data + // and when they have real codes and not test ones + //$nearest_branch_codes[] = $hub['hub']->getBranchCode(); + $nearest_branch_codes = ['WestAve','jay_franchise']; + } + + // get battery sku + $invoice = $jo->getInvoice(); + if ($invoice != null) + { + $items = $invoice->getItems(); + $skus = []; + foreach ($items as $item) + { + // TODO: uncomment this when they have real codes and not test ones + //$skus[] = $item->getBattery()->getSAPCode(); + $skus[] = 'WMGD31EL-CPNM0-L'; + } + + // api call to check inventory + // pass the list of branch codes of nearest hubs and the skus + // go through returned list of branch codes + $hubs_with_inventory = $im->getBranchesInventory($nearest_branch_codes, $skus); + if (!empty($hubs_with_inventory)) + { + $nearest = []; + $flag_hub_found = false; + foreach ($hubs_with_inventory as $hub_with_inventory) + { + // find hub according to branch code + $found_hub = $em->getRepository(Hub::class)->findOneBy(['branch_code' => $hub_with_inventory['BranchCode']]); + if ($found_hub != null) + { + // check rider availability + if (count($found_hub->getAvailableRiders()) > 0) + { + // check against nearest hubs with distance + foreach ($nearest_hubs_with_distance as $nhd) + { + // get distance of hub from location, compare with $nearest. if less, replace nearest + if ($found_hub->getID() == $nhd['hub']->getID()) + { + if (empty($nearest)) + { + $nearest = $nhd; + $flag_hub_found = true; + } + else + { + if ($nhd['distance'] < $nearest['distance']) + { + $nearest = $nhd; + $flag_hub_found = true; + } + } + } + } + } + } + } + + if ($flag_hub_found) + { + $jo->setHub($nearest['hub']); + + $hub_riders = $nearest['hub']->getAvailableRiders(); + $rider = null; + if (count($hub_riders) > 1) + { + // TODO: this will no longer be necessary when the contents + // of randomizeRider changes + $available_riders = []; + foreach ($hub_riders as $rider) + { + $available_riders[] = $rider; + } + + $rider = $this->randomizeRider($available_riders); + } + else + $rider = $hub_riders[0]; + + $jo->setRider($rider); + + $jo->setStatus(JOStatus::ASSIGNED); + + $em->persist($jo); + $em->flush(); + } + } + } + } + + protected function randomizeRider($riders) + { + // TODO: get redis to track the sales per rider per day + // check the time they came in + // for now, randomize the rider + $selected_index = array_rand($riders); + + $selected_rider = $riders[$selected_index]; + + return $selected_rider; + } } diff --git a/src/Controller/LoggedController.php b/src/Controller/LoggedController.php new file mode 100644 index 00000000..00e7c02a --- /dev/null +++ b/src/Controller/LoggedController.php @@ -0,0 +1,7 @@ +time_open = new DateTime(); $this->time_close = new DateTime(); $this->riders = new ArrayCollection(); $this->outlets = new ArrayCollection(); + $this->status_open = true; } public function getRiders() @@ -114,4 +127,27 @@ class Hub { return $this->outlets; } + + public function setBranchCode($branch_code) + { + $this->branch_code = $branch_code; + return $this; + } + + public function getBranchCode() + { + return $this->branch_code; + } + + public function setStatusOpen($status = true) + { + $this->status_open = $status; + return $this; + } + + public function isStatusOpen() + { + return $this->status_open; + } + } diff --git a/src/EventSubscriber/LogSubscriber.php b/src/EventSubscriber/LogSubscriber.php new file mode 100644 index 00000000..1f62f577 --- /dev/null +++ b/src/EventSubscriber/LogSubscriber.php @@ -0,0 +1,72 @@ +api_log_flag = $api_log_flag; + } + + public function onKernelController(ControllerEvent $event) + { + if ($this->api_log_flag == 'false') + return; + + $controller = $event->getController(); + $this->allow = false; + + // when a controller class defines multiple action methods, the controller + // is returned as [$controllerInstance, 'methodName'] + if (is_array($controller)) + $controller = $controller[0]; + + if (!($controller instanceof LoggedController)) + return; + + $this->allow = true; + + $req = $event->getRequest(); + $reqdata = [ + $req->getPathInfo(), + $req->getMethod(), + $req->query->all(), + $req->request->all(), + ]; + error_log(print_r($reqdata, true)); + } + + public function onKernelResponse(ResponseEvent $event) + { + if ($this->api_log_flag == 'false') + return; + + if (!$this->allow) + return; + + $resp = $event->getResponse(); + + $content = $resp->getContent(); + + error_log(print_r($content, true)); + } + + public static function getSubscribedEvents() + { + return [ + KernelEvents::CONTROLLER => 'onKernelController', + KernelEvents::RESPONSE => 'onKernelResponse', + ]; + } +} diff --git a/src/Service/InventoryManager.php b/src/Service/InventoryManager.php new file mode 100644 index 00000000..4dc5a8b1 --- /dev/null +++ b/src/Service/InventoryManager.php @@ -0,0 +1,80 @@ +api_url = $api_url; + $this->api_ocp_key = $api_ocp_key; + $this->api_auth_prefix = $api_auth_prefix; + $this->api_auth_token = $api_auth_token; + } + + public function getBranchesInventory($nearest_branch_codes, $skus) + { + // api call to check inventory + // pass the list of branch codes and the skus + $data = [ + "BranchCodes" => $nearest_branch_codes, + "Skus" => $skus, + ]; + + $json_data = json_encode($data); + error_log('JSON ' . $json_data); + + // initializes a new cURL session + $curl = curl_init($this->api_url); + + // Set the CURLOPT_RETURNTRANSFER option to true + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + + // Set the CURLOPT_POST option to true for POST request + curl_setopt($curl, CURLOPT_POST, true); + + // Set the request data as JSON using json_encode function + curl_setopt($curl, CURLOPT_POSTFIELDS, $json_data); + + // set timeout + curl_setopt($curl, CURLOPT_TIMEOUT, 300); + + $authorization = $this->api_auth_prefix . ' ' . $this->api_auth_token; + $ocp = $this->api_ocp_key; + + curl_setopt($curl, CURLOPT_HTTPHEADER, [ + 'Authorization: ' . $authorization, + 'Content-Type: application/json', + 'Ocp-Apim-Subscription-Key: ' . $ocp, + ]); + + $response = curl_exec($curl); + + // close cURL session + curl_close($curl); + + // response is array of these + // { + // "Id": 37, + // "BranchCode": "WestAve", + // "BranchName": "West ave. Branch", + // "BranchRole": 0, + // "Name": "3SMF / GOLD ", + // "SapCode": "WMGD31EL-CPNM0-L", + // "Quantity": 38 + // } + + // check if the response is empty + $results = []; + if (!empty($response)) + $results = json_decode($response, true); + + return $results; + } + +} diff --git a/src/Service/JobOrderHandler/CMBJobOrderHandler.php b/src/Service/JobOrderHandler/CMBJobOrderHandler.php index bd268c51..1bce48c3 100644 --- a/src/Service/JobOrderHandler/CMBJobOrderHandler.php +++ b/src/Service/JobOrderHandler/CMBJobOrderHandler.php @@ -2838,6 +2838,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface $this->template_hash['jo_onestep_edit_form'] = 'job-order/cmb.form.onestep.html.twig'; $this->template_hash['jo_walkin_form'] = 'job-order/cmb.form.walkin.html.twig'; $this->template_hash['jo_walkin_edit_form'] = 'job-order/cmb.form.walkin.html.twig'; + $this->template_hash['jo_popup'] = 'job-order/cmb.popup.html.twig'; } protected function checkTier($tier) diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 4e58b05c..1a4b7e3f 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -262,7 +262,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface return $params; } - // creates/updates job order + // creates job order public function generateJobOrder(Request $req, $id) { // initialize error list @@ -401,7 +401,113 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } } - return $error_array; + $data['error_array'] = $error_array; + + if ($jo != null) + { + $data['job_order'] = $jo; + } + + return $data; + } + + // updates job order + public function openEditJobOrder(Request $req, $id) + { + $em = $this->em; + + $obj = $em->getRepository(JobOrder::class)->find($id); + + // initialize error list + $error_array = []; + + // make sure this object exists + if (empty($obj)) + throw $this->createNotFoundException('The item does not exist'); + + // check if lat and lng are provided + if (empty($req->request->get('coord_lng')) || empty($req->request->get('coord_lat'))) { + $error_array['coordinates'] = 'No map coordinates provided. Please click on a location on the map.'; + } + + if (empty($error_array)) + { + // get current user + $user = $this->security->getUser(); + + // coordinates + $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); + + $stype = $req->request->get('service_type'); + + // set and save values + $obj->setDateSchedule(DateTime::createFromFormat("d M Y h:i A", $req->request->get('date_schedule_date') . " " . $req->request->get('date_schedule_time'))) + ->setCoordinates($point) + ->setAdvanceOrder($req->request->get('flag_advance') ?? false) + ->setServiceType($stype) + ->setWarrantyClass($req->request->get('warranty_class')) + ->setSource($req->request->get('source')) + ->setDeliveryInstructions($req->request->get('delivery_instructions')) + ->setTier1Notes($req->request->get('tier1_notes')) + ->setTier2Notes($req->request->get('tier2_notes')) + ->setDeliveryAddress($req->request->get('delivery_address')) + ->setORName($req->request->get('or_name')) + ->setPromoDetail($req->request->get('promo_detail')) + ->setModeOfPayment($req->request->get('mode_of_payment')) + ->setLandmark($req->request->get('landmark')); + + // did they change invoice? + $invoice_items = $req->request->get('invoice_items', []); + $promo_id = $req->request->get('invoice_promo'); + $invoice_change = $req->request->get('invoice_change', 0); + if ($invoice_change) + { + $this->ic->generateInvoiceCriteria($obj, $promo_id, $invoice_items, $error_array); + } + + // validate + $errors = $this->validator->validate($obj); + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // the event + $event = new JOEvent(); + $event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::OPEN_EDIT) + ->setJobOrder($obj); + + if ($user != null) + { + $event->setUser($user); + } + + $em->persist($event); + + // validated! save the entity + $em->flush(); + } + + $data['error_array'] = $error_array; + + if ($obj != null) + { + $data['job_order'] = $obj; + } + + return $data; + } // dispatch job order @@ -2394,8 +2500,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $this->template_hash['jo_list_fulfillment'] = 'job-order/list.fulfillment.html.twig'; $this->template_hash['jo_list_open'] = 'job-order/list.open.html.twig'; $this->template_hash['jo_list_all'] = 'job-order/list.all.html.twig'; - $this->template_hash['jo_onestep'] = 'job-order/form.onestep.html.twig'; - $this->template_hash['jo_onestep_edit_form'] = 'job-order/form.onestep.html.twig'; + $this->template_hash['jo_popup'] = 'job-order/popup.html.twig'; } protected function checkTier($tier) diff --git a/src/Service/MapTools.php b/src/Service/MapTools.php index 6aa5848c..b60130a0 100644 --- a/src/Service/MapTools.php +++ b/src/Service/MapTools.php @@ -17,10 +17,15 @@ class MapTools // google maps api key protected $gmaps_api_key; - public function __construct(EntityManagerInterface $em, $gmaps_api_key) + // customer distance limit + protected $cust_dist_limit; + + public function __construct(EntityManagerInterface $em, $gmaps_api_key, + $cust_dist_limit) { $this->em = $em; $this->gmaps_api_key = $gmaps_api_key; + $this->cust_dist_limit = $cust_dist_limit; } protected function mapGetDistances(Point $point, $hubs) @@ -148,6 +153,57 @@ class MapTools return $final_data; */ } + + // NOTE: only the API calls this + public function getClosestOpenHubs(Point $point, $limit, $time = false) + { + // get closest hubs based on st_distance function from db + $query = $this->em->createQuery('SELECT h, st_distance(h.coordinates, point(:lng, :lat)) as dist FROM App\Entity\Hub h' . ($time ? ' WHERE h.status_open = true AND :time BETWEEN h.time_open AND h.time_close' : '') . ' ORDER BY dist') + ->setParameter('lat', $point->getLatitude()) + ->setParameter('lng', $point->getLongitude()); + + if ($time) { + $query->setParameter('time', $time); + } + + $query->setMaxResults($limit); + + // error_log($query->getSql()); + $result = $query->getResult(); + + $hubs = []; + $final_data = []; + foreach ($result as $row) + { + //error_log($row[0]->getName() . ' - ' . $row['dist']); + $hubs[] = $row[0]; + + // get coordinates of hub + $hub_coordinates = $row[0]->getCoordinates(); + + $cust_lat = $point->getLatitude(); + $cust_lng = $point->getLongitude(); + + $hub_lat = $hub_coordinates->getLatitude(); + $hub_lng = $hub_coordinates->getLongitude(); + + // get distance in kilometers from customer point to hub point + $dist = $this->distance($cust_lat, $cust_lng, $hub_lat, $hub_lng); + + if ($dist < $this->cust_dist_limit) + { + $final_data[] = [ + 'hub' => $row[0], + 'db_distance' => $row['dist'], + 'distance' => $dist, + 'duration' => 0, + ]; + } + } + + error_log('nearest open hubs count: ' . count($final_data)); + return $final_data; + } protected function distance($lat1, $lon1, $lat2, $lon2) { diff --git a/src/Service/RiderAPIHandler/ResqRiderAPIHandler.php b/src/Service/RiderAPIHandler/ResqRiderAPIHandler.php index 4a3ea62a..23a5be47 100644 --- a/src/Service/RiderAPIHandler/ResqRiderAPIHandler.php +++ b/src/Service/RiderAPIHandler/ResqRiderAPIHandler.php @@ -479,7 +479,9 @@ class ResqRiderAPIHandler implements RiderAPIHandlerInterface return $data; } - // TODO: tag rider as available + // tag rider as available + $rider = $this->session->getRider(); + $rider->setAvailable(true); $this->em->flush(); @@ -511,7 +513,8 @@ class ResqRiderAPIHandler implements RiderAPIHandlerInterface ->setRider($rider); $this->em->persist($event); - // TODO: tag rider as unavailable + // tag rider as unavailable + $rider->setAvailable(false); // save to customer vehicle battery record $this->jo_handler->updateVehicleBattery($jo); diff --git a/templates/home.html.twig b/templates/home.html.twig index 19e99011..05fedfd9 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -5,7 +5,7 @@ {% endblock %} {% block body %} -
+
{% endblock %} diff --git a/templates/hub/form.html.twig b/templates/hub/form.html.twig index df8a7190..aed63021 100644 --- a/templates/hub/form.html.twig +++ b/templates/hub/form.html.twig @@ -82,6 +82,24 @@
+
+
+ + + +
+
+ + + +
+