diff --git a/config/acl.yaml b/config/acl.yaml index fb623290..838d46dd 100644 --- a/config/acl.yaml +++ b/config/acl.yaml @@ -258,14 +258,6 @@ access_keys: label: Edit - id: joborder.cancel label: Cancel - - id: jo_onestep.form - label: One-step Process - - id: jo_onestep.edit - label: One-step Process Edit - - id: jo_walkin.form - label: Walk-in - - id: jo_walkin.edit - label: Walk-in Edit - id: jo_autoassign.test label: Autoassign Test - id: jo_hub.list diff --git a/config/api_acl.yaml b/config/api_acl.yaml index 2e5bbdd3..2da7b9df 100644 --- a/config/api_acl.yaml +++ b/config/api_acl.yaml @@ -67,3 +67,107 @@ access_keys: acls: - id: dealer.list label: List + + - id: mobile_user + label: Mobile User Access + acls: + - id: mobile_user.register + label: Register Mobile User + - id: mobile_user.confirm.number + label: Confirm Number + - id: mobile_user.validate.code + label: Validate Code + - id: mobile_user.get.info + label: Get Customer Info + - id: mobile_user.update.info + label: Update Customer Info + - id: mobile_user.get.status + label: Get Status + - id: mobile_user.resend.code + label: Resend Code + - id: mobile_user.version.check + label: Version Check + - id: mobile_user.update.deviceid + label: Update Device ID + - id: mobile_user.privacy.settings + label: Privacy Settings + - id: mobile_vmanufacturer + label: Mobile Vehicle Manufacturer Access + acls: + - id: mobile_vmanufacturer.list + label: List Vehicle Manufacturers + - id: mobile_vehicle + label: Mobile Vehicle Make Access + acls: + - id: mobile_vehicle.list + label: List Vehicle Makes + - id: mobile_customer_vehicle + label: Mobile Customer Vehicle Access + acls: + - id: mobile_customer_vehicle.add + label: Add Mobile Customer Vehicle + - id: mobile_customer_vehicle.update + label: Update Mobile Customer Vehicle + - id: mobile_customer_vehicle.list + label: List Mobile Customer Vehicles + - id: mobile_promo + label: Mobile Promo Access + acls: + - id: mobile_promo.list + label: List Promos + - id: mobile_battery + label: Mobile Battery Access + acls: + - id: mobile_battery.list + label: List Compatible Batteries + - id: mobile_service + label: Mobile Service Access + acls: + - id: mobile_service.list + label: List Services + - id: mobile_partner + label: Mobile Partner Access + acls: + - id: mobile_partner.list + label: List Mobile Partners + - id: mobile_partner.info + label: Get Partner Info + - id: mobile_partner.review + label: Add Partner Review + - id: mobile_rider + label: Mobile Rider Access + acls: + - id: mobile_rider.status.get + label: Get Rider Status + - id: mobile_rider.rating.add + label: Add Rider Rating + - id: job_order + label: Mobile Job Order Access + acls: + - id: mobile_jo.request + label: Request Job Order + - id: mobile_jo.get.estimate + label: Get Estimate + - id: mobile_jo.get.ongoing + label: Get Ongoing Job Order + - id: mobile_jo.cancel + label: Cancel Job Order + - id: mobile_jo.get.history + label: Get Job Order History + - id: mobile_jo.get.invoice + label: Get Job Order Invoice + - id: mobile_jo.location.support + label: Check Location Support + - id: mobile_jo.nearest_hub.get + label: Get Nearest Hub and Slots + - id: mobile_jo.schedule_option.status + label: Schedule Option Status + - id: mobile_warranty + label: Mobile Warranty Access + acls: + - id: mobile_warranty.register.serial + label: Register Warranty Serial + - id: mobile_warranty.check + label: Check Warranty Serial + - id: mobile_warranty.activate + label: Activate Warranty diff --git a/config/cmb.menu.yaml b/config/cmb.menu.yaml deleted file mode 100644 index 9f28b315..00000000 --- a/config/cmb.menu.yaml +++ /dev/null @@ -1,179 +0,0 @@ -main_menu: - - id: home - acl: dashboard.menu - label: Dashboard - icon: flaticon-line-graph - - id: user - acl: user.menu - label: User - icon: flaticon-users - - id: user_list - acl: user.list - label: Users - parent: user - - id: role_list - acl: role.list - label: Roles - parent: user - - - id: apiuser - acl: apiuser.menu - label: API User - icon: flaticon-users - - id: api_user_list - acl: apiuser.list - label: API Users - parent: apiuser - - id: api_role_list - acl: apirole.list - label: API Roles - parent: apiuser - - - id: logistics - acl: logistics.menu - label: Logistics - icon: fa fa-truck - - id: rider_list - 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 - label: Battery - icon: fa fa-battery-3 - - id: battery_list - acl: battery.list - label: Batteries - parent: battery - - id: bmfg_list - acl: bmfg.list - label: Manufacturers - parent: battery - - id: bmodel_list - acl: bmodel.list - label: Models - parent: battery - - id: bsize_list - acl: bsize.list - label: Sizes - parent: battery - - id: promo_list - acl: promo.list - label: Promos - parent: battery - - - id: vehicle - acl: vehicle.menu - label: Vehicle - icon: fa fa-car - - id: vehicle_list - acl: vehicle.list - label: Vehicles - parent: vehicle - - id: vmfg_list - acl: vmfg.list - label: Manufacturers - parent: vehicle - - - id: location - acl: location.menu - label: Location - icon: fa fa-home - - id: outlet_list - acl: outlet.menu - label: Outlet - parent: location - - id: hub_list - acl: hub.menu - label: Hub - parent: location - - id: geofence_list - acl: geofence.menu - label: Geofence - parent: location - - - - id: joborder - acl: joborder.menu - label: Job Order - icon: flaticon-calendar-3 - - id: jo_onestep_form - acl: jo_onestep.form - label: One-step Process - parent: joborder - - id: jo_walkin_form - acl: jo_walkin.form - label: Walk-in - parent: joborder - - id: jo_open - acl: jo_open.list - label: Open - parent: joborder - - id: jo_all - acl: jo_all.list - label: View All - parent: joborder - - - id: support - acl: support.menu - label: Customer Support - icon: flaticon-support - - id: customer_list - acl: customer.list - label: Customers - parent: support - - id: ticket_list - acl: ticket.list - label: Tickets - parent: support - - id: general_search - acl: general.search - label: Search - parent: support - - id: warranty_search - acl: warranty.search - label: Customer Battery Search - parent: support - - id: privacy_policy_list - acl: privacy_policy.list - label: Privacy Policy - parent: support - - id: warranty_list - acl: warranty.list - label: Warranty - parent: support - - id: warranty_upload - acl: warranty.upload - label: Warranty Upload - parent: support - - id: static_content_list - acl: static_content.list - label: Static Content - parent: support - - - id: service - acl: service.menu - label: Other Services - icon: flaticon-squares - - id: service_list - acl: service.list - label: Services - parent: service - - - id: partner - acl: partner.menu - label: Partners - icon: flaticon-network - - id: partner_list - acl: partner.list - label: Partners - parent: partner - - id: review_list - acl: review.list - label: Reviews - parent: partner diff --git a/config/cmb.services.yaml b/config/cmb.services.yaml deleted file mode 100644 index 5f24e525..00000000 --- a/config/cmb.services.yaml +++ /dev/null @@ -1,235 +0,0 @@ -# Put parameters here that don't need to change on each machine where the app is deployed -# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration -parameters: - map_default: - latitude: 14.6091 - longitude: 121.0223 - image_upload_directory: '%kernel.project_dir%/public/uploads' - job_order_refresh_interval: 300000 - api_acl_file: 'api_acl.yaml' - api_access_key: 'api_access_keys' - app_acl_file: 'acl.yaml' - app_access_key: 'access_keys' - cvu_brand_id: "%env(CVU_BRAND_ID)%" - country_code: "%env(COUNTRY_CODE)%" - -services: - # default configuration for services in *this* file - _defaults: - autowire: true # Automatically injects dependencies in your services. - autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. - public: false # Allows optimizing the container by removing unused services; this also means - # fetching services directly from the container via $container->get() won't work. - # The best practice is to be explicit about your dependencies anyway. - - # makes classes in src/ available to be used as services - # this creates a service per class whose id is the fully-qualified class name - App\: - resource: '../src/*' - exclude: '../src/{Entity,Migrations,Tests,Menu,Access}' - - # controllers are imported separately to make sure services can be injected - # as action arguments even if you don't extend any base controller class - App\Controller\: - resource: '../src/Controller' - tags: ['controller.service_arguments'] - - # add more service definitions when explicit configuration is needed - # please note that last definitions always *replace* previous ones - App\Menu\Generator: - arguments: - $router: "@router.default" - $cache_dir: "%kernel.cache_dir%" - $config_dir: "%kernel.root_dir%/../config" - - Catalyst\AuthBundle\Service\ACLGenerator: - arguments: - $router: "@router.default" - $cache_dir: "%kernel.cache_dir%" - $config_dir: "%kernel.root_dir%/../config" - $acl_file: "%app_acl_file%" - - Catalyst\AuthBundle\Service\ACLVoter: - arguments: - $user_class: "App\\Entity\\User" - tags: ['security.voter'] - - Catalyst\AuthBundle\Service\UserChecker: - - App\Service\FileUploader: - arguments: - $target_dir: '%image_upload_directory%' - - App\Service\MapTools: - arguments: - $em: "@doctrine.orm.entity_manager" - $gmaps_api_key: "%env(GMAPS_API_KEY)%" - $cust_dist_limit: "%env(CUST_DISTANCE_LIMIT)%" - - App\Service\RisingTideGateway: - arguments: - $em: "@doctrine.orm.entity_manager" - $user: "%env(RT_USER)%" - $pass: "%env(RT_PASS)%" - $usage_type: "%env(RT_USAGE_TYPE)%" - $shortcode: "%env(RT_SHORTCODE)%" - - App\Service\MQTTClient: - arguments: - $redis_client: "@App\\Service\\RedisClientProvider" - $key: "mqtt_events" - - App\Service\APNSClient: - arguments: - $redis_client: "@App\\Service\\RedisClientProvider" - - App\Service\RedisClientProvider: - arguments: - $scheme: "%env(REDIS_CLIENT_SCHEME)%" - $host: "%env(REDIS_CLIENT_HOST)%" - $port: "%env(REDIS_CLIENT_PORT)%" - $password: "%env(REDIS_CLIENT_PASSWORD)%" - - App\Service\GeofenceTracker: - arguments: - $geofence_flag: "%env(GEOFENCE_ENABLE)%" - - App\Service\WarrantyHandler: - arguments: - $em: "@doctrine.orm.entity_manager" - - App\Command\SetCustomerPrivacyPolicyCommand: - arguments: - $policy_promo: "%env(POLICY_PROMO)%" - $policy_third_party: "%env(POLICY_THIRD_PARTY)%" - $policy_mobile: "%env(POLICY_MOBILE)%" - - App\Command\CreateCustomerFromWarrantyCommand: - arguments: - $cvu_mfg_id: "%env(CVU_MFG_ID)%" - $cvu_brand_id: "%env(CVU_BRAND_ID)%" - - # rider tracker service - App\Service\RiderTracker: - arguments: - $redis_client: "@App\\Service\\RedisClientProvider" - - Catalyst\APIBundle\Security\APIKeyUserProvider: - arguments: - $em: "@doctrine.orm.entity_manager" - - Catalyst\APIBundle\Security\APIKeyAuthenticator: - arguments: - $em: "@doctrine.orm.entity_manager" - - Catalyst\APIBundle\Command\UserCreateCommand: - arguments: - $em: "@doctrine.orm.entity_manager" - tags: ['console.command'] - - Catalyst\APIBundle\Command\TestCommand: - tags: ['console.command'] - - Catalyst\APIBundle\Command\TestAPICommand: - tags: ['console.command'] - - Catalyst\APIBundle\Access\Voter: - arguments: - $acl_gen: "@Catalyst\\APIBundle\\Access\\Generator" - $user_class: "Catalyst\\APIBundle\\Entity\\User" - tags: ['security.voter'] - - Catalyst\APIBundle\Access\Generator: - arguments: - $router: "@router.default" - $cache_dir: "%kernel.cache_dir%" - $config_dir: "%kernel.root_dir%/../config" - $acl_file: "%api_acl_file%" - - Catalyst\MenuBundle\Menu\Generator: - arguments: - $router: "@router.default" - $cache_dir: "%kernel.cache_dir%" - $config_dir: "%kernel.root_dir%/../config" - - Catalyst\MenuBundle\Listener\MenuAnnotationListener: - arguments: - $menu_name: "main_menu" - tags: - - { name: kernel.event_listener, event: kernel.controller, method: onKernelController } - - # invoice generator - App\Service\InvoiceGenerator\CMBInvoiceGenerator: ~ - - # invoice generator interface - App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\CMBInvoiceGenerator" - - # job order generator - App\Service\JobOrderHandler\CMBJobOrderHandler: - arguments: - $country_code: "%env(COUNTRY_CODE)%" - - #job order generator interface - App\Service\JobOrderHandlerInterface: "@App\\Service\\JobOrderHandler\\CMBJobOrderHandler" - - # customer generator - App\Service\CustomerHandler\CMBCustomerHandler: - arguments: - $country_code: "%env(COUNTRY_CODE)%" - - # customer generator interface - App\Service\CustomerHandlerInterface: "@App\\Service\\CustomerHandler\\CMBCustomerHandler" - - # rider assignment - App\Service\RiderAssignmentHandler\CMBRiderAssignmentHandler: ~ - - # rider assignment interface - App\Service\RiderAssignmentHandlerInterface: "@App\\Service\\RiderAssignmentHandler\\CMBRiderAssignmentHandler" - - # rider API service - App\Service\RiderAPIHandler\CMBRiderAPIHandler: - arguments: - $country_code: "%env(COUNTRY_CODE)%" - - # rider API interface - App\Service\RiderAPIHandlerInterface: "@App\\Service\\RiderAPIHandler\\CMBRiderAPIHandler" - - # map manager - #App\Service\GISManager\Bing: ~ - App\Service\GISManager\OpenStreet: ~ - #App\Service\GISManager\Google: ~ - - #App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Bing" - App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet" - #App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Google" - - App\EventListener\JobOrderActiveCacheListener: - arguments: - $jo_cache: "@App\\Service\\JobOrderCache" - $mqtt: "@App\\Service\\MQTTClient" - tags: - - name: 'doctrine.orm.entity_listener' - event: 'postUpdate' - entity: 'App\Entity\JobOrder' - - name: 'doctrine.orm.entity_listener' - event: 'postRemove' - entity: 'App\Entity\JobOrder' - - name: 'doctrine.orm.entity_listener' - event: 'postPersist' - entity: 'App\Entity\JobOrder' - - App\Service\JobOrderCache: - arguments: - $redis_prov: "@App\\Service\\RedisClientProvider" - $active_jo_key: "%env(LOCATION_JO_ACTIVE_KEY)%" - - App\Service\RiderCache: - arguments: - $redis_prov: "@App\\Service\\RedisClientProvider" - $loc_key: "%env(LOCATION_RIDER_ACTIVE_KEY)%" - $status_key: "%env(STATUS_RIDER_KEY)%" - - # API logging - App\EventSubscriber\LogSubscriber: - arguments: - $api_log_flag: "%env(API_LOGGING)%" diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 4339eba4..bae83a76 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -46,6 +46,14 @@ security: provider: api_key_user_provider user_checker: Catalyst\AuthBundle\Service\UserChecker + mobile_api: + pattern: ^\/resqapi\/ + stateless: true + simple_preauth: + authenticator: Catalyst\APIBundle\Security\APIKeyAuthenticator + provider: api_key_user_provider + user_checker: Catalyst\AuthBundle\Service\UserChecker + main: provider: user_provider form_login: diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml index 6ea3a3c5..a67620d6 100644 --- a/config/routes/job_order.yaml +++ b/config/routes/job_order.yaml @@ -176,26 +176,6 @@ jo_reject_hub: controller: App\Controller\JobOrderController::rejectHubSubmit methods: [POST] -jo_onestep_form: - path: /job-order/onestep - controller: App\Controller\JobOrderController::oneStepForm - methods: [GET] - -jo_onestep_submit: - path: /job-order/onestep - controller: App\Controller\JobOrderController::oneStepSubmit - methods: [POST] - -jo_onestep_edit_form: - path: /job-order/onestep/{id}/edit - controller: App\Controller\JobOrderController::oneStepEditForm - methods: [GET] - -jo_onestep_edit_submit: - path: /job-order/onestep/{id}/edit - controller: App\Controller\JobOrderController::oneStepEditSubmit - methods: [POST] - jo_ajax_popup: path: /job-order/{id}/popup controller: App\Controller\JobOrderController::popupInfo @@ -206,26 +186,6 @@ jo_tracker: controller: App\Controller\JobOrderController::tracker methods: [GET] -jo_walkin_form: - path: /job-order/walk-in - controller: App\Controller\JobOrderController::walkInForm - methods: [GET] - -jo_walkin_submit: - path: /job-order/walk-in - controller: App\Controller\JobOrderController::walkInSubmit - methods: [POST] - -jo_walkin_edit_form: - path: /job-order/walk-in/{id} - controller: App\Controller\JobOrderController::walkInEditForm - methods: [GET] - -jo_walkin_edit_submit: - path: /job-order/walk-in/{id} - controller: App\Controller\JobOrderController::walkInEditSubmit - methods: [POST] - jo_autoassign: path: /job-order/autoassign controller: App\Controller\JobOrderController::autoAssignForm diff --git a/config/routes/resqapi.yaml b/config/routes/resqapi.yaml new file mode 100644 index 00000000..df45f624 --- /dev/null +++ b/config/routes/resqapi.yaml @@ -0,0 +1,193 @@ +# resqapi aka mobile api + +# customer/registration-related endpoints +resqapi_register: + path: /resqapi/register + controller: App\Controller\ResqAPI\CustomerController::register + methods: [POST] + +resqapi_confirm: + path: /resqapi/number_confirm + controller: App\Controller\ResqAPI\CustomerController::confirmNumber + methods: [POST] + +resqapi_validate: + path: /resqapi/code_validate + controller: App\Controller\ResqAPI\CustomerController::validateCode + methods: [POST] + +resqapi_info_get: + path: /resqapi/info + controller: App\Controller\ResqAPI\CustomerController::getInfo + methods: [GET] + +resqapi_info_update: + path: /resqapi/info + controller: App\Controller\ResqAPI\CustomerController::updateInfo + methods: [POST] + +resqapi_status: + path: /resqapi/status + controller: App\Controller\ResqAPI\CustomerController::getStatus + methods: [GET] + +resqapi_resend_code: + path: /resqapi/resend_code + controller: App\Controller\ResqAPI\CustomerController:resendCode + methods: [POST] + +resqapi_version_check: + path: /resqapi/version_check + controller: App\Controller\ResqAPI\CustomerController::versionCheck + methods: [POST] + +resqapi_device_id: + path: /resqapi/device_id + controller: App\Controller\ResqAPI\CustomerController:updateDeviceID + methods: [POST] + +resqapi_privacy: + path: /resqapi/privacy + controller: App\Controller\ResqAPI\CustomerController:privacySettings + methods: [POST] + +# vehicle manufacturer and vehicle endpoints +resqapi_vehicle_mfg_list: + path: /resqapi/vehicle/mfgs + controller: App\Controller\ResqAPI\VehicleController::listVehicleManufacturers + methods: [GET] + +resqapi_vehicle_make_list: + path: /resqapi/vehicle/mfgs/{mfg_id}/makes + controller: App\Controller\ResqAPI\VehicleController::listVehicleMakes + methods: [GET] + +# customer vehicle endpoints +resqapi_cust_vehicle_add: + path: /resqapi/vehicles + controller: App\Controller\ResqAPI\CustomerVehicleController::addVehicle + methods: [POST] + +resqapi_cust_vehicle_update: + path: /resqapi/vehicles/{id} + controller: App\Controller\ResqAPI\CustomerVehicleController::updateVehicle + methods: [POST] + +resqapi_cust_vehicle_list: + path: /resqapi/vehicles + controller: App\Controller\ResqAPI\CustomerVehicleController::listVehicles + methods: [GET] + +# promo endpoints +resqapi_promo_list: + path: /resqapi/promos + controller: App\Controller\ResqAPI\PromoController::listPromos + methods: [GET] + +# battery endpoints +resqapi_battery_list: + path: /resqapi/vehicles/{vid}/compatible_batteries + controller: App\Controller\ResqAPI\BatteryController::getCompatibleBatteries + methods: [GET] + +# service endpoints +resqapi_service_list: + path: /resqapi/services + controller: App\Controller\ResqAPI\ServiceController:listServices + methods: [GET] + +# partner endpoints +resqapi_partner_info: + path: /resqapi/partners/{pid} + controller: App\Controller\ResqAPI\PartnerController:getPartnerInformation + methods: [GET] + +resqapi_partner: + path: /resqapi/partners + controller: App\Controller\ResqAPI\PartnerController:getClosestPartners + methods: [POST] + +resqapi_partner_review: + path: /resqapi/partners/{pid}/review + controller: App\Controller\ResqAPI\PartnerController:reviewPartner + methods: [POST] + +# rider endpoints +resq_rider_status: + path: /resqapi/rider + controller: App\Controller\ResqAPI\RiderController::getRiderStatus + methods: [GET] + +resqapi_rider_rating_add: + path: /resqapi/rider_rating + controller: App\Controller\ResqAPI\RiderController::addRiderRating + methods: [POST] + +# job order endpoints +resqapi_jo_request: + path: /resqapi/job_order + controller: App\Controller\ResqAPI\JobOrderController::requestJobOrder + methods: [POST] + +resqapi_estimate: + path: /resqapi/estimate + controller: App\Controller\ResqAPI\JobOrderController::getEstimate + methods: [POST] + +resqapi_ongoing: + path: /resqapi/job_order/ongoing + controller: App\Controller\ResqAPI\JobOrderController::getOngoing + methods: [GET] + +resqapi_jo_cancel: + path: /resqapi/job_order/cancel + controller: App\Controller\ResqAPI\JobOrderController:cancelJobOrder + methods: [POST] + +resqapi_jo_history: + path: /resqapi/job_order/history + controller: App\Controller\ResqAPI\JobOrderController:getJOHistory + methods: [GET] + +resqapi_jo_invoice: + path: /resqapi/job_order/invoice + controller: App\Controller\ResqAPI\JobOrderController:getJOInvoice + methods: [GET] + +resqapi_location_support: + path: /resqapi/location_support + controller: App\Controller\ResqAPI\JobOrderController:locationSupport + methods: [POST] + +resqapi_nearest_hub_slots: + path: /resqapi/hub_slots + controller: App\Controller\ResqAPI\JobOrderController::getNearestHubAndSlots + methods: [POST] + +resqapi_new_jo_request: + path: /resqapi/new_job_order + controller: App\Controller\ResqAPI\JobOrderController::newRequestJobOrder + methods: [POST] + +resqapi_schedule_option_status: + path: /resqapi/schedule_option_status + controller: App\Controller\ResqAPI\JobOrderController::scheduleOptionStatus + methods: [GET] + +# warranty endpoints +resqapi_activate_warranty: + path: /resqapi/activate_warranty + controller: App\Controller\ResqAPI\WarrantyController:activateWarranty + methods: [POST] + +# paperless warranty / qr code +resqapi_warr_serial_check: + path: /resqapi/warranty/{serial} + controller: App\Controller\ResqAPI\WarrantyController::warrantyCheck + methods: [GET] + +resqapi_warr_serial_register: + path: /resqapi/warranty/{serial} + controller: App\Controller\ResqAPI\WarrantyController::warrantyRegister + methods: [POST] + diff --git a/config/services.yaml b/config/services.yaml index c4da052b..87fa1e05 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -299,3 +299,15 @@ services: App\Service\HubFilteringGeoChecker: arguments: $geofence_flag: "%env(HUB_GEOFENCE_ENABLE)%" + + # mobile api handler + App\Service\MobileAPIHandler: + arguments: + $em: "@doctrine.orm.entity_manager" + + # job order service + App\Service\JobOrderManager: + arguments: + $em: "@doctrine.orm.entity_manager" + $rah: "@App\\Service\\RiderAssignmentHandlerInterface" + diff --git a/src/Controller/CustomerController.php b/src/Controller/CustomerController.php index e15e050d..a8f1da4a 100644 --- a/src/Controller/CustomerController.php +++ b/src/Controller/CustomerController.php @@ -182,9 +182,7 @@ class CustomerController extends Controller public function getCustomerVehicles(Request $req, CustomerHandlerInterface $cust_handler) { - if (!(($this->isGranted('jo_onestep.form')) || - ($this->isGranted('jo_walkin.form')) || - ($this->isGranted('jo_in.list')))) + if (!($this->isGranted('jo_in.list'))) { $exception = $this->createAccessDeniedException('No access.'); throw $exception; diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php index fbab3cb2..acc302fe 100644 --- a/src/Controller/JobOrderController.php +++ b/src/Controller/JobOrderController.php @@ -4,7 +4,6 @@ namespace App\Controller; use App\Ramcar\JOStatus; use App\Ramcar\InvoiceCriteria; -use App\Ramcar\CMBServiceType; use App\Ramcar\ServiceType; use App\Ramcar\JOCancelReasons; @@ -291,13 +290,11 @@ class JobOrderController extends Controller $rows[$key]['meta']['reassign_rider_url'] = $this->generateUrl('jo_open_rider_form', ['id' => $jo_id]); // $rows[$key]['meta']['edit_url'] = $this->generateUrl('jo_open_edit_form', ['id' => $jo_id]); $rows[$key]['meta']['edit_url'] = $this->generateUrl($jo_handler->getEditRoute($jo_id, $tier_params['edit_route']), ['id' => $jo_id]); - $rows[$key]['meta']['onestep_edit_url'] = $this->generateUrl('jo_onestep_edit_form', ['id' => $jo_id]); } else { // $rows[$key]['meta']['update_url'] = $this->generateUrl($tier_params['edit_route'], ['id' => $jo_id]); $rows[$key]['meta']['update_url'] = $this->generateUrl($jo_handler->getEditRoute($jo_id, $tier_params['edit_route']), ['id' => $jo_id]); - $rows[$key]['meta']['onestep_edit_url'] = $this->generateUrl('jo_onestep_edit_form', ['id' => $jo_id]); $rows[$key]['meta']['pdf_url'] = $this->generateUrl('jo_pdf_form', ['id' => $jo_id]); } @@ -853,95 +850,6 @@ class JobOrderController extends Controller ]); } - /** - * @Menu(selected="jo_onestep_form") - */ - public function oneStepForm(EntityManagerInterface $em, JobOrderHandlerInterface $jo_handler, GISManagerInterface $gis) - { - $this->denyAccessUnlessGranted('jo_onestep.form', null, 'No access.'); - - $params = $jo_handler->initializeOneStepForm(); - $params['submit_url'] = $this->generateUrl('jo_onestep_submit'); - $params['return_url'] = $this->generateUrl('jo_onestep_form'); - $params['map_js_file'] = $gis->getJSJOFile(); - $params['vmfgs'] = $em->getRepository(VehicleManufacturer::class)->findAll(); - $params['vmakes'] = $em->getRepository(Vehicle::class)->findAll(); - - $template = $params['template']; - - // response - return $this->render($template, $params); - } - - public function oneStepSubmit(Request $req, JobOrderHandlerInterface $jo_handler) - { - $this->denyAccessUnlessGranted('jo_onestep.form', null, 'No access.'); - - // initialize error list - $error_array = []; - $id = -1; - $error_array = $jo_handler->processOneStepJobOrder($req, $id); - - // check if any errors were found - if (!empty($error_array)) { - // return validation failure response - return $this->json([ - 'success' => false, - 'errors' => $error_array - ], 422); - } - - - // return successful response - return $this->json([ - 'success' => 'Changes have been saved!' - ]); - } - - /** - * @Menu(selected="jo_onestep_edit_form") - */ - public function oneStepEditForm($id, EntityManagerInterface $em, JobOrderHandlerInterface $jo_handler, - GISManagerInterface $gis, MapTools $map_tools) - { - $this->denyAccessUnlessGranted('jo_onestep.edit', null, 'No access.'); - - $params = $jo_handler->initializeOneStepEditForm($id, $map_tools); - $params['submit_url'] = $this->generateUrl('jo_onestep_edit_submit', ['id' => $id]); - $params['return_url'] = $this->generateUrl('jo_open'); - $params['map_js_file'] = $gis->getJSJOFile(); - $params['vmfgs'] = $em->getRepository(VehicleManufacturer::class)->findAll(); - $params['vmakes'] = $em->getRepository(Vehicle::class)->findAll(); - - $template = $params['template']; - - // response - return $this->render($template, $params); - } - - public function oneStepEditSubmit(Request $req, JobOrderHandlerInterface $jo_handler, $id) - { - $this->denyAccessUnlessGranted('jo_onestep.edit', null, 'No access.'); - - $error_array = []; - $error_array = $jo_handler->processOneStepJobOrder($req, $id); - - // check if any errors were found - if (!empty($error_array)) { - // return validation failure response - return $this->json([ - 'success' => false, - 'errors' => $error_array - ], 422); - } - - - // return successful response - return $this->json([ - 'success' => 'Changes have been saved!' - ]); - } - /** * @ParamConverter("jo", class="App\Entity\JobOrder") */ @@ -978,99 +886,12 @@ class JobOrderController extends Controller $params['jo'] = $jo; $params['rider'] = $rider; $params['rider_pos'] = $rider_tracker->getRiderLocation($rider->getID()); - $params['service_type'] = CMBServiceType::getName($jo->getServiceType()); + $params['service_type'] = ServiceType::getName($jo->getServiceType()); $params['map_js_file'] = $gis_manager->getJSInitFile(); return $this->render('job-order/tracker.html.twig', $params); } - /** - * @Menu(selected="jo_walkin_form") - */ - public function walkInForm(EntityManagerInterface $em, JobOrderHandlerInterface $jo_handler) - { - $this->denyAccessUnlessGranted('jo_walkin.form', null, 'No access.'); - - $params = $jo_handler->initializeWalkinForm(); - $params['submit_url'] = $this->generateUrl('jo_walkin_submit'); - $params['return_url'] = $this->generateUrl('jo_walkin_form'); - $params['vmfgs'] = $em->getRepository(VehicleManufacturer::class)->findAll(); - $params['vmakes'] = $em->getRepository(Vehicle::class)->findAll(); - $params['hubs'] = $em->getRepository(Hub::class)->findAll(); - - $template = $params['template']; - - // response - return $this->render($template, $params); - } - - public function walkInSubmit(Request $req, JobOrderHandlerInterface $jo_handler) - { - $this->denyAccessUnlessGranted('jo_walkin.form', null, 'No access.'); - - // initialize error list - $error_array = []; - $id = -1; - $error_array = $jo_handler->processWalkinJobOrder($req, $id); - - // check if any errors were found - if (!empty($error_array)) { - // return validation failure response - return $this->json([ - 'success' => false, - 'errors' => $error_array - ], 422); - } - - // return successful response - return $this->json([ - 'success' => 'Changes have been saved!' - ]); - } - - /** - * @Menu(selected="jo_walkin_edit_form") - */ - public function walkInEditForm($id, EntityManagerInterface $em, JobOrderHandlerInterface $jo_handler) - { - $this->denyAccessUnlessGranted('jo_walkin.edit', null, 'No access.'); - - $params = $jo_handler->initializeWalkinEditForm($id); - $params['submit_url'] = $this->generateUrl('jo_walkin_edit_submit', ['id' => $id]); - $params['return_url'] = $this->generateUrl('jo_open'); - $params['vmfgs'] = $em->getRepository(VehicleManufacturer::class)->findAll(); - $params['vmakes'] = $em->getRepository(Vehicle::class)->findAll(); - $params['hubs'] = $em->getRepository(Hub::class)->findAll(); - - $template = $params['template']; - - // response - return $this->render($template, $params); - } - - public function walkInEditSubmit(Request $req, JobOrderHandlerInterface $jo_handler, $id) - { - $this->denyAccessUnlessGranted('jo_walkin.edit', null, 'No access.'); - - $error_array = []; - $error_array = $jo_handler->processWalkinJobOrder($req, $id); - - // check if any errors were found - if (!empty($error_array)) { - // return validation failure response - return $this->json([ - 'success' => false, - 'errors' => $error_array - ], 422); - } - - - // return successful response - return $this->json([ - 'success' => 'Changes have been saved!' - ]); - } - /** * @Menu(selected="jo_hub_view") */ diff --git a/src/Controller/ResqAPI/BatteryController.php b/src/Controller/ResqAPI/BatteryController.php new file mode 100644 index 00000000..d1517838 --- /dev/null +++ b/src/Controller/ResqAPI/BatteryController.php @@ -0,0 +1,94 @@ +acl_gen = $acl_gen; + } + + public function getCompatibleBatteries(Request $req, $vid, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_battery.list', null, 'No access.'); + + // check required parameters + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // get vehicle + $vehicle = $em->getRepository(Vehicle::class)->find($vid); + if ($vehicle == null) + return new APIResponse(false, 'Invalid vehicle'); + + // batteries + $batt_list = []; + $batts = $vehicle->getBatteries(); + foreach ($batts as $batt) + { + // generate the url for image + $battery_image_url = $this->generateUrl('static_battery_image'); + + // TODO: Add warranty_tnv to battery information + $batt_list[] = [ + 'id' => $batt->getID(), + 'mfg_id' => $batt->getManufacturer()->getID(), + 'mfg_name' => $batt->getManufacturer()->getName(), + 'model_id' => $batt->getModel()->getID(), + 'model_name' => $batt->getModel()->getName(), + 'size_id' => $batt->getSize()->getID(), + 'size_name' => $batt->getSize()->getName(), + 'price' => $batt->getSellingPrice(), + 'wty_private' => $batt->getWarrantyPrivate(), + 'wty_commercial' => $batt->getWarrantyCommercial(), + 'image_url' => $mah->getBatteryImageURL($req, $batt, $battery_image_url), + ]; + } + + // data + $data = [ + 'vehicle' => [ + 'id' => $vehicle->getID(), + 'mfg_id' => $vehicle->getManufacturer()->getID(), + 'mfg_name' => $vehicle->getManufacturer()->getName(), + 'make' => $vehicle->getMake(), + 'model_year_from' => $vehicle->getModelYearFrom(), + 'model_year_to' => $vehicle->getModelYearTo(), + ], + 'batteries' => $batt_list, + ]; + + return new APIResponse(true, 'Compatible batteries found', $data); + } +} diff --git a/src/Controller/ResqAPI/CustomerController.php b/src/Controller/ResqAPI/CustomerController.php new file mode 100644 index 00000000..ecece983 --- /dev/null +++ b/src/Controller/ResqAPI/CustomerController.php @@ -0,0 +1,615 @@ +acl_gen = $acl_gen; + } + + public function register(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.register', null, 'No access.'); + + // confirm parameters + $required_params = [ + 'phone_model', + 'os_type', + 'os_version', + 'phone_id' + ]; + + // check required parameters + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // check if capi user already has a mobile user + $mobile_user = $mah->findMobileUser($user_id); + if ($mobile_user != null) + return new APIResponse(false, 'User already registered'); + + // retry until we get a unique id + while (true) + { + try + { + // create mobile user + $mobile_user = new MobileUser(); + $mobile_user->setPhoneModel($req->request->get('phone_model')) + ->setOSType($req->request->get('os_type')) + ->setOSVersion($req->request->get('os_version')) + ->setPhoneID($req->request->get('phone_id')) + ->setCapiUserId($user_id); + + // reopen in case we get an exception + if (!$em->isOpen()) + { + $em = $em->create( + $em->getConnection(), + $em->getConfiguration() + ); + } + + // save + $em->persist($mobile_user); + $em->flush(); + } + catch (DBALException $e) + { + error_log($e->getMessage()); + // delay one second and try again + sleep(1); + continue; + } + + break; + } + + // return data + // TODO: do we need to return the same names as before? + // right now, still using the old names so we use session_id name + $data = [ + 'session_id' => $mobile_user->getID(), + ]; + + // response + return new APIResponse(true, 'Mobile user created.', $data); + } + + public function confirmNumber(RisingTideGateway $rt, Request $req, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.confirm.number', null, 'No access.'); + + // check parameters + $required_params = [ + 'phone_number', + ]; + + // check required parameters + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // phone number + $phone_number = $req->request->get('phone_number'); + + // get otp_mode from .env + $dotenv = new Dotenv(); + $dotenv->loadEnv(__DIR__.'/../../../.env'); + + $otp_mode = $_ENV['OTP_MODE']; + + $data = []; + // check for hardcoded phone number for app store testing + if ($phone_number == '639221111111') + { + $code = '123456'; + $mobile_user->setConfirmCode($code) + ->setPhoneNumber($phone_number); + $em->flush(); + + return new APIResponse(true, 'Number confirmed.'); + } + + // check if otp_mode is test + if ($otp_mode == 'test') + { + $code = '123456'; + $mobile_user->setConfirmCode($code) + ->setPhoneNumber($phone_number); + $em->flush(); + + return new APIResponse(true, 'Number confirmed.'); + } + + // TODO: spam protection + + // TODO: validate phone number + + // generate code and save + $code = $this->generateConfirmCode(); + $mobile_user->setConfirmCode($code) + ->setPhoneNumber($phone_number); + $em->flush(); + + if ($otp_mode != 'test') + { + // send sms to number + $this->sendConfirmationCode($rt, $phone_number, $code); + } + + // response + return new APIResponse(true, 'Number confirmed.'); + } + + public function validateCode(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.validate.code', null, 'No access.'); + + // check parameters + $required_params = [ + 'code', + ]; + + // check required parameters + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // code is wrong + $code = $req->request->get('code'); + if ($mobile_user->getConfirmCode() != $code) + return new APIResponse(false, 'Wrong confirm code'); + + // set confirm date + $date = new DateTime(); + $mobile_user->setDateConfirmed($date) + ->setConfirmed(); + + // TODO: check if we have the number registered before and merge + $dupe_user = $this->findNumberMobileUser($mobile_user->getPhoneNumber(), $em); + if ($dupe_user != null) + { + $dupe_cust = $dupe_user->getCustomer(); + $mobile_user->setCustomer($dupe_cust); + } + + // TODO: check if mobile matches mobile of customer + // TODO: need to "clean" the mobile number. Mobile user stores the number with the area code prepended + // Customer stores the number without the area code + $customer = $this->findCustomerByNumber($mobile_user->getPhoneNumber(), $em); + if ($customer != null) + { + // TODO: if there is a dupe_sess, do we need to check if + // dupe_cust is the same as the customer we found? + $mobile_user->setCustomer($customer); + } + + $em->flush(); + + // response + return new APIResponse(true, 'Code validated'); + } + + public function getInfo(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.get.info', null, 'No access.'); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // if no customer found + $cust = $mobile_user->getCustomer(); + if ($cust == null) + { + $data = [ + 'first_name' => '', + 'last_name' => '', + 'priv_third_party' => (bool) false, + 'priv_promo' => (bool) false, + ]; + + return new APIResponse(true, 'No customer info found', $data); + } + + // send back customer details + $data = [ + 'first_name' => $cust->getFirstName(), + 'last_name' => $cust->getLastName(), + 'priv_third_party' => (bool) $cust->getPrivacyThirdParty(), + 'priv_promo' => (bool) $cust->getPrivacyPromo(), + ]; + + return new APIResponse(true, 'Customer info found', $data); + } + + public function updateInfo(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.update.info', null, 'No access.'); + + // check required parameters + $required_params = [ + 'first_name', + 'last_name', + ]; + + // check required parameters + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $cust = $this->updateCustomerInfo($req, $em, $mobile_user); + + // get privacy policy for mobile + $dotenv = new Dotenv(); + $dotenv->loadEnv(__DIR__.'/../../../.env'); + + $policy_mobile_id = $_ENV['POLICY_MOBILE']; + + $mobile_policy = $em->getRepository(PrivacyPolicy::class)->find($policy_mobile_id); + + // set policy id + if ($mobile_policy != null) + { + $cust->setPrivacyPolicyMobile($mobile_policy); + } + + $em->flush(); + + $data = [ + 'first_name' => $cust->getFirstName(), + 'last_name' => $cust->getLastName(), + ]; + + return new APIResponse(true, 'Customer info updated'); + } + + public function getStatus(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.get.status', null, 'No access.'); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // set data + $data = []; + if ($mobile_user->isConfirmed()) + $data['status'] = 'confirmed'; + else + $data['status'] = 'unconfirmed'; + + return new APIResponse(true, 'Customer status', $data); + } + + public function resendCode(Request $req, RisingTideGateway $rt, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.resend.code', null, 'No access.'); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // already confirmed + if ($mobile_user->isConfirmed()) + return new APIResponse(true, 'User is already confirmed'); + + // have sent code before + if ($mobile_session->getDateCodeSent() != null) + return new APIResponse(true, 'Can only send confirm code every 5 mins'); + + // TODO: send via sms + $phone_number = $mobile_user->getPhoneNumber(); + $code = $mobile_user->getConfirmCode(); + $this->sendConfirmationCode($rt, $phone_number, $code); + + return new APIResponse(true, 'Code re-sent'); + } + + public function versionCheck(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.version.check', null, 'No access.'); + + $required_params = [ + 'version', + ]; + + // check required parameters + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $need_update = false; + $msg = 'Version is up to date.'; + + $api_version = $this->getParameter('api_version'); + + $app_version = $req->request->get('version'); + + $api_v = explode('.', $api_version); + $app_v = explode('.', $app_version); + + if ($api_v[0] < $app_v[0]) + return new APIResponse(false, 'Invalid application version: ' . $app_version); + + if ($api_v[0] > $app_v[0]) + { + $need_update = true; + $msg = 'Your version is outdated and needs an update to use the latest features RES-Q has to offer.'; + } + + + $data = [ + 'need_update' => $need_update, + 'message' => $msg, + ]; + + return new APIResponse(true, 'Version checked', $data); + } + + public function updateDeviceID(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.update.deviceid', null, 'No access.'); + + $required_params = [ + 'device_id', + ]; + + // check required parameters + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $device_id = $req->request->get('device_id'); + $mobile_user->setDevicePushID($device_id); + + $em->flush(); + + $data = [ + 'device_id' => $mobile_user->getDevicePushID(), + ]; + + // response + return new APIResponse(true, 'Device ID updated', $data); + } + + public function privacySettings(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_user.privacy.settings', null, 'No access.'); + + $required_params = [ + 'priv_third_party', + // 'priv_promo', + ]; + + // check required parameters + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // get customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + // set privacy settings + $priv_promo = $req->request->get('priv_promo', false); + $priv_third_party = $req->request->get('priv_third_party'); + $cust->setPrivacyThirdParty($priv_third_party) + ->setPrivacyPromo($priv_promo); + + // get the policy ids from .env + $dotenv = new Dotenv(); + $dotenv->loadEnv(__DIR__.'/../../../.env'); + + $policy_promo_id = $_ENV['POLICY_PROMO']; + $policy_third_party_id = $_ENV['POLICY_THIRD_PARTY']; + + // check if privacy settings are true + // if true, set the private policy for the customer + if ($priv_promo) + { + // find the promo policy + $policy = $em->getRepository(PrivacyPolicy::class)->find($policy_promo_id); + + // set policy id + if ($policy != null) + { + $cust->setPrivacyPolicyPromo($policy); + } + } + + if ($priv_third_party) + { + // find the third party policy + $policy = $em->getRepository(PrivacyPolicy::class)->find($policy_third_party_id); + + // set policy id + if ($policy != null) + { + $cust->setPrivacyPolicyThirdParty($policy); + } + } + + $em->flush(); + + return new APIResponse(true, 'Privacy policy settings set'); + } + + // TODO: find session customer by phone number + protected function findNumberMobileUser($number, $em) + { + $query = $em->getRepository(MobileUser::class)->createQueryBuilder('s') + ->where('s.phone_number = :number') + ->andWhere('s.customer is not null') + ->andWhere('s.confirm_flag = 1') + ->setParameter('number', $number) + ->setMaxResults(1) + ->getQuery(); + + // we just need one + $res = $query->getOneOrNullResult(); + + return $res; + } + + protected function findCustomerByNumber($number, $em) + { + $customers = $em->getRepository(Customer::class)->findBy(['phone_mobile' => $number]); + + // find the customer with the most number of cars + $car_count = 0; + $cust = null; + + foreach($customers as $customer) + { + error_log('no customer?'); + $vehicles = $customer->getVehicles(); + if (count($vehicles) > $car_count) + { + $car_count = count($vehicles); + + // "save" customer object + $cust = $customer; + } + } + + return $cust; + } + + protected function updateCustomerInfo($req, $em, $mobile_user) + { + // create new customer if it's not there + $cust = $mobile_user->getCustomer(); + if ($cust == null) + { + $cust = new Customer(); + + // set customer source + $cust->setCreateSource(CustomerSource::MOBILE); + $em->persist($cust); + + $mobile_user->setCustomer($cust); + } + + $cust->setFirstName($req->request->get('first_name')) + ->setLastName($req->request->get('last_name')) + ->setEmail($req->request->get('email', '')) + ->setConfirmed($mobile_user->isConfirmed()); + + // update mobile phone of customer + $cust->setPhoneMobile(substr($mobile_user->getPhoneNumber(), 2)); + + return $cust; + } + + protected function sendConfirmationCode(RisingTideGateway $rt, $phone_number, $code) + { + // send sms to number + $message = "Your Resq confirmation code is $code."; + $rt->sendSMS($phone_number, 'MOTOLITE', $message); + } +} diff --git a/src/Controller/ResqAPI/CustomerVehicleController.php b/src/Controller/ResqAPI/CustomerVehicleController.php new file mode 100644 index 00000000..fdbba8e0 --- /dev/null +++ b/src/Controller/ResqAPI/CustomerVehicleController.php @@ -0,0 +1,251 @@ +acl_gen = $acl_gen; + } + + public function addVehicle(Request $req, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_customer_vehicle.add', null, 'No access.'); + + // check requirements + $msg = $this->checkVehicleRequirements($req); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // customer vehicle + $cv = new CustomerVehicle(); + + $res = $this->setCustomerVehicleObject($mobile_user, $req, $cv, $em); + if (isset($res['cv_id'])) + return new APIResponse(true, 'Customer vehicle added', $res); + else + return new APIResponse(false, $res); + } + + public function updateVehicle(Request $req, $id, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_customer_vehicle.update', null, 'No access.'); + + // check requirements + $msg = $this->checkVehicleRequirements($req); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + // get customer vehicle + $cv = $em->getRepository(CustomerVehicle::class)->find($id); + + // check if it exists + if ($cv == null) + return new APIResponse(false, 'Vehicle does not exist'); + + // check if it's owned by customer + if ($cv->getCustomer()->getID() != $mobile_user->getCustomer()->getID()) + return new APIResponse(false, 'Invalid vehicle'); + + $res = $this->setCustomerVehicleObject($mobile_user, $req, $cv, $em); + if (isset($res['cv_id'])) + return new APIResponse(true, 'Customer vehicle updated', $res); + else + return new APIResponse(false, $res); + + } + + public function listVehicles(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_customer_vehicle.list', null, 'No access.'); + + // check required parameters + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + // vehicles + $cv_list = []; + $cvs = $cust->getVehicles(); + foreach ($cvs as $cv) + { + $battery_id = null; + if ($cv->getCurrentBattery() != null) + $battery_id = $cv->getCurrentBattery()->getID(); + + $wty_ex = null; + if ($cv->getWarrantyExpiration() != null) + $wty_ex = $cv->getWarrantyExpiration()->format('Y-m-d'); + + $warranty = $mah->findWarranty($cv->getPlateNumber()); + + $cv_name = ''; + if ($cv->getName() != null) + $cv_name = $cv->getName(); + + $cv_list[] = [ + 'cv_id' => $cv->getID(), + 'mfg_id' => $cv->getVehicle()->getManufacturer()->getID(), + 'make_id' => $cv->getVehicle()->getID(), + 'name' => $cv_name, + 'plate_num' => $cv->getPlateNumber(), + 'model_year' => $cv->getModelYear(), + 'color' => $cv->getColor(), + 'condition' => $cv->getStatusCondition(), + 'fuel_type' => $cv->getFuelType(), + 'wty_code' => $cv->getWarrantyCode(), + 'wty_expire' => $wty_ex, + 'curr_batt_id' => $battery_id, + 'is_motolite' => $cv->hasMotoliteBattery() ? 1 : 0, + 'is_active' => $cv->isActive() ? 1 : 0, + 'warranty' => $warranty, + ]; + } + + // data + $data = [ + 'vehicles' => $cv_list + ]; + + return new APIResponse(true, 'Customer vehicles listed', $data); + } + + protected function checkVehicleRequirements(Request $req) + { + // check required parameters + $required_params = [ + 'make_id', + 'name', + 'plate_num', + 'model_year', + 'color', + 'condition', + 'fuel_type', + ]; + $msg = $this->checkRequiredParameters($req, $required_params); + + // TODO: check valid plate number + // TODO: check valid fuel type (gas / diesel) + // TODO: check current battery id + // TODO: check condition (brand new / second-hand) + // TODO: check is_motolite and is_active (1 or 0) + // TODO: check warranty expiration date (YYYYMMDD) + // TODO: check model year coverage if it fits in between + + return $msg; + } + + protected function setCustomerVehicleObject(MobileUser $mobile_user, Request $req, + CustomerVehicle $cv, EntityManagerInterface $em) + { + $msg = ''; + // check customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + { + $msg = 'No customer information found'; + return $msg; + } + + // get vehicle + $vehicle = $em->getRepository(Vehicle::class)->find($req->request->get('make_id')); + if ($vehicle == null) + { + $msg = 'Invalid vehicle make id'; + return $msg; + } + + $cv->setCustomer($cust) + ->setVehicle($vehicle) + ->setName($req->request->get('name')) + ->setPlateNumber($req->request->get('plate_num')) + ->setModelYear($req->request->get('model_year')) + ->setColor($req->request->get('color')) + ->setFuelType($req->request->get('fuel_type')) + ->setStatusCondition($req->request->get('condition')); + + // set warranty code and expiration + // TODO: check warranty requirements + if (!empty($req->request->get('wty_code'))) + $cv->setWarrantyCode($req->request->get('wty_code')); + if (!empty($req->request->get('wty_expire'))) + $cv->setWarrantyExpiration(new DateTime($req->request->get('wty_expire'))); + + // TODO: get current battery + + // is motolite + if ($req->request->get('is_motolite') == 0) + $cv->setHasMotoliteBattery(false); + else + $cv->setHasMotoliteBattery(true); + + // is active + if ($req->request->get('is_active') == 0) + $cv->setActive(false); + else + $cv->setActive(true); + + // save + $em->persist($cv); + $em->flush(); + + // data + $data = [ + 'cv_id' => $cv->getID() + ]; + + return $data; + } +} diff --git a/src/Controller/ResqAPI/JobOrderController.php b/src/Controller/ResqAPI/JobOrderController.php new file mode 100644 index 00000000..6ac4dd23 --- /dev/null +++ b/src/Controller/ResqAPI/JobOrderController.php @@ -0,0 +1,1289 @@ +acl_gen = $acl_gen; + } + + public function requestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo, + InventoryManager $im, MQTTClient $mclient, RiderAssignmentHandlerInterface $rah, + EntityManagerInterface $em, HubSelector $hub_select, + HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger, HubFilteringGeoChecker $hub_geofence, + MobileAPIHandler $mah, JobOrderManager $jo_manager) + { + $this->denyAccessUnlessGranted('mobile_jo.request', null, 'No access.'); + + // check required parameters + $required_params = [ + 'service_type', + 'cv_id', + // 'batt_id', + 'trade_in', + 'long', + 'lat', + 'warranty', + 'mode_of_payment', + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // validate request + $msg = $this->validateJORequest($req, $geo); + if ($msg) + return new APIResponse(false, $msg); + + $jo = new JobOrder(); + $this->setJOObject($jo, $req, $em); + + // customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + $jo->setCustomer($cust); + + // make invoice criteria + $icrit = new InvoiceCriteria(); + $msg = $this->setInvoiceCriteria($em, $icrit, $req, $cust, $jo); + if ($msg != null) + return new APIResponse(false, $msg); + + // send to invoice generator + $invoice = $ic->generateInvoice($icrit); + $jo->setInvoice($invoice); + + // set hub criteria details + $hub_criteria = new HubCriteria(); + $this->setHubCriteria($em, $hub_geofence, $hub_criteria, $jo, $req); + + // find nearest hubs + $nearest_hubs = $hub_select->find($hub_criteria); + + if (!empty($nearest_hubs)) + $this->assignRider($nearest_hubs, $jo, $hub_filter_logger, $hub_dist); + + $em->persist($jo); + $em->persist($invoice); + + // create JO event logs + $jo_manager->processJobOrderEvents($jo, JOEventType::CREATE); + + // send mqtt events + $payload = [ + 'event' => 'outlet_assign' + ]; + $this->processMQTTEvents($jo, $payload, $mclient, $rah); + + $em->flush(); + + $invoice_data = $this->makeInvoiceData($invoice, $mah, $req); + + // make job order data + $data = [ + 'jo_id' => $jo->getID(), + 'invoice' => $invoice_data + ]; + + // check service type + if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) + { + $customer_tags = $cust->getCustomerTagObjects(); + $jo_manager->removeCustomerTag($jo, $cust, $customer_tags, $mobile_user->getID()); + } + + return new APIResponse(true, 'Job order created', $data); + } + + public function newRequestJobOrder(Request $req, InvoiceGeneratorInterface $ic, GeofenceTracker $geo, + InventoryManager $im, MQTTClient $mclient, + RiderAssignmentHandlerInterface $rah, HubSelector $hub_select, + HubDistributor $hub_dist, HubFilterLogger $hub_filter_logger, + HubFilteringGeoChecker $hub_geofence, EntityManagerInterface $em, + MobileAPIHandler $mah, JobOrderManager $jo_manager) + { + $this->denyAccessUnlessGranted('mobile_jo.request', null, 'No access.'); + + // check required parameters + $required_params = [ + 'service_type', + 'cv_id', + 'trade_in', + 'long', + 'lat', + 'warranty', + 'mode_of_payment', + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // validate request + $msg = $this->validateJORequest($req, $geo); + if ($msg) + return new APIResponse(false, $msg); + + $jo = new JobOrder(); + $this->setJOObject($jo, $req, $em); + + // customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + $jo->setCustomer($cust); + + // make invoice criteria + $icrit = new InvoiceCriteria(); + $msg = $this->setInvoiceCriteria($em, $icrit, $req, $cust, $jo); + if ($msg != null) + return new APIResponse(false, $msg); + + // send to invoice generator + $invoice = $ic->generateInvoice($icrit); + $jo->setInvoice($invoice); + + // assign hub and rider + // check if hub is null + if ($jo->getHub() == null) + { + // set hub criteria details + $hub_criteria = new HubCriteria(); + $this->setHubCriteria($em, $hub_geofence, $hub_criteria, $jo, $req); + + // find nearest hubs + $nearest_hubs = $hub_select->find($hub_criteria); + + if (!empty($nearest_hubs)) + $this->assignRider($nearest_hubs, $jo, $hub_filter_logger, $hub_dist); + } + else + { + $schedule_date = $req->request->get('date_schedule'); + $slot_id = $req->request->get('slot_id'); + $date_schedule = null; + + if ((strlen($schedule_date) > 0) && (strlen($slot_id) > 0)) + { + $time_schedule = $this->getTimeFromSlot($slot_id); + if (!empty($time_schedule)) + { + $s_date = $schedule_date . ' ' . $time_schedule; + $date_schedule = DateTime::createFromFormat('Y-m-d H:i', $s_date); + error_log($date_schedule->format('Y-m-d H:i')); + } + } + + $jo->setStatus(JOStatus::RIDER_ASSIGN); + $jo->setStatusAutoAssign(AutoAssignStatus::HUB_ASSIGNED); + + if ($date_schedule != null) + $jo->setDateSchedule($date_schedule); + + // update redis hub_jo_count for hub + $hub_dist->incrementJoCountForHub($jo->getHub()); + } + + $em->persist($jo); + $em->persist($invoice); + + // create JO event logs + $jo_manager->processJobOrderEvents($jo, JOEventType::CREATE); + + // send mqtt events + $payload = [ + 'event' => 'outlet_assign' + ]; + $this->processMQTTEvents($jo, $payload, $mclient, $rah); + + $em->flush(); + + // make invoice json data + $invoice_data = $this->makeInvoiceData($invoice, $mah, $req); + + // make job order data + $data = [ + 'jo_id' => $jo->getID(), + 'invoice' => $invoice_data + ]; + + // check service type + if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) + { + $customer_tags = $cust->getCustomerTagObjects(); + $jo_manager->removeCustomerTag($jo, $cust, $customer_tags, $mobile_user->getID()); + } + + return new APIResponse(true, 'Job order created', $data); + } + + public function getEstimate(Request $req, InvoiceGeneratorInterface $ic, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_jo.get.estimate', null, 'No access.'); + + // check required parameters + $required_params = [ + 'service_type', + 'cv_id', + // 'batt_id', + 'trade_in', + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + // make invoice criteria + $icrit = new InvoiceCriteria(); + $msg = $this->setInvoiceCriteria($em, $icrit, $req, $cust, null); + if ($msg != null) + return new APIResponse(false, $msg); + + // send to invoice generator + $invoice = $ic->generateInvoice($icrit); + + // make invoice json data + $invoice_data = $this->makeInvoiceData($invoice, $mah, $req); + + return new APIResponse(true, 'Estimate found', $invoice_data); + } + + public function getOngoing(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_jo.get.ongoing', null, 'No access.'); + + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + $ongoing_jos = $mah->getOngoingJobOrders($cust); + + // initialize data + $data = []; + + // no ongoing + if (count($ongoing_jos) <= 0) + { + $data = [ + 'has_ongoing' => false, + ]; + } + else + { + $data = [ + 'has_ongoing' => true, + ]; + } + + return new APIResponse(true, 'Ongoing job orders found.', $data); + } + + public function cancelJobOrder(Request $req, MQTTClient $mclient, EntityManagerInterface $em, + MobileAPIHandler $mah, RiderAssignmentHandlerInterface $rah, + JobOrderManager $jo_manager) + { + $this->denyAccessUnlessGranted('mobile_jo.cancel', null, 'No access.'); + + $required_params = [ + 'jo_id', + 'reason' + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // get job order + $jo_id = $req->request->get('jo_id'); + $jo = $em->getRepository(JobOrder::class)->find($jo_id); + if ($jo == null) + return new APIResponse(false, 'No job order found'); + + // get customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + new APIResponse(false, 'No customer information found'); + + // check that the customer owns the job order + $jo_cust = $jo->getCustomer(); + if ($jo_cust->getID() != $cust->getID()) + return new APIResponse(false, 'Job order was not initiated by customer'); + + // TODO: check job order status, if it's cancellable + $cancel_reason = $req->request->get('reason'); + + $jo->cancel($cancel_reason); + + // create event logs + $jo_manager->processJobOrderEvents($jo, JOEventType::CANCEL); + + // send mqtt events + $payload = [ + 'event' => 'cancelled', + 'reason' => $cancel_reason, + 'jo_id' => $jo->getID(), + ]; + $this->processMQTTEvents($jo, $payload, $mclient, $rah); + + $em->flush(); + + $data = []; + return new APIResponse(true, 'Job order cancelled', $data); + } + + public function getJOHistory(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah, + RiderTracker $rt) + { + $this->denyAccessUnlessGranted('mobile_jo.get.history', null, 'No access.'); + + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + // get job orders + $all_jo_data = []; + $jos = $cust->getJobOrders(); + foreach ($jos as $jo) + { + $all_jo_data[] = $this->generateJobOrderData($req, $jo, $rt, $mah); + } + + // return data + $data = [ + 'job_orders' => $all_jo_data + ]; + + return new APIResponse(true, 'Job order history found', $data); + } + + public function getJOInvoice(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_jo.get.invoice', null, 'No access.'); + + $required_params = [ + 'jo_id', + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + // check that the customer owns the job order + $jo_cust = $jo->getCustomer(); + if ($jo_cust->getID() != $cust->getID()) + return new APIResponse(false, 'Job order was not initiated by customer'); + + $invoice = $jo->getInvoice(); + + $data = $this->makeInvoiceData($invoice, $mah, $req); + + return new APIResponse(true, 'Job order invoice found', $data); + } + + public function locationSupport(Request $req, GeofenceTracker $geo, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $required_params = [ + 'longitude', + 'latitude', + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $long = $req->request->get('longitude'); + $lat = $req->request->get('latitude'); + + // geofence + $is_covered = $geo->isCovered($long, $lat); + + $data = [ + 'longitude' => $long, + 'latitude' => $lat, + 'supported' => $is_covered, + ]; + + return new APIResponse(true, 'Location checked', $data); + } + + public function getNearestHubAndSlots(Request $req, EntityManagerInterface $em, + MobileAPIHandler $mah, HubSelector $hub_select) + { + $this->denyAccessUnlessGranted('mobile_jo.nearest_hub.get', null, 'No access.'); + + $required_params = [ + 'longitude', + 'latitude', + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $coordinates = new Point($req->request->get('longitude'), $req->request->get('latitude')); + + $hub_criteria = new HubCriteria(); + // get the nearest 10 hubs + // TODO: do we need to check for inventory? if so, we need battery id + $hub_criteria->setPoint($coordinates) + ->setLimitResults(10); + + // find nearest hubs + $nearest_hubs = $hub_select->find($hub_criteria); + + // get the nearest hub and slots + $nearest_hub_slots = $this->findAdvanceNearestHubAndSlots($nearest_hubs, $em); + + if (empty($nearest_hub_slots['hub'])) + return new APIResponse(false, 'Thank you for reaching out to us. Due to the General Community Quarantine, our Operations are from 8AM to 6PM only. Please expect a call from us tomorrow and we will assist you with your request. Thank you and stay safe!'); + + // make hub data + $data = [ + 'hub_id' => $nearest_hub_slots['hub']->getID(), + 'hub_slots' => $nearest_hub_slots['slots'], + ]; + + return new APIResponse(true, 'Hub and slot found', $data); + } + + public function scheduleOptionStatus(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_jo.schedule_option.status', null, 'No access.'); + + // check required parameters and api key + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $schedule_choice = true; + + // TODO: remove the time check after ECQ. This will then always return true + // get current time + $current_datetime = new DateTime(); + //$current_datetime = DateTime::createFromFormat('Y-m-d H:i', '2020-04-30 17:01'); + + // get the hour + $hour = $current_datetime->format('G'); + + if (($hour < 8) || ($hour > 16)) + $schedule_choice = false; + + $data = [ + 'display_schedule_choice' => $schedule_choice, + ]; + + return new APIResponse(true, 'Schedule option status check', $data); + } + + 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; + } + + protected function findAdvanceNearestHubAndSlots($nearest_hubs, EntityManagerInterface $em) + { + $nearest = null; + $slot_found = false; + // find the nearest hub + if (!empty($nearest_hubs)) + { + foreach ($nearest_hubs as $nhd) + { + if (empty($nearest)) + $nearest = $nhd; + else + { + if ($nhd['distance'] < $nearest['distance']) + $nearest = $nhd; + } + } + + // get slots of nearest hub + if ($nearest != null) + { + $hub_slots = $this->getHubRiderSlots($nearest['hub'], $em); + + $hub_data = [ + 'hub' => $nearest['hub'], + 'slots' => $hub_slots, + ]; + } + } + + return $hub_data; + } + + protected function getHubRiderSlots(Hub $hub, EntityManagerInterface $em) + { + // check hub's advance orders for the day + + /* + // get number of advance orders for the next day if request came in before midnight + // or for current day if request came in after midnight + // check request_time + $request_time = time(); + $midnight = strtotime('00:00'); + */ + $start_date = new DateTime(); + $end_date = new DateTime(); + + // to keep things simple, just start on next day regardless of midnight timer + $start_date->add(new DateInterval('P1D')); + $end_date->add(new DateInterval('P3D')); + + /* + if ($request_time < $midnight) + { + // add +1 to start date to get the next day + // add +3 to date to end date to get the advance orders for the next three days + $start_date->add(new DateInterval('P1D')); + $end_date->add(new DateInterval('P1D')); + } + $end_date->add(new DateInterval('P2D')); + */ + + // set time bounds for the start and end date + $start_date->setTime(0, 1); + $end_date->setTime(23, 59); + + // NOTE: get advance orders via query + // get JOs assigned to hub that are advance orders and scheduled for the next three days with + // for hub assignment status + $query = $em->createQuery('select jo from App\Entity\JobOrder jo where jo.hub = :hub and jo.flag_advance = true and + jo.date_schedule >= :date_start and jo.date_schedule <= :date_end and jo.status != :status_cancelled + and jo.status != :status_fulfilled'); + $jos_advance_orders = $query->setParameters([ + 'hub' => $hub, + 'date_start' => $start_date, + 'date_end' => $end_date, + 'status_cancelled' => JOStatus::CANCELLED, + 'status_fulfilled' => JOStatus::FULFILLED, + ]) + ->getResult(); + // check request_time + // define slots + $slots = [ + '08_09' => '8:00 AM', + '09_10' => '9:00 AM', + '10_11' => '10:00 AM', + '11_12' => '11:00 AM', + '12_13' => '12:00 PM', + '13_14' => '1:00 PM', + '14_15' => '2:00 PM', + '15_16' => '3:00 PM', + '16_17' => '4:00 PM', + ]; + + // get the dates for the next three days + $first_date = $start_date->format('Y-m-d'); + $second_date = $start_date->add(new DateInterval('P1D')); + $sec_date = $second_date->format('Y-m-d'); + $third_date = $end_date->format('Y-m-d'); + + // define days + $days = [ + $first_date => $first_date, + $sec_date => $sec_date, + $third_date => $third_date, + ]; + + // initialize hub rider slots + $hub_rider_slots = []; + foreach ($days as $day) + { + foreach ($slots as $slot_key => $slot) + { + $hub_rider_slots[$day][$slot_key] = $hub->getRiderSlots(); + } + } + + // check each JO's date_schedule, decrement rider_slots if date schedule falls in that slot + foreach ($jos_advance_orders as $jo) + { + // get date key + $date_sched = $jo->getDateSchedule(); + $date_string = $date_sched->format('Y-m-d'); + $hour = $date_sched->format('H'); + $slot_id = sprintf('%02d_%02d', $hour, $hour + 1); + + error_log("SLOT - $date_string - $slot_id"); + + // decrement rider slot + if (isset($hub_rider_slots[$date_string][$slot_id])) + $hub_rider_slots[$date_string][$slot_id]--; + + // check if it goes through next slot (10 min allowance) + $mins = $date_sched->format('i'); + if ($mins > 10) + { + $next_slot_id = sprintf('%02d_%02d', $hour + 1, $hour + 2); + error_log("NEXT SLOT - $date_string - $next_slot_id"); + // decrement rider slot + if (isset($hub_rider_slots[$date_string][$next_slot_id])) + $hub_rider_slots[$date_string][$next_slot_id]--; + + } + } + + error_log(print_r($hub_rider_slots, true)); + + $hub_slots = $this->generateHubSlots($hub_rider_slots, $slots); + + return $hub_slots; + } + + protected function generateHubSlots($rider_slots, $slots) + { + $data = []; + foreach ($rider_slots as $day_id => $rslot) + { + $data[$day_id] = []; + + foreach ($rslot as $slot_id => $avail_slots) + { + $slot_data = [ + 'id' => $slot_id, + 'label' => $slots[$slot_id], + 'available' => true, + ]; + + // mark unavailable ones + if ($avail_slots <= 0) + $slot_data['available'] = false; + + // add to day data + $data[$day_id][] = $slot_data; + } + } + + return $data; + } + + protected function getTimeFromSlot($slot_id) + { + $time_selected = ''; + + switch($slot_id) { + case '08_09': + $time_selected = AdvanceOrderSlot::_08_09; + break; + case '09_10': + $time_selected = AdvanceOrderSlot::_09_10; + break; + case '10_11': + $time_selected = AdvanceOrderSlot::_10_11; + break; + case '11_12': + $time_selected = AdvanceOrderSlot::_11_12; + break; + case '12_13': + $time_selected = AdvanceOrderSlot::_12_13; + break; + case '13_14': + $time_selected = AdvanceOrderSlot::_13_14; + break; + case '14_15': + $time_selected = AdvanceOrderSlot::_14_15; + break; + case '15_16': + $time_selected = AdvanceOrderSlot::_15_16; + break; + case '16_17': + $time_selected = AdvanceOrderSlot::_16_17; + break; + default: + error_log('Invalid slot id ' . $slot_id); + } + + return $time_selected; + } + + protected function validateJORequest(Request $req, GeofenceTracker $geo) + { + // geofence + $long = $req->request->get('long'); + $lat = $req->request->get('lat'); + $is_covered = $geo->isCovered($long, $lat); + if (!$is_covered) + { + // TODO: put geofence error message in config file somewhere + $msg = 'Oops! Our service is limited to some areas in Metro Manila, Laguna, and Baguio only. We will update you as soon as we are able to cover your area'; + return $msg; + } + + // validate service type + $stype = $req->request->get('service_type'); + if (!ServiceType::validate($stype)) + { + $msg = 'Invalid service type'; + return $msg; + } + + // validate warranty + $warr = $req->request->get('warranty'); + if (!WarrantyClass::validate($warr)) + { + $msg = 'Invalid warranty class'; + return $msg; + } + + // validate payment method + $payment_method = $req->request->get('mode_of_payment'); + if (!ModeOfPayment::validate($payment_method)) + { + $msg = 'Invalid payment method'; + return $msg; + } + + return null; + } + + protected function setJOObject(JobOrder $jo, Request $req, EntityManagerInterface $em) + { + // longitude and latitude + $long = $req->request->get('long'); + $lat = $req->request->get('lat'); + + $point = new Point($long, $lat); + + // check for hub + $hub = null; + $hub_id = $req->request->get('hub_id'); + if (strlen($hub_id) > 0) + { + $hub = $em->getRepository(Hub::class)->find($hub_id); + $jo->setHub($hub); + } + + // check if advance order + $advance_order = $req->request->get('flag_advance_order'); + // check for 'false' text + if ($advance_order === false || $advance_order === 0 || $advance_order === '0' || $advance_order == 'false') + $flag_advance_order = false; + else + $flag_advance_order = true; + + $jo->setSource(TransactionOrigin::MOBILE_APP) + ->setStatus(JOStatus::PENDING) + ->setDeliveryInstructions('') + ->setTier1Notes('') + ->setTier2Notes('') + ->setDeliveryAddress($req->request->get('delivery_address', 'Set by mobile application')) + ->setTradeInType($req->request->get('trade_in')) + ->setDeliveryInstructions($req->request->get('delivery_instructions', '')) + ->setModeOfPayment($req->request->get('mode_of_payment')) + ->setServiceType($req->request->get('service_type')) + ->setWarrantyClass($req->request->get('warranty')) + ->setCoordinates($point) + ->setAdvanceOrder($flag_advance_order) + ->setStatusAutoAssign(AutoAssignStatus::NOT_ASSIGNED); + } + + protected function setInvoiceCriteria(EntityManagerInterface $em, InvoiceCriteria $icrit, Request $req, + Customer $cust, $jo) + { + $icrit->setServiceType($req->request->get('service_type')); + + // check promo + $promo_id = $req->request->get('promo_id'); + if (!empty($promo_id)) + { + $promo = $em->getRepository(Promo::class)->find($promo_id); + if ($promo == null) + { + $msg = 'Invalid promo id'; + return $msg; + } + + // put in criteria + $icrit->addPromo($promo); + } + + // check customer vehicle + $cv = $em->getRepository(CustomerVehicle::class)->find($req->request->get('cv_id')); + if ($cv == null) + { + $msg = 'Invalid customer vehicle id'; + return $msg; + } + $icrit->setCustomerVehicle($cv); + if ($jo != null) + $jo->setCustomerVehicle($cv); + + // check if customer owns vehicle + if ($cust->getID() != $cv->getCustomer()->getID()) + { + $msg = 'Customer does not own vehicle'; + return $msg; + } + + // check battery + $batt_id = $req->request->get('batt_id'); + if ($batt_id != null) + { + $batt = $em->getRepository(Battery::class)->find($batt_id); + if ($batt == null) + { + $msg = 'Invalid battery id'; + return $msg; + } + } + else + $batt = null; + + /* + // put battery in criteria + $icrit->addBattery($batt); + */ + + // check trade-in + // only allow motolite, other, none + $trade_in = $req->request->get('trade_in'); + switch ($trade_in) + { + case TradeInType::MOTOLITE: + case TradeInType::OTHER: + break; + + default: + $trade_in = ''; + break; + } + + $icrit->addEntry($batt, $trade_in, 1); + + return null; + } + + protected function setHubCriteria(EntityManagerInterface $em, HubFilteringGeoChecker $hub_geofence, + HubCriteria $hub_criteria, JobOrder $jo, Request $req) + { + $hub_criteria->setPoint($jo->getCoordinates()); + $long = $jo->getCoordinates()->getLongitude(); + $lat = $jo->getCoordinates()->getLatitude(); + + if ($hub_geofence->isCovered($long, $lat)) + { + // TODO: set this properly, since the other flags + // are on default values. + // if true, set other values for HubCriteria + // error_log('Area is covered by hub filtering'); + $hub_criteria->setJoType($jo->getServiceType()) + ->setPaymentMethod($jo->getModeOfPayment()) + ->setRoundRobin(true); + } + + $batt_id = $req->request->get('batt_id'); + if ($batt_id != null) + { + $batt = $em->getRepository(Battery::class)->find($batt_id); + if ($batt != null) + { + // add battery to items + $sku = $batt->getSAPCode(); + if (!empty($sku)) + $hub_criteria->addItem($batt->getSAPCode(), 1); + } + } + } + + protected function assignRider($nearest_hubs, JobOrder $jo, HubFilterLogger $hub_filter_logger, + HubDistributor $hub_dist) + { + // try to assin rider + // go through the hub list, find the nearest hub + // with an available rider + //error_log('found nearest hub ' . $nearest_hub->getID()); + foreach ($nearest_hubs as $nearest_hub) + { + $available_riders = $nearest_hub['hub']->getAvailableRiders(); + if (count($available_riders) >= 1) + { + $assigned_rider = null; + if (count($available_riders) == 1) + { + $assigned_rider = $available_riders[0]; + } + else + { + // TODO: the setting of riders into an array + // will no longer be necessary when the contents + // of randomizeRider changes + $riders = []; + foreach ($available_riders as $rider) + { + $riders[] = $rider; + } + + $assigned_rider = $this->randomizeRider($riders); + } + + $jo->setHub($nearest_hub['hub']); + $jo->setRider($assigned_rider); + $jo->setStatus(JOStatus::ASSIGNED); + $jo->setStatusAutoAssign(AutoAssignStatus::HUB_AND_RIDER_ASSIGNED); + + $assigned_rider->setAvailable(false); + + // update redis hub_jo_count for hub + $hub_dist->incrementJoCountForHub($nearest_hub['hub']); + + // break out of loop + break; + } + else + { + // log hub into hub_filter_log + $hub_filter_logger->logFilteredHub($nearest_hub['hub'], 'no_available_rider', $jo->getID(), $jo->getCustomer()->getID()); + // continue to go through list to find hub with an available rider + } + } + } + + protected function processMQTTEvents(JobOrder $jo, $payload, MQTTClient $mclient, + RiderAssignmentHandlerInterface$rah) + { + // check JO status + if ($jo->getStatus() == JOStatus::ASSIGNED) + { + $mclient->sendEvent($jo, $payload); + $rah->assignJobOrder($jo, $jo->getRider()); + } + if ($jo->getStatus() == JOStatus::RIDER_ASSIGN) + { + $mclient->sendEvent($jo, $payload); + } + if ($jo->getStatus() == JOStatus::CANCELLED) + { + $mclient->sendRiderEvent($jo, $payload); + } + } + + protected function makeInvoiceData(Invoice $invoice, MobileAPIHandler$mah, Request $req) + { + // make invoice json data + $invoice_data = [ + 'total_price' => $invoice->getTotalPrice(), + 'vat_ex_price' => $invoice->getVATExclusivePrice(), + 'vat' => $invoice->getVAT(), + 'discount' => $invoice->getDiscount(), + 'trade_in' => $invoice->getTradeIn(), + ]; + $items = $invoice->getItems(); + $items_data = []; + foreach ($items as $item) + { + $my_data = [ + 'title' => $item->getTitle(), + 'qty' => (int) $item->getQuantity() + 0, + 'price' => (float) $item->getPrice() + 0.0, + ]; + + $item_batt = $item->getBattery(); + if ($item_batt != null) + { + // generate the url for image + $battery_image_url = $this->generateUrl('static_battery_image'); + $my_data['image_url'] = $mah->getBatteryImageURL($req, $item_batt, $battery_image_url); + } + + $items_data[] = $my_data; + } + + $invoice_data['items'] = $items_data; + return $invoice_data; + } + + protected function generateJobOrderData($req, $jo, $rt, $mah) + { + $status = $jo->getStatus(); + $dest = $jo->getCoordinates(); + + $jo_data = [ + 'id' => $jo->getID(), + 'date_create' => $jo->getDateCreate()->format('M d, Y'), + 'service_type' => $jo->getServiceType(), + 'destination' => [ + 'long' => $dest->getLongitude(), + 'lat' => $dest->getLatitude(), + ], + 'delivery_address' => $jo->getDeliveryAddress(), + 'delivery_instructions' => $jo->getDeliveryInstructions(), + 'jo_status' => $status, + 'status' => $this->generateAPIRiderStatus($status), + ]; + + // customer vehicle and warranty + $cv = $jo->getCustomerVehicle(); + + // get latest warranty using plate number + $warranty = $mah->findWarranty($cv->getPlateNumber()); + + $jo_data['customer_vehicle'] = [ + 'id' => $cv->getID(), + 'plate_number' => $cv->getPlateNumber(), + 'warranty' => $warranty, + ]; + + // rider + $rider = $jo->getRider(); + if ($rider != null) + { + // default image url + $url_prefix = $req->getSchemeAndHttpHost(); + $image_url = $url_prefix . '/assets/images/user.gif'; + if ($rider->getImageFile() != null) + $image_url = $url_prefix . '/uploads/' . $rider->getImageFile(); + + $coord = $rt->getRiderLocation($rider->getID()); + + $jo_data['rider'] = [ + 'id' => $rider->getID(), + 'name' => $rider->getFullName(), + 'plate_num' => $rider->getPlateNumber(), + 'contact_num' => $rider->getContactNumber(), + 'image_url' => $image_url, + 'location' => [ + 'long' => $coord->getLongitude(), + 'lat' => $coord->getLatitude() + ] + ]; + } + else + { + $jo_data['rider'] = null; + } + + // invoice items + $items = []; + $jo_items = $jo->getInvoice()->getItems(); + foreach ($jo_items as $item) + { + $items[] = [ + 'id' => $item->getID(), + 'title' => $item->getTitle(), + 'qty' => $item->getQuantity(), + 'price' => $item->getPrice(), + ]; + } + + $jo_data['items'] = $items; + + // dates depending on status + switch ($status) + { + case JOStatus::FULFILLED: + if ($jo->getDateFulfill() == null) + $jo_data['date_fulfilled'] = ''; + else + $jo_data['date_fulfilled'] = $jo->getDateFulfill()->format('M d, Y'); + break; + case JOStatus::CANCELLED: + $date_cancel = $jo->getDateCancel(); + if ($date_cancel == null) + $date_cancel = new DateTime(); + $jo_data['date_cancelled'] = $date_cancel->format('M d, Y'); + break; + } + + return $jo_data; + } + + protected function generateAPIRiderStatus($status) + { + switch ($status) + { + case JOStatus::PENDING: + return APIRiderStatus::OUTLET_ASSIGN; + case JOStatus::RIDER_ASSIGN: + return APIRiderStatus::RIDER_ASSIGN; + case JOStatus::ASSIGNED: + case JOStatus::IN_TRANSIT: + case JOStatus::IN_PROGRESS: + return APIRiderStatus::RIDER_PICK_UP; + } + return 'unknown'; + } +} diff --git a/src/Controller/ResqAPI/PartnerController.php b/src/Controller/ResqAPI/PartnerController.php new file mode 100644 index 00000000..01c3609b --- /dev/null +++ b/src/Controller/ResqAPI/PartnerController.php @@ -0,0 +1,200 @@ +acl_gen = $acl_gen; + } + + public function getClosestPartners(Request $req, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_partner.list', null, 'No access.'); + + // check required parameters + $required_params = [ + 'longitude', + 'latitude', + 'service_id', + 'limit', + ]; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $long = $req->request->get('longitude'); + $lat = $req->request->get('latitude'); + $service_id = $req->request->get('service_id'); + $limit = $req->request->get('limit'); + + // get partners within range + $query = $em->createQuery('SELECT p, st_distance(p.coordinates, point(:lng, :lat)) as dist FROM App\Entity\Partner p + JOIN App\Entity\Service s where s.id = :service_id ORDER BY dist') + ->setParameter('lat', $lat) + ->setParameter('lng', $long) + ->setParameter('service_id', $service_id); + + $query->setMaxResults($limit); + $result = $query->getResult(); + + $partners = []; + foreach($result as $row) + { + error_log($row[0]->getID()); + $partners[] = [ + 'id' => $row[0]->getID(), + 'name' => $row[0]->getName(), + 'branch' => $row[0]->getBranch(), + 'address' => $row[0]->getAddress(), + 'contact_nums' => $row[0]->getContactNumbers(), + 'time_open' => $row[0]->getTimeOpen()->format("g:i A"), + 'time_close' => $row[0]->getTimeClose()->format("g:i A"), + 'longitude' => $row[0]->getCoordinates()->getLongitude(), + 'latitude' => $row[0]->getCoordinates()->getLatitude(), + 'db_distance' => $row['dist'], + ]; + } + + $data['partners'] = $partners; + + return new APIResponse(true, 'Partners found', $data); + } + + public function getPartnerInformation(Request $req, $pid, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_partner.info', null, 'No access.'); + + // check required parameters + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // get partner + $partner = $em->getRepository(Partner::class)->findOneBy(['id' => $pid]); + if ($partner == null) + return new APIResponse(false, 'No partner found.'); + + // get reviews for partner + $reviews = $em->getRepository(Review::class)->findBy(['partner' => $partner]); + + // get average rating for all reviews + $average_rating = 0; + if (!empty($reviews)) + { + $rating = 0; + foreach($reviews as $review) + { + $rating = $rating + $review->getRating(); + } + + $average_rating = $rating / sizeof($reviews); + } + + $data['partner'] = [ + 'id' => $partner->getID(), + 'name' => $partner->getName(), + 'branch' => $partner->getBranch(), + 'address' => $partner->getAddress(), + 'contact_nums' => $partner->getContactNumbers(), + 'time_open' => $partner->getTimeOpen()->format("g:i A"), + 'time_close' => $partner->getTimeClose()->format("g:i A"), + 'longitude' => $partner->getCoordinates()->getLongitude(), + 'latitude' => $partner->getCoordinates()->getLatitude(), + 'average_rating' => $average_rating, + ]; + + return new APIResponse(true, 'Partner information found', $data); + } + + public function reviewPartner($pid, Request $req, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_partner.review', null, 'No access.'); + + $required_params = [ + 'rating', + 'message', + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $rating = $req->request->get('rating'); + $msg = $req->request->get('message'); + + // TODO: check rating if 1 - 5 + + // check if partner exists + $partner = $em->getRepository(Partner::class)->find($pid); + if ($partner == null) + return new APIResponse(false, 'No partner found.'); + + $rev = new Review(); + $rev->setRating($rating) + ->setMessage($msg) + ->setPartner($partner) + ->setMobileUser($mobile_user); + + // save to db + $em->persist($rev); + $em->flush(); + + $data = [ + 'rating' => $rev->getRating(), + 'message' => $rev->getMessage(), + ]; + + return new APIResponse(true, 'Review added', $data); + } +} diff --git a/src/Controller/ResqAPI/PromoController.php b/src/Controller/ResqAPI/PromoController.php new file mode 100644 index 00000000..74fdb850 --- /dev/null +++ b/src/Controller/ResqAPI/PromoController.php @@ -0,0 +1,39 @@ +acl_gen = $acl_gen; + } + + public function listPromos(Request $req, EntityManagerInterface $em) + { + $this->denyAccessUnlessGranted('mobile_promo.list', null, 'No access.'); + + // check required parameters + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + return new APIResponse(true, 'Promos listed'); + } +} diff --git a/src/Controller/ResqAPI/RiderController.php b/src/Controller/ResqAPI/RiderController.php new file mode 100644 index 00000000..8422db81 --- /dev/null +++ b/src/Controller/ResqAPI/RiderController.php @@ -0,0 +1,277 @@ +acl_gen = $acl_gen; + } + + public function getRiderStatus(Request $req, RiderTracker $rt, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_rider.status.get', null, 'No access.'); + + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // get customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + $ongoing_jos = $mah->getOngoingJobOrders($cust); + + $data = []; + if (count($ongoing_jos) <= 0) + { + try + { + // check if the latest fulfilled jo they have needs rider rating + $query = $em->createQuery('select jo from App\Entity\JobOrder jo where jo.customer = :cust and jo.status = :status order by jo.date_fulfill desc'); + $fulfill_jo = $query->setParameters([ + 'cust' => $cust, + 'status' => JOStatus::FULFILLED, + ]) + ->setMaxResults(1) + ->getSingleResult(); + } + catch (Exception $e) + { + // no pending + $data[] = [ + 'status' => APIRiderStatus::NO_PENDING_JO, + ]; + return new APIResponse(true, 'No pending job order', $data); + } + + // we got a recently fulfilled job order + if ($fulfill_jo) + { + // check if the rider has been rated + if (!$fulfill_jo->hasRiderRating()) + { + $dest = $fulfill_jo->getCoordinates(); + + $data[] = [ + 'jo_id' => $fulfill_jo->getID(), + 'service_type' => $fulfill_jo->getServiceType(), + 'destination' => [ + 'long' => $dest->getLongitude(), + 'lat' => $dest->getLatitude(), + ], + 'delivery_address' => $fulfill_jo->getDeliveryAddress(), + 'delivery_instructions' => $fulfill_jo->getDeliveryInstructions(), + ]; + + $rider = $fulfill_jo->getRider(); + + // default image url + $url_prefix = $req->getSchemeAndHttpHost(); + $image_url = $url_prefix . '/assets/images/user.gif'; + if ($rider->getImageFile() != null) + $image_url = $url_prefix . '/uploads/' . $rider->getImageFile(); + + $data[] = [ + 'status' => APIRiderStatus::RIDER_RATING + ]; + // default rider location to hub + $data['rider'] = [ + 'id' => $rider->getID(), + 'name' => $rider->getFullName(), + 'plate_num' => $rider->getPlateNumber(), + 'contact_num' => $rider->getContactNumber(), + 'image_url' => $image_url, + ]; + return new APIResponse(true, 'Rider status found', $data); + } + } + + // no pending + $data[] = [ + 'status' => APIRiderStatus::NO_PENDING_JO, + ]; + return new APIResponse(true, 'No pending job order', $data); + } + + // get first jo that's pending + $jo = $ongoing_jos[0]; + $dest = $jo->getCoordinates(); + + $data = [ + 'jo_id' => $jo->getID(), + 'service_type' => $jo->getServiceType(), + 'destination' => [ + 'long' => $dest->getLongitude(), + 'lat' => $dest->getLatitude(), + ], + 'delivery_address' => $jo->getDeliveryAddress(), + 'delivery_instructions' => $jo->getDeliveryInstructions(), + ]; + + switch ($jo->getStatus()) + { + case JOStatus::PENDING: + $data['status'] = APIRiderStatus::OUTLET_ASSIGN; + $res->setData($data); + return $res->getReturnResponse(); + case JOStatus::RIDER_ASSIGN: + $data['status'] = APIRiderStatus::RIDER_ASSIGN; + $res->setData($data); + return $res->getReturnResponse(); + case JOStatus::ASSIGNED: + case JOStatus::IN_TRANSIT: + case JOStatus::IN_PROGRESS: + $rider = $jo->getRider(); + // get rider coordinates from redis + $coord = $rt->getRiderLocation($rider->getID()); + + // default image url + $url_prefix = $req->getSchemeAndHttpHost(); + $image_url = $url_prefix . '/assets/images/user.gif'; + if ($rider->getImageFile() != null) + $image_url = $url_prefix . '/uploads/' . $rider->getImageFile(); + + $data['status'] = APIRiderStatus::RIDER_PICK_UP; + // TODO: fix this to actual location of rider + // default rider location to hub + $data['rider'] = [ + 'id' => $rider->getID(), + 'name' => $rider->getFullName(), + 'plate_num' => $rider->getPlateNumber(), + 'contact_num' => $rider->getContactNumber(), + 'image_url' => $image_url, + 'location' => [ + 'long' => $coord->getLongitude(), + 'lat' => $coord->getLatitude() + ] + ]; + + return new APIResponse(true, 'Rider status found', $data); + } + + return new APIResponse(true, 'Rider status found', $data); + } + + public function addRiderRating(Request $req, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_rider.rating.add', null, 'No access.'); + + $required_params = [ + 'jo_id', + 'rating', + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // get customer + $cust = $mobile_user->getCustomer(); + if ($cust == null) + return new APIResponse(false, 'No customer information found'); + + // get job order + $jo_id = $req->request->get('jo_id'); + $jo = $em->getRepository(JobOrder::class)->find($jo_id); + if ($jo == null) + return new APIResponse(false, 'No job order found'); + + // get rider + $rider = $jo->getRider(); + if ($rider == null) + return new APIResponse(false, 'No rider found'); + + // check that the customer owns the job order + $jo_cust = $jo->getCustomer(); + if ($jo_cust->getID() != $cust->getID()) + return new APIResponse(false, 'Job order was not initiated by customer;'); + + // TODO: check job order status, if it's complete + + // add rider rating + $rating_num = $req->request->get('rating', -1); + + // if rating is -1 + if ($rating_num == -1) + { + $jo->setHasRiderRating(); + $em->flush(); + + return new APIResponse(false, 'No rider rating', $data); + } + + + $rating = new RiderRating(); + $rating->setRider($rider) + ->setCustomer($cust) + ->setJobOrder($jo) + ->setRating($rating_num); + + // rider rating comment + $comment = $req->request->get('comment'); + if (!empty($comment)) + $rating->setComment($comment); + + // mark jo as rider rated already + $jo->setHasRiderRating(); + + $em->persist($rating); + $em->flush(); + + $data = [ + 'rating' => $rating->getRating(), + 'comment' => $rating->getComment(), + ]; + + // TODO: set average rating in rider entity + + return new APIResponse(true, 'Rider rating added', $data); + } +} diff --git a/src/Controller/ResqAPI/ServiceController.php b/src/Controller/ResqAPI/ServiceController.php new file mode 100644 index 00000000..e1b45dfa --- /dev/null +++ b/src/Controller/ResqAPI/ServiceController.php @@ -0,0 +1,67 @@ +acl_gen = $acl_gen; + } + + public function listServices(Request $req, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_service.list', null, 'No access.'); + + // check required parameters + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // services + $results = $em->getRepository(Service::class)->findAll(); + if (empty($results)) + return new APIResponse(false, 'No services available'); + + $services = []; + foreach ($results as $result) + { + $services[] = [ + 'id' => $result->getID(), + 'name' => $result->getName(), + ]; + } + + $data['services'] = $services; + + return new APIResponse(true, 'Services found', $data); + } +} diff --git a/src/Controller/ResqAPI/VehicleController.php b/src/Controller/ResqAPI/VehicleController.php new file mode 100644 index 00000000..3f949aec --- /dev/null +++ b/src/Controller/ResqAPI/VehicleController.php @@ -0,0 +1,122 @@ +acl_gen = $acl_gen; + } + + public function listVehicleManufacturers(Request $req, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_vmanufacturer.list', null, 'No access.'); + + // check required parameters + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // get manufacturer list + $mfgs = $em->getRepository(VehicleManufacturer::class)->findBy(['flag_mobile' => true], ['name' => 'asc']); + $mfg_list = []; + foreach ($mfgs as $mfg) + { + $mfg_list[] = [ + 'id' => $mfg->getID(), + 'name' => $mfg->getName(), + ]; + } + + $data = [ + 'manufacturers' => $mfg_list + ]; + + return new APIResponse(true, 'Vehicle manufacturers listed.', $data); + } + + public function listVehicleMakes(Request $req, $mfg_id, EntityManagerInterface $em, + MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_vehicle.list', null, 'No access.'); + + // check required parameters + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // get manufacturer + $mfg = $em->getRepository(VehicleManufacturer::class)->find($mfg_id); + if ($mfg == null) + return new APIResponse(false, 'Invalid vehicle manufacturer id'); + + // get makes + $vehicles = $em->getRepository(Vehicle::class)->findBy( + [ + 'flag_mobile' => true, + 'manufacturer' => $mfg_id, + ], + ['make' => 'asc'] + ); + // $vehicles = $mfg->getVehicles(); + $vlist = []; + foreach ($vehicles as $v) + { + $vlist[] = [ + 'id' => $v->getID(), + 'make' => trim($v->getMake() . ' ' . $v->getModelYearFormatted(false)), + ]; + } + + $data = [ + 'manufacturer' => [ + 'id' => $mfg->getID(), + 'name' => $mfg->getName(), + ], + 'makes' => $vlist, + ]; + + return new APIResponse(true, 'Vehicle makes listed.', $data); + } + +} diff --git a/src/Controller/ResqAPI/WarrantyController.php b/src/Controller/ResqAPI/WarrantyController.php new file mode 100644 index 00000000..8198098f --- /dev/null +++ b/src/Controller/ResqAPI/WarrantyController.php @@ -0,0 +1,563 @@ +acl_gen = $acl_gen; + } + + public function warrantyRegister($serial, EntityManagerInterface $em, Request $req, KernelInterface $kernel, RisingTideGateway $rt, + TranslatorInterface $trans, WarrantyAPILogger $logger, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_jo.request', null, 'No access.'); + + // check required parameters + $required_params = [ + 'first_name', + 'last_name', + 'plate_number', + 'date_purchase', + ]; + + // handle file uploads + $invoice = $req->files->get('invoice'); + $warr_card = $req->files->get('warr_card'); + + // normalize serial + $serial = trim(strtoupper($serial)); + + // process picture uploads + $upload_dir = $kernel->getProjectDir() . '/public/warranty_uploads'; + $inv_filename = $this->handlePictureUpload($invoice, $upload_dir, $serial, 'invoice'); + $wcard_filename = $this->handlePictureUpload($warr_card, $upload_dir, $serial, 'wcard'); + + // get capi user to link to mobile user + $capi_user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($capi_user_id); + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $user_id = $mobile_user->getID(); + + // prepare logging data + $action = 'create'; + $source = WarrantySource::MOBILE; + $log_data = [ + 'plate_number' => $req->request->get('plate_num'), + 'first_name' => $req->request->get('first_name'), + 'last_name' => $req->request->get('last_name'), + 'date_purchase' => $req->request->get('date_purchase'), + ]; + + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + { + $logger->logWarrantyInfo($log_data, $msg, $user_id, $action, $source); + return new APIResponse(false, $msg); + } + + // update customer information + // $cust = $this->updateCustomerInfo($req, $em); + + // update warranty + $data = []; + $msg = $this->updateWarranty($data, $em, $rt, $trans, $req, $serial, $inv_filename, $wcard_filename, + $logger, $log_data, $user_id, $action, $source, $mobile_user); + if ($msg != null) + return new APIResponse(false, $msg); + + $em->flush(); + + return new APIResponse(true, 'Warranty registered', $data); + } + + public function warrantyCheck($serial, EntityManagerInterface $em, Request $req, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_warranty.check', null, 'No access.'); + + // check required parameters + $required_params = []; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + // check if warranty serial is there + $warr_serial = $em->getRepository(WarrantySerial::class)->find($serial); + if ($warr_serial == null) + return new APIResponse(false, 'Invalid warranty serial code'); + + $warr = $em->getRepository(Warranty::class)->findOneBy(['serial' => $serial]); + $batt = null; + $is_registered = false; + $today = new DateTime(); + + // if we have a warranty entry for the serial already + if ($warr != null) + { + $warr_plate = $warr->getPlateNumber(); + $is_registered = true; + $is_customer_warranty = false; + + // check if the warranty is registered to a car owned by the customer + $cust = $mobile_user->getCustomer(); + $is_customer_warranty = $this->checkCustomerPlateNumber($warr_plate, $cust); + + // null mobile number should be blank string instead + if ($warr->getMobileNumber() == null) + $mobile_num = ''; + else + $mobile_num = $warr->getMobileNumber(); + + $can_edit = $is_customer_warranty; + + // if customer plate number matches the one registered on the warranty + if ($is_customer_warranty) + { + // purchase date of customer + if ($warr->getDatePurchaseCustomer() != null) + $date_purchase_cust = $warr->getDatePurchaseCustomer()->format('Y-m-d'); + else + $date_purchase_cust = $today->format('Y-m-d'); + + // invoice + if ($warr->getFileInvoice() != null) + $invoice_url = $req->getSchemeAndHttpHost() . '/warranty_uploads/' . $warr->getFileInvoice(); + else + $invoice_url = ''; + + // warranty card + if ($warr->getFileWarrantyCard() != null) + $warr_card_url = $req->getSchemeAndHttpHost() . '/warranty_uploads/' . $warr->getFileWarrantyCard(); + else + $warr_card_url = ''; + + $customer = [ + 'first_name' => $warr->getFirstName() ?? '', + 'last_name' => $warr->getLastName() ?? '', + 'mobile_number' => $mobile_num, + 'plate_number' => $warr_plate, + 'email' => $warr->getEmail() ?? '', + 'contact_num' => $warr->getContactNumber() ?? '', + 'address' => $warr->getCustomerAddress() ?? '', + ]; + $other_data = [ + 'odometer' => (int) $warr->getOdometer() ?? 0, + 'date_purchase' => $date_purchase_cust, + 'invoice' => $invoice_url, + 'warr_card' => $warr_card_url, + 'dealer_name' => $warr->getDealerName() ?? '', + 'dealer_address' => $warr->getDealerAddress() ?? '', + ]; + } + else + { + // hide customer information if customer is not the one registered + $customer = [ + 'first_name' => '', + 'last_name' => '', + 'mobile_number' => '', + 'plate_number' => '', + 'email' => '', + 'contact_num' => '', + 'address' => '', + ]; + $other_data = [ + 'odometer' => 0, + 'date_purchase' => $today->format('Y-m-d'), + 'invoice' => '', + 'warr_card' => '', + 'dealer_name' => '', + 'dealer_address' => '', + ]; + } + } + else + { + $can_edit = true; + $customer = [ + 'first_name' => '', + 'last_name' => '', + 'mobile_number' => '', + 'plate_number' => '', + 'email' => '', + 'contact_num' => '', + 'address' => '', + ]; + $other_data = [ + 'odometer' => 0, + 'date_purchase' => $today->format('Y-m-d'), + 'invoice' => '', + 'warr_card' => '', + 'dealer_name' => '', + 'dealer_address' => '', + ]; + } + + $sku = $warr_serial->getSKU(); + $batt = null; + $cat_name = ''; + if ($sku != null) + $batt = $em->getRepository(SAPBattery::class)->find($sku); + else + { + // get the category name of the serial + $cat_name = $warr_serial->getMetaInfo('category_name'); + } + + // TODO: put this in a config file + $image_url = $req->getSchemeAndHttpHost() . '/battery/generic.png'; + if ($batt != null) + { + $battery = [ + 'brand' => $batt->getBrand()->getName(), + 'size' => $batt->getSize()->getName(), + 'image_url' => $image_url, + ]; + } + else + { + $battery = [ + 'brand' => $cat_name, + 'size' => '', + 'image_url' => '', + ]; + } + + // populate data + $data = [ + 'is_valid' => true, + 'is_registered' => $is_registered, + 'can_edit' => $can_edit, + 'customer' => $customer, + 'battery' => $battery, + 'odometer' => $other_data['odometer'], + 'invoice' => $other_data['invoice'], + 'warr_card' => $other_data['warr_card'], + 'date_purchase' => $other_data['date_purchase'], + 'dealer_name' => $other_data['dealer_name'], + 'dealer_address' => $other_data['dealer_address'], + 'message' => [ + 'register_error' => 'Warranty serial code has already been registered.', + 'edit_error' => 'Sorry, warranty is registered under another vehicle not in your list of vehicles.', + ], + ]; + + return new APIResponse(true, 'Warranty checked', $data); + } + + public function activateWarranty(Request $req, EntityManagerInterface $em, MobileAPIHandler $mah) + { + $this->denyAccessUnlessGranted('mobile_warranty.activate', null, 'No access.'); + + $required_params = ['plate_number']; + $msg = $this->checkRequiredParameters($req, $required_params); + if ($msg) + return new APIResponse(false, $msg); + + // get capi user to link to mobile user + $user_id = $this->getUser()->getID(); + + // get mobile user + $mobile_user = $mah->findMobileUser($user_id); + + if ($mobile_user == null) + return new APIResponse(false, 'No mobile user found.'); + + $plate_number = $req->request->get('plate_number'); + + // find warranty using plate number + $warranty_results = $em->getRepository(Warranty::class)->findBy(['plate_number' => $plate_number], + ['date_create' => 'desc']); + + // check if warranty_results is empty + if (empty($warranty_results)) + return new APIResponse(false, 'No warranty found for plate number'); + + // activate all entries + foreach ($warranty_results as $warranty) + { + $warranty->setActivated(); + } + + $em->flush(); + + $data = $this->generateWarrantyData($warranty); + + return new APIResponse(true, 'Warranty activated', $data); + } + + protected function handlePictureUpload($file, $target_dir, $serial, $name) + { + error_log("handling $name upload"); + // no file sent + if ($file == null) + { + error_log('no file'); + return null; + } + + // create target dir if it doesn't exist + if (!file_exists($target_dir)) + { + if (!mkdir($target_dir, 0744, true)) + { + error_log('failed to create folder for warranty pictures'); + return null; + } + } + + // move file + $filename = $name . '.' . $file->getClientOriginalExtension(); + $file->move($target_dir . '/' . $serial, $filename); + + error_log("filename - $filename"); + error_log($target_dir . '/' . $serial . '/' . $filename); + + return $serial . '/' . $filename; + } + + protected function updateWarranty(&$data, $em, $rt, $trans, $req, $serial, $inv_filename = null, $wcard_filename = null, + $logger, $log_data, $user_id, $action, $source, $mobile_user) + { + // get serial + $warr_serial = $em->getRepository(WarrantySerial::class)->find($serial); + if ($warr_serial == null) + { + $msg = 'Invalid warranty serial code.'; + $logger->logWarrantyInfo($log_data, $msg, $user_id, $action, $source); + return $msg; + } + + // check if warranty exists already + $warr = $em->getRepository(Warranty::class)->findOneBy(['serial' => $serial]); + + // skip warranty if it already exists + if ($warr != null) + { + /* + // NOTE: we could not update in the old version + $res->setError(true) + ->setErrorMessage('Warranty registration entry already exists.'); + return $res; + */ + + // check if warranty is registered to a serial owned by customer + $warr_plate = $warr->getPlateNumber(); + $cust = $mobile_user->getCustomer(); + $is_customer_warranty = $this->checkCustomerPlateNumber($warr_plate, $cust); + + if (!$is_customer_warranty) + { + $msg = 'Warranty registered to a vehicle not in your list of vehicles.'; + $logger->logWarrantyInfo($log_data, $msg, $user_id, $action, $source); + return $msg; + } + + $sms_msg = $trans->trans('warranty_update_confirm'); + } + else + { + $warr = new Warranty(); + $sms_msg = $trans->trans('warranty_register_confirm'); + + // set warranty source + $warr->setCreateSource($source); + } + + // get sap battery + $sku = $warr_serial->getSKU(); + $sap_bty = null; + if ($sku != null) + { + $sap_bty = $em->getRepository(SAPBattery::class)->find($sku); + if ($sap_bty == null) + { + $msg = 'Could not find battery entry for warranty.'; + $logger->logWarrantyInfo($log_data, $msg, $user_id, $action, $source); + return $msg; + } + } + + // default date purchase to today + // NOTE: might need to change this later + $date_pur = new DateTime(); + + // get date purchase specified by customer + $date_pur_cust = DateTime::createFromFormat('Y-m-d', $req->request->get('date_purchase')); + if (!$date_pur_cust) + { + $msg = 'Invalid date format for date of purchase.'; + $logger->logWarrantyInfo($log_data, $msg, $user_id, $action, $source); + return $msg; + } + + $customer = $mobile_user->getCustomer(); + if ($customer != null) + { + $warr->setCustomer($customer); + // get customer vehicles + + $vehicle = $this->findCustomerVehicle($em, $customer, $req->request->get('plate_number')); + if ($vehicle != null) + $warr->setVehicle($vehicle); + } + + // create or update warranty entry + $warr->setSerial($serial) + ->setFirstName($req->request->get('first_name')) + ->setLastName($req->request->get('last_name')) + ->setEmail($req->request->get('email')) + ->setPlateNumber($req->request->get('plate_number')) + // TODO: figure out how to compute date of purchase + ->setDatePurchase($date_pur) + // TODO: set status + // ->setStatus() + // TODO: set battery model and size id + // ->setBatterySize() + // ->setBatteryModel() + ->setSAPBattery($sap_bty) + ->setMobileNumber(substr($mobile_user->getPhoneNumber(), 2)) + ->setActivated(true) + + // files + ->setFileInvoice($inv_filename) + ->setFileWarrantyCard($wcard_filename) + + // new fields + ->setOdometer($req->request->get('odometer', 0)) + ->setDatePurchaseCustomer($date_pur_cust) + ->setContactNumber($req->request->get('contact_num')) + ->setCustomerAddress($req->request->get('cust_address')) + ->setDealerName($req->request->get('dealer_name')) + ->setDealerAddress($req->request->get('dealer_address')) + ->setValidated(false); + + // TODO: check for date purchase and date expire + + $em->persist($warr); + + // TODO: check if we need to do anything else + // TODO: put warranty data into data + $data = $this->generateWarrantyData($warr); + + $logger->logWarrantyInfo($log_data, '', $user_id, $action, $source); + + // send sms + error_log('sending sms to - ' . $mobile_user->getPhoneNumber()); + $rt->sendSMS($mobile_user->getPhoneNumber(), 'MOTOLITE', $sms_msg); + } + + protected function findCustomerVehicle($em, $customer, $plate_number) + { + $clean_plate = Warranty::cleanPlateNumber($plate_number); + if ($clean_plate) + { + // find the customer vehicle and get the vehicle + $cv = $em->getRepository(CustomerVehicle::class)->findOneBy(['plate_number' => $clean_plate, 'customer' => $customer]); + if ($cv != null) + { + $vehicle = $cv->getVehicle(); + return $vehicle; + } + } + + return null; + } + + protected function checkCustomerPlateNumber($plate_number, $cust) + { + // strip spaces and make all caps + $plate_number = preg_replace('/\s+/', '', strtoupper($plate_number)); + + // if there's no customer linked to session + if ($cust != null) + { + // check all the customer vehicles + $cvs = $cust->getVehicles(); + foreach ($cvs as $cv) + { + $cv_plate = preg_replace('/\s+/', '', strtoupper($cv->getPlateNumber())); + + // did we find a match? + if ($cv_plate == $plate_number) + { + return true; + } + } + } + + return false; + } + + protected function generateWarrantyData($warranty) + { + $sap_bty_name = ''; + if ($warranty->getSAPBAttery() != null) + $sap_bty_name = $warranty->getSAPBattery()->getID(); + + $data = [ + 'serial' => $warranty->getSerial(), + 'first_name' => $warranty->getFirstName(), + 'last_name' => $warranty->getLastName(), + 'email' => $warranty->getEmail() ?? '', + 'plate_number' => $warranty->getPlateNumber(), + 'date_purchase' => $warranty->getDatePurchase()->format('Y-m-d'), + 'sap_battery' => $sap_bty_name, + 'mobile_number' => $warranty->getMobileNumber() ?? '', + 'activated' => (boolean) $warranty->isActivated(), + 'invoice_file' => $warranty->getFileInvoice() ?? '', + 'warranty_card_file' => $warranty->getFileWarrantyCard() ?? '', + 'odometer' => $warranty->getOdometer() ?? 0, + 'date_purchase_customer' => $warranty->getDatePurchaseCustomer()->format('Y-m-d'), + 'contact_number' => $warranty->getContactNumber() ?? '', + 'customer_address' => $warranty->getCustomerAddress() ?? '', + 'dealer_name' => $warranty->getDealerName() ?? '', + 'dealer_address' => $warranty->getDealerAddress() ?? '', + 'validated' => (boolean) $warranty->isValidated(), + ]; + + return $data; + } +} diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php index 2257bfb2..54f9e8ba 100644 --- a/src/Entity/Customer.php +++ b/src/Entity/Customer.php @@ -215,6 +215,12 @@ class Customer */ protected $customer_tags; + // mobile users linked to this customer + /** + * @ORM\OneToMany(targetEntity="MobileUser", mappedBy="customer") + */ + protected $mobile_users; + public function __construct() { $this->numbers = new ArrayCollection(); @@ -222,6 +228,7 @@ class Customer $this->vehicles = new ArrayCollection(); $this->job_orders = new ArrayCollection(); $this->customer_tags = new ArrayCollection(); + $this->mobile_users = new ArrayCollection(); $this->customer_classification = CustomerClassification::REGULAR; $this->customer_notes = ''; @@ -656,4 +663,22 @@ class Customer $this->customer_tags->removeElement($customer_tag); $customer_tag->removeCustomer($this); } + + public function addMobileUser(MobileUser $mobile_user) + { + $this->mobile_users->add($mobile_user); + return $this; + } + + public function clearMobileUsers() + { + $this->mobile_users->clear(); + return $this; + } + + public function getMobileUsers() + { + return $this->mobile_users; + } + } diff --git a/src/Entity/MobileUser.php b/src/Entity/MobileUser.php new file mode 100644 index 00000000..f31a04fb --- /dev/null +++ b/src/Entity/MobileUser.php @@ -0,0 +1,277 @@ +id = $this->generateKeyID(); + $this->date_generated = new DateTime(); + $this->customer = null; + $this->confirm_flag = false; + $this->date_confirmed = null; + $this->date_code_sent = null; + $this->reviews = new ArrayCollection(); + $this->capi_user_id = 0; + } + + public function generateKeyID() + { + // use uniqid for now, since primary key dupes will trigger exceptions + return uniqid(); + } + + public function getID() + { + return $this->id; + } + + public function setPhoneModel($model) + { + $this->phone_model = $model; + return $this; + } + + public function getPhoneModel() + { + return $this->phone_model; + } + + public function setOSType($type) + { + $this->os_type = $type; + return $this; + } + + public function getOSType() + { + return $this->os_type; + } + + public function setOSVersion($version) + { + $this->os_version = $version; + return $this; + } + + public function getOSVersion() + { + return $this->os_version; + } + + public function setPhoneID($id) + { + $this->phone_id = $id; + return $this; + } + + public function getPhoneID() + { + return $this->phone_id; + } + + public function setDevicePushID($id) + { + $this->device_push_id = $id; + return $this; + } + + public function getDevicePushID() + { + return $this->device_push_id; + } + + public function setCustomer(Customer $cust = null) + { + $this->customer = $cust; + return $this; + } + + public function getCustomer() + { + return $this->customer; + } + + public function getDateGenerated() + { + return $this->date_generated; + } + + public function setPhoneNumber($num) + { + $this->phone_number = $num; + return $this; + } + + public function getPhoneNumber() + { + return $this->phone_number; + } + + public function setConfirmCode($code) + { + $this->confirm_code = $code; + return $this; + } + + public function getConfirmCode() + { + return $this->confirm_code; + } + + public function setConfirmed($flag = true) + { + $this->confirm_flag = $flag; + return $this; + } + + public function isConfirmed() + { + return $this->confirm_flag; + } + + public function setDateConfirmed(DateTime $date) + { + $this->date_confirmed = $date; + return $this; + } + + public function getDateConfirmed() + { + return $this->date_confirmed; + } + + public function setDateCodeSent(DateTime $date) + { + $this->date_code_sent = $date; + return $this; + } + + public function getDateCodeSent() + { + return $this->date_code_sent; + } + + public function getReviews() + { + return $this->reviews; + } + + public function setCapiUserId($capi_user_id) + { + $this->capi_user_id = $capi_user_id; + } + + public function getCapiUserId() + { + return $this->capi_user_id; + } + +} diff --git a/src/Entity/Review.php b/src/Entity/Review.php index 9903784e..efb8314a 100644 --- a/src/Entity/Review.php +++ b/src/Entity/Review.php @@ -52,6 +52,13 @@ class Review */ protected $mobile_session; + // mobile user that sent review + /** + * @ORM\ManyToOne(targetEntity="MobileUser", inversedBy="reviews") + * @ORM\JoinColumn(name="mobile_user_id", referencedColumnName="id") + */ + protected $mobile_user; + public function __construct() { $this->date_create = new DateTime(); @@ -114,4 +121,15 @@ class Review return $this->mobile_session; } + public function setMobileUser(MobileUser $mobile_user) + { + $this->mobile_user = $mobile_user; + return $this; + } + + public function getMobileUser() + { + return $this->mobile_user; + } + } diff --git a/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php b/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php index 47d8728e..d3e3e4cc 100644 --- a/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php +++ b/src/Service/InvoiceGenerator/ResqInvoiceGenerator.php @@ -123,7 +123,9 @@ class ResqInvoiceGenerator implements InvoiceGeneratorInterface // get current user $user = $this->security->getUser(); - if ($user != null) + // check if user is User or APIUser + //if ($user != null) + if ($user instanceof User) { $invoice->setCreatedBy($user); } diff --git a/src/Service/JobOrderHandler/ResqJobOrderHandler.php b/src/Service/JobOrderHandler/ResqJobOrderHandler.php index 971f6903..bc18a81c 100644 --- a/src/Service/JobOrderHandler/ResqJobOrderHandler.php +++ b/src/Service/JobOrderHandler/ResqJobOrderHandler.php @@ -56,6 +56,7 @@ use App\Service\PromoLogger; use App\Service\HubSelector; use App\Service\HubDistributor; use App\Service\HubFilteringGeoChecker; +use App\Service\JobOrderManager; use CrEOF\Spatial\PHP\Types\Geometry\Point; @@ -80,6 +81,8 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface protected $promo_logger; protected $hub_dist; protected $hub_geofence; + protected $mclient; + protected $jo_manager; protected $template_hash; @@ -87,7 +90,8 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface InvoiceGeneratorInterface $ic, ValidatorInterface $validator, TranslatorInterface $translator, RiderAssignmentHandlerInterface $rah, string $country_code, WarrantyHandler $wh, RisingTideGateway $rt, - PromoLogger $promo_logger, HubDistributor $hub_dist, HubFilteringGeoChecker $hub_geofence) + PromoLogger $promo_logger, HubDistributor $hub_dist, HubFilteringGeoChecker $hub_geofence, + MQTTClient $mclient, JobOrderManager $jo_manager) { $this->em = $em; $this->ic = $ic; @@ -101,6 +105,8 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $this->promo_logger = $promo_logger; $this->hub_dist = $hub_dist; $this->hub_geofence = $hub_geofence; + $this->mclient = $mclient; + $this->jo_manager = $jo_manager; $this->loadTemplates(); } @@ -342,106 +348,15 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ->setDpaConsent($is_dpa_checked); } - // 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.'; - } - // check if customer vehicle is set - if (empty($req->request->get('customer_vehicle'))) { - $error_array['customer_vehicle'] = 'No vehicle selected.'; - } else { - // get customer vehicle - $cust_vehicle = $em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle')); - - if (empty($cust_vehicle)) { - $error_array['customer_vehicle'] = 'Invalid vehicle specified.'; - } - } - - // check if landmark is set - if (empty($req->request->get('landmark'))) - $error_array['landmark'] = 'Landmark is required.'; - - // check if customer is not willing to wait - $will_wait = $req->request->get('flag_willing_to_wait'); - $reason = ''; - $more_reason = ''; - if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT) + // validate and set job order object + $jo_status = JOStatus::PENDING; + $this->validateJobOrder($req, $error_array, $jo_status); + if (empty($error_array)) { - // get the reason and text - $reason = $req->request->get('no_wait_reason'); - $more_reason = $req->request->get('not_wait_notes'); - } + $this->setJobOrderObject($req, $jo, $jo_status); - // check if service is battery sales - $stype = $req->request->get('service_type'); - $no_trade_in_reason = ''; - if ($stype == ServiceType::BATTERY_REPLACEMENT_NEW) - { - // check if trade in - $is_trade_in = $req->request->get('invoice_trade_in_type'); - if (empty($is_trade_in)) - { - $no_trade_in_reason = $req->request->get('no_trade_in_reason'); - - if (empty($no_trade_in_reason)) - $error_array['no_trade_in_reason'] = 'No trade in reason required.'; - } - } - - // TODO: check status before saving since JO might already - // have a status that needs to be retained - - 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')); - - // set and save values - $jo->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')) - ->setCustomer($cust_vehicle->getCustomer()) - ->setCustomerVehicle($cust_vehicle) - ->setSource($req->request->get('source')) - ->setStatus(JOStatus::PENDING) - ->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')) - ->setWillWait($req->request->get('flag_willing_to_wait')) - ->setReasonNotWait($reason) - ->setNotWaitingNotes($more_reason) - ->setNoTradeInReason($no_trade_in_reason); - - // check if user is null, meaning call to create came from API - if ($user != null) - { - $jo->setCreatedBy($user); - } - - // check if reference JO is set and validate - if (!empty($req->request->get('ref_jo'))) { - // get reference JO - $ref_jo = $em->getRepository(JobOrder::class)->find($req->request->get('ref_jo')); - - if (empty($ref_jo)) { - $error_array['ref_jo'] = 'Invalid reference job order specified.'; - } else { - $jo->setReferenceJO($ref_jo); - } - } - - // call service to generate job order and invoice + // call service to generate invoice $invoice_items = $req->request->get('invoice_items', []); $promo_id = $req->request->get('invoice_promo'); $invoice_change = $req->request->get('invoice_change', 0); @@ -460,27 +375,21 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $error_array[$error->getPropertyPath()] = $error->getMessage(); } - // check if errors are found - if (empty($error_array)) - { - // validated, no error. save the job order and customer - $em->persist($jo); - $em->persist($customer); - - // the event - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::CREATE) - ->setJobOrder($jo); - - if ($user != null) - { - $event->setUser($user); - } - - $em->persist($event); - $em->flush(); + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); } + + $em->persist($jo); + $em->persist($customer); + + $this->jo_manager->processJobOrderEvents($jo, JOEventType::CREATE); + + $em->flush(); } $data['error_array'] = $error_array; @@ -495,30 +404,12 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // check service type if ($jo->getServiceType() == ServiceType::BATTERY_REPLACEMENT_NEW) { - $customer = $cust_vehicle->getCustomer(); + // process customer tags $customer_tags = $customer->getCustomerTagObjects(); if (!empty($customer_tags)) { - foreach ($customer_tags as $customer_tag) - { - if ($customer_tag->getID() == $jo->getInvoice()->getUsedCustomerTagId()) - { - // remove associated entity - $customer->removeCustomerTag($customer_tag); - - // log the availment of promo from customer - $created_by = $jo->getCreatedBy()->getUsername(); - $cust_id = $jo->getCustomer()->getID(); - $cust_fname = $jo->getCustomer()->getFirstName(); - $cust_lname = $jo->getCustomer()->getLastName(); - $jo_id = $jo->getID(); - $invoice_id = $jo->getInvoice()->getID(); - // TODO: check if we store total price of invoice or just the discounted amount - $amount = $jo->getInvoice()->getTotalPrice(); - $this->promo_logger->logPromoInfo($created_by, $cust_id, $cust_fname, $cust_lname, $jo_id, - $invoice_id, $amount); - } - } + $username = $jo->getCreatedBy()->getUsername(); + $this->jo_manager->removeCustomerTag($jo, $customer, $customer_tags, $username); } } } @@ -541,69 +432,11 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface 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.'; - } - - // check if landmark is set - if (empty($req->request->get('landmark'))) - $error_array['landmark'] = 'Landmark is required.'; - - // check if customer is not willing to wait - $will_wait = $req->request->get('flag_willing_to_wait'); - $reason = ''; - $more_reason = ''; - if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT) + // validate and set job order object + $this->validateJobOrder($req, $error_array, $obj->getStatus()); + if (empty($error_array)) { - // get the reason and text - $reason = $req->request->get('no_wait_reason'); - $more_reason = $req->request->get('not_wait_notes'); - } - - // check if service type is battery sales - $stype = $req->request->get('service_type'); - $no_trade_in_reason = ''; - if ($stype == ServiceType::BATTERY_REPLACEMENT_NEW) - { - // check if trade in - $is_trade_in = $req->request->get('invoice_trade_in_type'); - if (empty($is_trade_in)) - { - $no_trade_in_reason = $req->request->get('no_trade_in_reason'); - - if (empty($no_trade_in_reason)) - $error_array['no_trade_in_reason'] = 'No trade in reason required.'; - } - } - - 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')); - - // 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')) - ->setWillWait($req->request->get('flag_willing_to_wait')) - ->setReasonNotWait($reason) - ->setNotWaitingNotes($more_reason) - ->setNoTradeInReason($no_trade_in_reason); + $this->setJobOrderObject($req, $obj, $obj->getStatus()); // did they change invoice? $invoice_items = $req->request->get('invoice_items', []); @@ -631,20 +464,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface ], 422); } - // the event - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::OPEN_EDIT) - ->setJobOrder($obj); - - error_log('open edit?'); - - if ($user != null) - { - $event->setUser($user); - } - - $em->persist($event); + $this->jo_manager->processJobOrderEvents($obj, JOEventType::OPEN_EDIT); // validated! save the entity $em->flush(); @@ -658,7 +478,6 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } return $data; - } // dispatch job order @@ -668,7 +487,7 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $em = $this->em; $obj = $em->getRepository(JobOrder::class)->find($id); $processor = $obj->getProcessedBy(); - $user = $this->security->getUser();; + $user = $this->security->getUser(); // check if we're the one processing, return error otherwise if ($processor == null) @@ -692,87 +511,24 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface // $error_array['dispatch'] = 'Could not dispatch. Job Order is not pending.'; } - // 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.'; - } + // validate + $jo_status = JOStatus::RIDER_ASSIGN; + $this->validateJobOrder($req, $error_array, $jo_status); // check if hub is set if (empty($req->request->get('hub'))) - { $error_array['hub'] = 'No hub selected.'; - } else { // get hub $hub = $em->getRepository(Hub::class)->find($req->request->get('hub')); - if (empty($hub)) - { $error_array['hub'] = 'Invalid hub specified.'; - } - } - - // check facilitated type - $fac_type = $req->request->get('facilitated_type'); - if (!empty($fac_type)) - { - if (!FacilitatedType::validate($fac_type)) - $fac_type = null; - } - else - $fac_type = null; - - // check facilitated by - $fac_by_id = $req->request->get('facilitated_by'); - $fac_by = null; - if (!empty($fac_by_id)) - { - $fac_by = $em->getRepository(Hub::class)->find($fac_by_id); - if (empty($fac_by)) - $fac_by = null; - } - - // check if landmark is set - if (empty($req->request->get('landmark'))) - $error_array['landmark'] = 'Landmark is required.'; - - // check if customer is not willing to wait - $will_wait = $req->request->get('flag_willing_to_wait'); - $reason = ''; - $more_reason = ''; - if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT) - { - // get the reason and text - $reason = $req->request->get('no_wait_reason'); - $more_reason = $req->request->get('not_wait_notes'); } if (empty($error_array)) { - // coordinates - $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); - - // 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($req->request->get('service_type')) - ->setWarrantyClass($req->request->get('warranty_class')) - ->setSource($req->request->get('source')) - ->setStatus(JOStatus::RIDER_ASSIGN) - ->setDeliveryInstructions($req->request->get('delivery_instructions')) - ->setTier1Notes($req->request->get('tier1_notes')) - ->setTier2Notes($req->request->get('tier2_notes')) - ->setDeliveryAddress($req->request->get('delivery_address')) - ->setFacilitatedType($fac_type) - ->setFacilitatedBy($fac_by) - ->setHub($hub) - ->setLandmark($req->request->get('landmark')) - ->setWillWait($req->request->get('flag_willing_to_wait')) - ->setReasonNotWait($reason) - ->setNotWaitingNotes($more_reason); + $this->setJobOrderObject($req, $obj, $jo_status); // validate $errors = $this->validator->validate($obj); @@ -781,22 +537,17 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface foreach ($errors as $error) { $error_array[$error->getPropertyPath()] = $error->getMessage(); } - } - if (empty($error_array)) - { - // the event - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::HUB_ASSIGN) - ->setJobOrder($obj); - - if ($user != null) - { - $event->setUser($user); + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); } - $em->persist($event); + $this->jo_manager->processJobOrderEvents($obj, JOEventType::HUB_ASSIGN); // validated! save the entity $em->flush(); @@ -1517,157 +1268,6 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } - // CMB code - public function processOneStepJobOrder(Request $req, $id) - { - // initialize error list - $error_array = []; - - $em = $this->em; - - $jo = $em->getRepository(JobOrder::class)->find($id); - if (empty($jo)) - { - // new job order - $jo = new JobOrder(); - } - - // 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.'; - } - - // check if customer vehicle is set - if (empty($req->request->get('customer_vehicle'))) { - $error_array['customer_vehicle'] = 'No vehicle selected.'; - } else { - // get customer vehicle - $cust_vehicle = $em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle')); - - if (empty($cust_vehicle)) { - $error_array['customer_vehicle'] = 'Invalid vehicle specified.'; - } - } - - // check if hub AND rider is selected - if ((empty($req->request->get('hub_id'))) && - (empty($req->request->get('rider_id')))) { - $error_array['hub'] = 'No hub selected.'; - } else { - if (empty($req->request->get('rider_id'))) { - $error_array['rider'] = 'No rider selected.'; - } else { - // get hub - $hub = $em->getRepository(Hub::class)->find($req->request->get('hub_id')); - - if (empty($hub)) { - $error_array['hub'] = 'Invalid hub specified.'; - } else { - // get rider - $rider = $em->getRepository(Rider::class)->find($req->request->get('rider_id')); - - if (empty($rider)) { - $error_array['rider'] = 'Invalid rider specified.'; - } - } - } - } - - 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 - $jo->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')) - ->setCustomer($cust_vehicle->getCustomer()) - ->setCustomerVehicle($cust_vehicle) - ->setSource($req->request->get('source')) - ->setStatus(JOStatus::ASSIGNED) - ->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')) - ->setHub($hub) - ->setRider($rider); - - // check if user is null, meaning call to create came from API - if ($user != null) - { - $jo->setCreatedBy($user); - } - - // check if reference JO is set and validate - if (!empty($req->request->get('ref_jo'))) { - // get reference JO - $ref_jo = $em->getRepository(JobOrder::class)->find($req->request->get('ref_jo')); - - if (empty($ref_jo)) { - $error_array['ref_jo'] = 'Invalid reference job order specified.'; - } else { - $jo->setReferenceJO($ref_jo); - } - } - - // call service to generate job order and invoice - $invoice_items = $req->request->get('invoice_items', []); - $promo_id = $req->request->get('invoice_promo'); - $invoice_change = $req->request->get('invoice_change', 0); - - // check if invoice changed - if ($invoice_change) - { - $this->ic->generateInvoiceCriteria($jo, $promo_id, $invoice_items, $error_array); - } - - // validate - $errors = $this->validator->validate($jo); - - // add errors to list - foreach ($errors as $error) { - $error_array[$error->getPropertyPath()] = $error->getMessage(); - } - - // check if errors are found - if (empty($error_array)) - { - // validated, no error. save the job order - $em->persist($jo); - - // the event - $event = new JOEvent(); - $event->setDateHappen(new DateTime()) - ->setTypeID(JOEventType::CREATE) - ->setJobOrder($jo); - - if ($user != null) - { - $event->setUser($user); - } - - $em->persist($event); - $em->flush(); - } - } - - return $error_array; - - } - - // initialize incoming job order form public function initializeIncomingForm() { @@ -1684,83 +1284,6 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface return $params; } - // CMB code - public function initializeOneStepForm() - { - $params['obj'] = new JobOrder(); - $params['mode'] = 'onestep'; - - $this->fillDropdownParameters($params); - $this->fillFormTags($params); - - // get template to display - $params['template'] = $this->getTwigTemplate('jo_onestep'); - - // return params - return $params; - } - - // CMB code - public function initializeOneStepEditForm($id, $map_tools) - { - $em = $this->em; - $obj = $em->getRepository(JobOrder::class)->find($id); - - $params['obj'] = $obj; - $params['mode'] = 'onestep-edit'; - $params['cvid'] = $obj->getCustomerVehicle()->getID(); - $params['vid'] = $obj->getCustomerVehicle()->getVehicle()->getID(); - - $this->fillDropdownParameters($params); - $this->fillFormTags($params); - - // get the hubs - // TODO: move this snippet to a function - $hubs = $map_tools->getClosestHubs($obj->getCoordinates(), 50, date("H:i:s")); - - $params['hubs'] = []; - - // format duration and distance into friendly time - foreach ($hubs as $hub) { - // duration - $seconds = $hub['duration']; - - if (!empty($seconds) && $seconds > 0) { - $hours = floor($seconds / 3600); - $minutes = ceil(($seconds / 60) % 60); - - $hub['duration'] = ($hours > 0 ? number_format($hours) . " hr" . ($hours > 1 ? "s" : '') . ($minutes > 0 ? ", " : '') : '') . ($minutes > 0 ? number_format($minutes) . " min" . ($minutes > 1 ? "s" : '') : ''); - } else { - $hub['duration'] = false; - } - - // distance - $meters = $hub['distance']; - - if (!empty($meters) && $meters > 0) { - $hub['distance'] = round($meters / 1000) . " km"; - } else { - $hub['distance'] = false; - } - - // counters - $hub['rider_count'] = count($hub['hub']->getAvailableRiders()); - $hub['jo_count'] = count($hub['hub']->getForAssignmentJobOrders()); - - // check for rejection - $hub['flag_rejected'] = false; - $hub_id = $hub['hub']->getID(); - - $params['hubs'][] = $hub; - } - - - // get template to display - $params['template'] = $this->getTwigTemplate('jo_onestep_edit_form'); - - return $params; - } - // initialize open edit job order form public function initializeOpenEditForm($id) { @@ -2916,17 +2439,6 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface $params['ftags']['invoice_edit'] = true; $params['ftags']['preset_vehicle'] = true; break; - case 'onestep': - $params['ftags']['vehicle_dropdown'] = true; - $params['ftags']['set_map_coordinate'] = false; - $params['ftags']['invoice_edit'] = true; - $params['ftags']['ticket_table'] = false; - $params['ftags']['cancel_button'] = false; - break; - case 'onestep-edit': - $params['ftags']['invoice_edit'] = true; - $params['ftags']['preset_vehicle'] = true; - break; } } @@ -3243,6 +2755,177 @@ class ResqJobOrderHandler implements JobOrderHandlerInterface } } + protected function validateJobOrder(Request $req, &$error_array, $jo_status) + { + // new job order + if ($jo_status == JOStatus::PENDING) + { + // check if service is battery sales + $stype = $req->request->get('service_type'); + if ($stype == ServiceType::BATTERY_REPLACEMENT_NEW) + { + // check if trade in + $is_trade_in = $req->request->get('invoice_trade_in_type'); + if (empty($is_trade_in)) + { + if (empty($req->request->get('no_trade_in_reason'))) + $error_array['no_trade_in_reason'] = 'No trade in reason required.'; + } + } + // check if customer vehicle is set + if (empty($req->request->get('customer_vehicle'))) + $error_array['customer_vehicle'] = 'No vehicle selected.'; + else + { + // get customer vehicle + $cust_vehicle = $this->em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle')); + + if (empty($cust_vehicle)) + $error_array['customer_vehicle'] = 'Invalid vehicle specified.'; + } + // check if reference JO is set and validate + if (!empty($req->request->get('ref_jo'))) + { + // get reference JO + $ref_jo = $em->getRepository(JobOrder::class)->find($req->request->get('ref_jo')); + + if (empty($ref_jo)) + $error_array['ref_jo'] = 'Invalid reference job order specified.'; + } + } + + // assign hub/dispatch job order + if ($jo_status == JOStatus::RIDER_ASSIGN) + { + // check if hub is set + if (empty($req->request->get('hub'))) + $error_array['hub'] = 'No hub selected.'; + else + { + // get hub + $hub = $this->em->getRepository(Hub::class)->find($req->request->get('hub')); + + if (empty($hub)) + $error_array['hub'] = 'Invalid hub specified.'; + } + } + + // 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.'; + + // check if landmark is set + if (empty($req->request->get('landmark'))) + $error_array['landmark'] = 'Landmark is required.'; + + } + + protected function setJobOrderObject(Request $req, JobOrder $jo, $jo_status) + { + // new job order + if ($jo_status == JOStatus::PENDING) + { + // these are fields that are only set when new job order + // get customer vehicle + $cust_vehicle = $this->em->getRepository(CustomerVehicle::class)->find($req->request->get('customer_vehicle')); + + $jo->setCustomer($cust_vehicle->getCustomer()) + ->setCustomerVehicle($cust_vehicle) + ->setORName($req->request->get('or_name')) + ->setPromoDetail($req->request->get('promo_detail')); + + // get current user + $user = $this->security->getUser(); + if ($user != null) + $jo->setCreatedBy($user); + + // check if reference JO is set + if (!empty($req->request->get('ref_jo'))) { + // get reference JO + $ref_jo = $this->em->getRepository(JobOrder::class)->find($req->request->get('ref_jo')); + $jo->setReferenceJO($ref_jo); + } + } + + // dispatch/assign hub + if ($jo_status == JOStatus::RIDER_ASSIGN) + { + // this means JO is in dispatch/assign hub + // for dispatch, set the hub and facilitated by and facilitated type + // check facilitated type + $fac_type = $req->request->get('facilitated_type'); + if (!empty($fac_type)) + { + if (!FacilitatedType::validate($fac_type)) + $fac_type = null; + } + else + $fac_type = null; + + // check facilitated by + $fac_by_id = $req->request->get('facilitated_by'); + $fac_by = null; + if (!empty($fac_by_id)) + { + $fac_by = $this->em->getRepository(Hub::class)->find($fac_by_id); + if (empty($fac_by)) + $fac_by = null; + } + + // get hub + $hub = $this->em->getRepository(Hub::class)->find($req->request->get('hub')); + + $jo->setFacilitatedType($fac_type) + ->setFacilitatedBy($fac_by) + ->setHub($hub); + } + + // check if customer is not willing to wait + $will_wait = $req->request->get('flag_willing_to_wait'); + $reason = ''; + $more_reason = ''; + if ($will_wait == WillingToWaitContent::NOT_WILLING_TO_WAIT) + { + // get the reason and text + $reason = $req->request->get('no_wait_reason'); + $more_reason = $req->request->get('not_wait_notes'); + } + + // check if service is battery sales + $stype = $req->request->get('service_type'); + $no_trade_in_reason = ''; + if ($stype == ServiceType::BATTERY_REPLACEMENT_NEW) + { + // check if trade in + $is_trade_in = $req->request->get('invoice_trade_in_type'); + if (empty($is_trade_in)) + $no_trade_in_reason = $req->request->get('no_trade_in_reason'); + } + + // coordinates + $point = new Point($req->request->get('coord_lng'), $req->request->get('coord_lat')); + + // set and save values + $jo->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')) + ->setModeOfPayment($req->request->get('mode_of_payment')) + ->setLandmark($req->request->get('landmark')) + ->setWillWait($req->request->get('flag_willing_to_wait')) + ->setReasonNotWait($reason) + ->setNotWaitingNotes($more_reason) + ->setNoTradeInReason($no_trade_in_reason) + ->setStatus($jo_status); + + } + public function getEditRoute($jo_id, $tier) { if (empty($tier)) diff --git a/src/Service/JobOrderHandlerInterface.php b/src/Service/JobOrderHandlerInterface.php index cff06242..77aa7cf0 100644 --- a/src/Service/JobOrderHandlerInterface.php +++ b/src/Service/JobOrderHandlerInterface.php @@ -25,9 +25,6 @@ interface JobOrderHandlerInterface // generate job order public function generateJobOrder(Request $req, int $id); - // process one step job order - public function processOneStepJobOrder(Request $req, int $id); - // dispatch job order public function dispatchJobOrder(Request $req, int $id, MQTTClient $mclient); @@ -82,12 +79,6 @@ interface JobOrderHandlerInterface // initialize rider form public function initializeRiderForm(int $id); - // initialize one step form - public function initializeOneStepForm(); - - // initialize one step edit form - public function initializeOneStepEditForm(int $id, MapTools $map_tools); - // generate pdf form for job order public function generatePDFForm(Request $req, int $id, string $proj_path); diff --git a/src/Service/JobOrderManager.php b/src/Service/JobOrderManager.php index c54557d6..a58a5581 100644 --- a/src/Service/JobOrderManager.php +++ b/src/Service/JobOrderManager.php @@ -2,19 +2,38 @@ namespace App\Service; +use Doctrine\ORM\EntityManagerInterface; + +use Symfony\Component\Security\Core\Security; + use App\Entity\JobOrder; +use App\Entity\JOEvent; +use App\Entity\User; +use App\Entity\Customer; use App\Ramcar\ServiceType; +use App\Ramcar\JOStatus; +use App\Ramcar\JOEventType; -use Doctrine\ORM\EntityManagerInterface; +use DateTime; class JobOrderManager { protected $em; + protected $security; + protected $rah; + protected $mclient; + protected $promo_logger; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, Security $security, + RiderAssignmentHandlerInterface $rah, MQTTClient $mclient, + PromoLogger $promo_logger) { $this->em = $em; + $this->security = $security; + $this->rah = $rah; + $this->mclient = $mclient; + $this->promo_logger = $promo_logger; } public function fulfillJobOrder($jo_id) @@ -51,4 +70,98 @@ class JobOrderManager $this->em->flush(); } } + + public function processJobOrderEvents(JobOrder $jo, $jo_event_type) + { + $em = $this->em; + + // check for the jo event type + if ($jo_event_type == JOEventType::CREATE) + { + // add event log for JO + $event = new JOEvent(); + $event->setDateHappen(new DateTime()) + ->setTypeID($jo_event_type) + ->setJobOrder($jo); + + $user = $this->security->getUser(); + // check if user is User or APIUser + if ($user instanceof User) + $event->setUser($user); + + $em->persist($event); + + // check for JO status for additional events + // (1) when mobile app gets a new JO, finds a hub and assigns a rider + 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); + } + // (2) when mobile app gets a new JO, finds a hub but no rider + if ($jo->getStatus() == JOStatus::RIDER_ASSIGN) + { + // add event logs for hub assignments + $hub_assign_event = new JOEvent(); + $hub_assign_event->setDateHappen(new DateTime()) + ->setTypeID(JOEventType::HUB_ASSIGN) + ->setJobOrder($jo); + + $em->persist($hub_assign_event); + } + } + else + { + // TODO: check for other JO event types. See if all other event types have no special processing + // cancel, edit, dispatch are the same + // add event log for JO + $event = new JOEvent(); + $event->setDateHappen(new DateTime()) + ->setTypeID($jo_event_type) + ->setJobOrder($jo); + + $user = $this->security->getUser(); + // check if user is User or APIUser + if ($user instanceof User) + $event->setUser($user); + + $em->persist($event); + } + } + + public function removeCustomerTag(JobOrder $jo, Customer $customer, $customer_tags, $username) + { + foreach ($customer_tags as $customer_tag) + { + if ($customer_tag->getID() == $jo->getInvoice()->getUsedCustomerTagId()) + { + // remove associated entity + $customer->removeCustomerTag($customer_tag); + + // log the availment of promo from customer + $created_by = $username; + $cust_id = $jo->getCustomer()->getID(); + $cust_fname = $jo->getCustomer()->getFirstName(); + $cust_lname = $jo->getCustomer()->getLastName(); + $jo_id = $jo->getID(); + $invoice_id = $jo->getInvoice()->getID(); + // TODO: check if we store total price of invoice or just the discounted amount + $amount = $jo->getInvoice()->getTotalPrice(); + $this->promo_logger->logPromoInfo($created_by, $cust_id, $cust_fname, $cust_lname, $jo_id, + $invoice_id, $amount); + } + } + } } diff --git a/src/Service/MobileAPIHandler.php b/src/Service/MobileAPIHandler.php new file mode 100644 index 00000000..124e4bfc --- /dev/null +++ b/src/Service/MobileAPIHandler.php @@ -0,0 +1,110 @@ +em = $em; + } + + public function findMobileUser($user_id) + { + // get capi user to link to mobile user + $mobile_user = $this->em->getRepository(MobileUser::class)->findOneBy(['capi_user_id' => $user_id]); + + return $mobile_user; + } + + public function findWarranty($plate_number) + { + // NOTE: Modify the search for the latest warranty. This seems hacky. + // get latest warranty using plate number + $warranty_results = $this->em->getRepository(Warranty::class)->findBy(['plate_number' => $plate_number], + ['date_create' => 'desc']); + + $warr = []; + + // check if warranty_results is empty + if (empty($warranty_results)) + return $warr; + + // get first entry + $warranty = current($warranty_results); + + // check for null values for battery and date claim and date expire + $batt_model = ''; + $batt_size = ''; + $sap_batt = ''; + $claim_date = ''; + $expiry_date = ''; + + if (!(is_null($warranty->getBatteryModel()))) { + $batt_model = $warranty->getBatteryModel()->getName(); + } + if (!(is_null($warranty->getBatterySize()))) { + $batt_size = $warranty->getBatterySize()->getName(); + } + if (!(is_null($warranty->getSAPBattery()))) { + $sap_batt = $warranty->getSAPBattery()->getID(); + } + if (!(is_null($warranty->getDateClaim()))) { + $claim_date = $warranty->getDateClaim()->format("d M Y"); + } + if (!(is_null($warranty->getDateExpire()))) { + $expiry_date = $warranty->getDateExpire()->format("d M Y"); + } + + $warr[] = [ + 'id' => $warranty->getID(), + 'serial' => $warranty->getSerial(), + 'warranty_class' => $warranty->getWarrantyClass(), + 'plate_number' => $warranty->getPlateNumber(), + 'first_name' => $warranty->getFirstName(), + 'last_name' => $warranty->getLastName(), + 'mobile_number' => $warranty->getMobileNumber(), + 'battery_model' => $batt_model, + 'battery_size' => $batt_size, + 'sap_battery' => $sap_batt, + 'status' => $warranty->getStatus(), + 'date_create' => $warranty->getDateCreate()->format("d M Y g:i A"), + 'date_purchase' => $warranty->getDatePurchase()->format("d M Y"), + 'date_expire' => $expiry_date, + 'date_claim' => $claim_date, + 'claim_from' => $warranty->getClaimedFrom(), + 'is_activated' => $warranty->isActivated() ? 1 : 0, + ]; + + return $warr; + } + + public function getBatteryImageURL($req, $batt, $battery_image_url) + { + // TODO: workaround for now, we get static image of battery based on model name + $filename = trim(strtolower($batt->getModel()->getName())) . '_mobile.jpg'; + $file_path = $req->getSchemeAndHttpHost() . $battery_image_url . '/' . $filename; + + return $file_path; + } + + public function getOngoingJobOrders($cust) + { + $ongoing_jos = $this->em->getRepository(JobOrder::class)->findBy([ + 'customer' => $cust, + 'status' => [JOStatus::PENDING, JOStatus::RIDER_ASSIGN, JOStatus::IN_TRANSIT, JOStatus::ASSIGNED, JOStatus::IN_PROGRESS], + ]); + + return $ongoing_jos; + } +}