diff --git a/.env.dist b/.env.dist
index bd82f980..11794311 100644
--- a/.env.dist
+++ b/.env.dist
@@ -74,3 +74,6 @@ MAPTILER_API_KEY=map_tiler_api_key
# API version
API_VERSION=insert_api_version_here
+
+# SSL
+SSL_ENABLE=true_or_false
diff --git a/composer.lock b/composer.lock
index 1e76e914..3d02c0bb 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,7 +1,7 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f03b92d48946e8b2ee19466f931c826f",
@@ -3214,20 +3214,20 @@
},
{
"name": "symfony/filesystem",
- "version": "v4.4.4",
+ "version": "v4.4.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd"
+ "reference": "b27f491309db5757816db672b256ea2e03677d30"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/266c9540b475f26122b61ef8b23dd9198f5d1cfd",
- "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/b27f491309db5757816db672b256ea2e03677d30",
+ "reference": "b27f491309db5757816db672b256ea2e03677d30",
"shasum": ""
},
"require": {
- "php": "^7.1.3",
+ "php": ">=7.1.3",
"symfony/polyfill-ctype": "~1.8"
},
"type": "library",
@@ -3260,7 +3260,7 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
- "time": "2020-01-21T08:20:44+00:00"
+ "time": "2020-05-30T18:50:54+00:00"
},
{
"name": "symfony/finder",
diff --git a/config/acl.yaml b/config/acl.yaml
index abd73118..1d1a0c74 100644
--- a/config/acl.yaml
+++ b/config/acl.yaml
@@ -270,6 +270,8 @@ access_keys:
label: Autoassign Test
- id: jo_hub.list
label: Hub View
+ - id: jo_behind_schedule.list
+ label: View Behind Schedule
- id: support
label: Customer Support Access
diff --git a/config/cmb.menu.yaml b/config/cmb.menu.yaml
index 9f28b315..ca756046 100644
--- a/config/cmb.menu.yaml
+++ b/config/cmb.menu.yaml
@@ -118,6 +118,10 @@ main_menu:
acl: jo_all.list
label: View All
parent: joborder
+ - id: jo_behind_schedule
+ acl: jo_behind_schedule.list
+ label: View Behind Schedule
+ parent: joborder
- id: support
acl: support.menu
diff --git a/config/cmb.services.yaml b/config/cmb.services.yaml
index 5f24e525..3d9fd7a3 100644
--- a/config/cmb.services.yaml
+++ b/config/cmb.services.yaml
@@ -5,6 +5,7 @@ parameters:
latitude: 14.6091
longitude: 121.0223
image_upload_directory: '%kernel.project_dir%/public/uploads'
+ jo_extra_upload_directory: '%kernel.project_dir%/public/uploads/jo_extra'
job_order_refresh_interval: 300000
api_acl_file: 'api_acl.yaml'
api_access_key: 'api_access_keys'
@@ -12,6 +13,7 @@ parameters:
app_access_key: 'access_keys'
cvu_brand_id: "%env(CVU_BRAND_ID)%"
country_code: "%env(COUNTRY_CODE)%"
+ api_version: "%env(API_VERSION)%"
services:
# default configuration for services in *this* file
@@ -73,6 +75,7 @@ services:
$pass: "%env(RT_PASS)%"
$usage_type: "%env(RT_USAGE_TYPE)%"
$shortcode: "%env(RT_SHORTCODE)%"
+ $dr_url: "https://resqaws.jankstudio.com/sms/delivery_receipt"
App\Service\MQTTClient:
arguments:
@@ -167,7 +170,7 @@ services:
# job order generator
App\Service\JobOrderHandler\CMBJobOrderHandler:
arguments:
- $country_code: "%env(COUNTRY_CODE)%"
+ $country_code: "%env(COUNTRY_CODE)%"
#job order generator interface
App\Service\JobOrderHandlerInterface: "@App\\Service\\JobOrderHandler\\CMBJobOrderHandler"
@@ -187,13 +190,13 @@ services:
App\Service\RiderAssignmentHandlerInterface: "@App\\Service\\RiderAssignmentHandler\\CMBRiderAssignmentHandler"
# rider API service
- App\Service\RiderAPIHandler\CMBRiderAPIHandler:
+ App\Service\RiderAPIHandler\CMBRiderAPIHandler:
arguments:
$country_code: "%env(COUNTRY_CODE)%"
+ $upload_dir: "%jo_extra_upload_directory%"
- # rider API interface
App\Service\RiderAPIHandlerInterface: "@App\\Service\\RiderAPIHandler\\CMBRiderAPIHandler"
-
+
# map manager
#App\Service\GISManager\Bing: ~
App\Service\GISManager\OpenStreet: ~
@@ -229,6 +232,14 @@ services:
$loc_key: "%env(LOCATION_RIDER_ACTIVE_KEY)%"
$status_key: "%env(STATUS_RIDER_KEY)%"
+ # inventory manager
+ App\Service\InventoryManager:
+ arguments:
+ $api_url: "%env(INVENTORY_API_URL)%"
+ $api_ocp_key: "%env(INVENTORY_API_OCP)%"
+ $api_auth_prefix: "%env(INVENTORY_API_AUTH_TOKEN_PREFIX)%"
+ $api_auth_token: "%env(INVENTORY_API_AUTH_TOKEN)%"
+
# API logging
App\EventSubscriber\LogSubscriber:
arguments:
diff --git a/config/menu.yaml b/config/menu.yaml
index 246eda01..ca756046 100644
--- a/config/menu.yaml
+++ b/config/menu.yaml
@@ -37,6 +37,10 @@ main_menu:
acl: rider.list
label: Riders
parent: logistics
+ - id: service_charge_list
+ acl: service_charge.list
+ label: Service Charges
+ parent: logistics
- id: battery
acl: battery.menu
@@ -98,21 +102,13 @@ main_menu:
acl: joborder.menu
label: Job Order
icon: flaticon-calendar-3
- - id: jo_in
- acl: jo_in.list
- label: Incoming
+ - id: jo_onestep_form
+ acl: jo_onestep.form
+ label: One-step Process
parent: joborder
- - id: jo_proc
- acl: jo_proc.list
- label: Dispatch
- parent: joborder
- - id: jo_assign
- acl: jo_assign.list
- label: Rider Assignment
- parent: joborder
- - id: jo_fulfill
- acl: jo_fulfill.list
- label: Fulfillment
+ - id: jo_walkin_form
+ acl: jo_walkin.form
+ label: Walk-in
parent: joborder
- id: jo_open
acl: jo_open.list
@@ -122,9 +118,9 @@ main_menu:
acl: jo_all.list
label: View All
parent: joborder
- - id: jo_hub_view
- acl: jo_hub.list
- label: Hub View
+ - id: jo_behind_schedule
+ acl: jo_behind_schedule.list
+ label: View Behind Schedule
parent: joborder
- id: support
@@ -185,12 +181,3 @@ main_menu:
acl: review.list
label: Reviews
parent: partner
-
- - id: analytics
- acl: analytics.menu
- label: Analytics
- icon: flaticon-graphic
- - id: analytics_forecast_form
- acl: analytics.forecast
- label: Forecasting
- parent: analytics
diff --git a/config/packages/security.yaml b/config/packages/security.yaml
index 4339eba4..86a9b1d7 100644
--- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -46,6 +46,10 @@ security:
provider: api_key_user_provider
user_checker: Catalyst\AuthBundle\Service\UserChecker
+ cmb_rider_api:
+ pattern: ^\/cmbrapi\/
+ security: false
+
main:
provider: user_provider
form_login:
diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml
index 1c848030..ade96bcc 100644
--- a/config/packages/twig.yaml
+++ b/config/packages/twig.yaml
@@ -8,3 +8,4 @@ twig:
mqtt_host: "%env(MQTT_WS_HOST)%"
mqtt_port: "%env(MQTT_WS_PORT)%"
dashboard_enable: "%env(DASHBOARD_ENABLE)%"
+ ssl_enable: "%env(SSL_ENABLE)%"
diff --git a/config/resq.services.yaml b/config/resq.services.yaml
index 82eb4cc5..05fd3b1a 100644
--- a/config/resq.services.yaml
+++ b/config/resq.services.yaml
@@ -74,6 +74,7 @@ services:
$pass: "%env(RT_PASS)%"
$usage_type: "%env(RT_USAGE_TYPE)%"
$shortcode: "%env(RT_SHORTCODE)%"
+ $dr_url: "https://resqaws.jankstudio.com/sms/delivery_receipt"
App\Service\MQTTClient:
arguments:
diff --git a/config/routes/cmb_rider_api.yaml b/config/routes/cmb_rider_api.yaml
new file mode 100644
index 00000000..805d898d
--- /dev/null
+++ b/config/routes/cmb_rider_api.yaml
@@ -0,0 +1,156 @@
+# rider app api
+
+cmb_rapi_register:
+ path: /cmbrapi/register
+ controller: App\Controller\CMBRAPIController::register
+ methods: [POST]
+
+cmb_rapi_login:
+ path: /cmbrapi/login
+ controller: App\Controller\CMBRAPIController::login
+ methods: [POST]
+
+cmb_rapi_logout:
+ path: /cmbrapi/logout
+ controller: App\Controller\CMBRAPIController::logout
+ methods: [POST]
+
+cmb_rapi_jo_get:
+ path: /cmbrapi/joborder
+ controller: App\Controller\CMBRAPIController::getJobOrder
+ methods: [GET]
+
+cmb_rapi_jo_accept:
+ path: /cmbrapi/joaccept
+ controller: App\Controller\CMBRAPIController::acceptJobOrder
+ methods: [POST]
+
+cmb_rapi_jo_cancel:
+ path: /cmbrapi/jocancel
+ controller: App\Controller\CMBRAPIController::cancelJobOrder
+ methods: [POST]
+
+cmb_rapi_arrive:
+ path: /cmbrapi/arrive
+ controller: App\Controller\CMBRAPIController::arrive
+ methods: [POST]
+
+cmb_rapi_performed:
+ path: /cmbrapi/joperform
+ controller: App\Controller\CMBRAPIController::performJobOrder
+ methods: [POST]
+
+cmb_rapi_payment:
+ path: /cmbrapi/jopayment
+ controller: App\Controller\CMBRAPIController::payment
+ methods: [POST]
+
+cmb_rapi_hub_arrive:
+ path: /cmbrapi/hub_arrive
+ controller: App\Controller\CMBRAPIController::hubArrive
+ methods: [POST]
+
+cmb_rapi_promos:
+ path: /cmbrapi/promos
+ controller: App\Controller\CMBRAPIController::getPromos
+ methods: [GET]
+
+cmb_rapi_batteries:
+ path: /cmbrapi/batteries
+ controller: App\Controller\CMBRAPIController::getBatteries
+ methods: [GET]
+
+cmb_rapi_change_service:
+ path: /cmbrapi/service
+ controller: App\Controller\CMBRAPIController::changeService
+ methods: [POST]
+
+cmb_rapi_available:
+ path: /cmbrapi/available
+ controller: App\Controller\CMBRAPIController::available
+ methods: [POST]
+
+cmb_rapi_jo_history:
+ path: /cmbrapi/johistory/{period}
+ controller: App\Controller\CMBRAPIController::getJobOrderHistory
+ methods: [GET]
+
+cmb_rapi_assigned_jo_get:
+ path: /cmbrapi/assignedjos
+ controller: App\Controller\CMBRAPIController::getAssignedJobOrders
+ methods: [GET]
+
+cmb_rapi_jo_in_transit:
+ path: /cmbrapi/jotransit
+ controller: App\Controller\CMBRAPIController::setJobOrderInTransit
+ methods: [POST]
+
+cmb_rapi_invoice_generate:
+ path: /cmbrapi/generateinvoice
+ controller: App\Controller\CMBRAPIController::generateInvoice
+ methods: [GET]
+
+cmb_rapi_online:
+ path: /cmbrapi/online
+ controller: App\Controller\CMBRAPIController::goOnline
+ methods: [POST]
+
+cmb_rapi_offline:
+ path: /cmbrapi/offline
+ controller: App\Controller\CMBRAPIController::goOffline
+ methods: [POST]
+
+cmb_rapi_jo_start:
+ path: /cmbrapi/jostart
+ controller: App\Controller\CMBRAPIController::startJobOrder
+ methods: [POST]
+
+cmb_rapi_jo_complete:
+ path: /cmbrapi/jocomplete
+ controller: App\Controller\CMBRAPIController::completeJobOrder
+ methods: [POST]
+
+cmb_rapi_jo_set_active:
+ path: /cmbrapi/joactive
+ controller: App\Controller\CMBRAPIController::setActiveJobOrder
+ methods: [POST]
+
+cmb_rapi_jo_reject:
+ path: /cmbrapi/joreject
+ controller: App\Controller\CMBRAPIController::rejectJobOrder
+ methods: [POST]
+
+cmb_rapi_jo_odometer:
+ path: /cmbrapi/odometer
+ controller: App\Controller\CMBRAPIController::setOdometer
+ methods: [POST]
+
+cmb_rapi_jo_finish_photos_upload:
+ path: /cmbrapi/uploadfinishphotos
+ controller: App\Controller\CMBRAPIController::uploadFinishPhotos
+ methods: [POST]
+
+cmb_rapi_status:
+ path: /cmbrapi/status
+ controller: App\Controller\CMBRAPIController::getStatus
+ methods: [GET]
+
+cmb_rapi_jo_ongoing:
+ path: /cmbrapi/joongoing
+ controller: App\Controller\CMBRAPIController::getOngoingJobOrder
+ methods: [GET]
+
+cmb_rapi_payment_methods:
+ path: /cmbrapi/paymentmethods
+ controller: App\Controller\CMBRAPIController::getPaymentMethods
+ methods: [GET]
+
+cmb_rapi_cancel_reasons:
+ path: /cmbrapi/cancelreasons
+ controller: App\Controller\CMBRAPIController::getCancelReasons
+ methods: [GET]
+
+cmb_rapi_jo_verify:
+ path: /cmbrapi/joverify
+ controller: App\Controller\CMBRAPIController::verifyJobOrder
+ methods: [GET]
diff --git a/config/routes/job_order.yaml b/config/routes/job_order.yaml
index 586ed81f..4ec4279e 100644
--- a/config/routes/job_order.yaml
+++ b/config/routes/job_order.yaml
@@ -253,3 +253,14 @@ jo_hub_view_form:
controller: App\Controller\JobOrderController::hubViewForm
methods: [GET]
+jo_behind_schedule:
+ path: /job-order/behind-schedule
+ controller: App\Controller\JobOrderController::listBehindSchedule
+ methods: [GET]
+
+jo_behind_schedule_rows:
+ path: /job-order/behind-schedule-rows
+ controller: App\Controller\JobOrderController::getRows
+ methods: [POST]
+ defaults:
+ tier: "behind_schedule"
diff --git a/config/routes/notification.yaml b/config/routes/notification.yaml
new file mode 100644
index 00000000..f3a15104
--- /dev/null
+++ b/config/routes/notification.yaml
@@ -0,0 +1,9 @@
+notification_ajax_list:
+ path: /ajax/notifications
+ controller: App\Controller\NotificationController::ajaxList
+ methods: [GET]
+
+notification_ajax_update:
+ path: /ajax/notifications
+ controller: App\Controller\NotificationController::ajaxUpdate
+ methods: [POST]
diff --git a/config/routes/rider.yaml b/config/routes/rider.yaml
index 1934a1b1..6c4911d0 100644
--- a/config/routes/rider.yaml
+++ b/config/routes/rider.yaml
@@ -56,3 +56,14 @@ rider_priority_down_jo:
path: /riders/{id}/priority_down/{jo_id}
controller: App\Controller\RiderController::priorityDownJO
methods: [GET]
+
+rider_ajax_avialable:
+ path: /riders/{id}/available
+ controller: App\Controller\RiderController::ajaxAvailable
+ methods: [GET]
+
+rider_ajax_rider_name:
+ path: /riders/{id}/name
+ controller: App\Controller\RiderController::ajaxRiderName
+ methods: [GET]
+
diff --git a/config/services.yaml b/config/services.yaml
index 05fd3b1a..7d585e08 100644
--- a/config/services.yaml
+++ b/config/services.yaml
@@ -5,6 +5,7 @@ parameters:
latitude: 14.6091
longitude: 121.0223
image_upload_directory: '%kernel.project_dir%/public/uploads'
+ jo_extra_upload_directory: '%kernel.project_dir%/public/uploads/jo_extra'
job_order_refresh_interval: 300000
api_acl_file: 'api_acl.yaml'
api_access_key: 'api_access_keys'
@@ -161,39 +162,40 @@ services:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
# invoice generator
- App\Service\InvoiceGenerator\ResqInvoiceGenerator: ~
+ App\Service\InvoiceGenerator\CMBInvoiceGenerator: ~
# invoice generator interface
- App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\ResqInvoiceGenerator"
+ App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\CMBInvoiceGenerator"
# job order generator
- App\Service\JobOrderHandler\ResqJobOrderHandler:
+ App\Service\JobOrderHandler\CMBJobOrderHandler:
arguments:
$country_code: "%env(COUNTRY_CODE)%"
#job order generator interface
- App\Service\JobOrderHandlerInterface: "@App\\Service\\JobOrderHandler\\ResqJobOrderHandler"
+ App\Service\JobOrderHandlerInterface: "@App\\Service\\JobOrderHandler\\CMBJobOrderHandler"
# customer generator
- App\Service\CustomerHandler\ResqCustomerHandler:
+ App\Service\CustomerHandler\CMBCustomerHandler:
arguments:
$country_code: "%env(COUNTRY_CODE)%"
# customer generator interface
- App\Service\CustomerHandlerInterface: "@App\\Service\\CustomerHandler\\ResqCustomerHandler"
+ App\Service\CustomerHandlerInterface: "@App\\Service\\CustomerHandler\\CMBCustomerHandler"
# rider assignment
- App\Service\RiderAssignmentHandler\ResqRiderAssignmentHandler: ~
+ App\Service\RiderAssignmentHandler\CMBRiderAssignmentHandler: ~
# rider assignment interface
- App\Service\RiderAssignmentHandlerInterface: "@App\\Service\\RiderAssignmentHandler\\ResqRiderAssignmentHandler"
+ App\Service\RiderAssignmentHandlerInterface: "@App\\Service\\RiderAssignmentHandler\\CMBRiderAssignmentHandler"
# rider API service
- App\Service\RiderAPIHandler\ResqRiderAPIHandler:
+ App\Service\RiderAPIHandler\CMBRiderAPIHandler:
arguments:
- $country_code: "%env(COUNTRY_CODE)%"
+ $country_code: "%env(COUNTRY_CODE)%"
+ $upload_dir: "%jo_extra_upload_directory%"
- App\Service\RiderAPIHandlerInterface: "@App\\Service\\RiderAPIHandler\\ResqRiderAPIHandler"
+ App\Service\RiderAPIHandlerInterface: "@App\\Service\\RiderAPIHandler\\CMBRiderAPIHandler"
# map manager
#App\Service\GISManager\Bing: ~
@@ -219,6 +221,12 @@ services:
event: 'postPersist'
entity: 'App\Entity\JobOrder'
+ App\Service\NotificationManager:
+ arguments:
+ $redis_prov: "@App\\Service\\RedisClientProvider"
+ $redis_mqtt_key: "mqtt_events"
+ $em: "@doctrine.orm.entity_manager"
+
App\Service\JobOrderCache:
arguments:
$redis_prov: "@App\\Service\\RedisClientProvider"
diff --git a/public/assets/css/style.css b/public/assets/css/style.css
index 140966da..4b69c445 100644
--- a/public/assets/css/style.css
+++ b/public/assets/css/style.css
@@ -155,6 +155,29 @@ span.has-danger,
color: #fff !important;
}
+.m-table__row--is_in_progress td {
+ background-color: #FFA500 !important;
+ color: #fff !important;
+}
+
+.m-table__row--is_in_progress td > span,
+.m-table__row--is_in_progress td > span a,
+.m-table__row--is_in_progress td > span a i {
+ color: #fff !important;
+}
+
+.m-table__row--is_assigned td {
+ background-color: #0000ff !important;
+ color: #fff !important;
+}
+
+.m-table__row--is_assigned td > span,
+.m-table__row--is_assigned td > span a,
+.m-table__row--is_assigned td > span a i {
+ color: #fff !important;
+}
+
+
.m-datatable.m-datatable--default > .m-datatable__table {
min-height: 0 !important;
}
@@ -358,4 +381,4 @@ span.has-danger,
.map-info .m-badge {
border-radius: 0;
-}
\ No newline at end of file
+}
diff --git a/public/assets/js/dashboard_map.js b/public/assets/js/dashboard_map.js
index 3f02d1f4..2df2d69f 100644
--- a/public/assets/js/dashboard_map.js
+++ b/public/assets/js/dashboard_map.js
@@ -3,6 +3,8 @@ class DashboardMap {
this.options = options;
this.rider_markers = rider_markers;
this.cust_markers = cust_markers;
+ this.rider_availability = {};
+ this.rider_names = {};
// layer groups
this.layer_groups = {
@@ -146,6 +148,49 @@ class DashboardMap {
}
}
+ putMarkerWithLabel(id, lat, lng, markers, icon, layer_group, popup_url, name) {
+ var my = this;
+ // existing marker
+ if (markers.hasOwnProperty(id)) {
+ markers[id].setLatLng(L.latLng(lat, lng));
+ return;
+ }
+
+ // new marker
+ // add label
+ markers[id] = L.marker(
+ [lat, lng],
+ { icon: icon }
+ );
+
+ var marker_label = id + ' - ' + name;
+
+ markers[id].bindTooltip(marker_label,
+ {
+ permanent: true,
+ direction: 'right'
+ }
+ );
+
+ markers[id].addTo(layer_group);
+
+ if (my.options.enable_popup) {
+ markers[id].bindPopup('Loading...');
+
+ // bind ajax for popup
+ markers[id].on('click', function(e) {
+ var popup = e.target.getPopup();
+ var url = popup_url.replace('[id]', id);
+ console.log(url);
+ $.get(url).done(function(data) {
+ popup.setContent(data);
+ popup.update();
+ });
+ });
+ }
+ }
+
+
putCustomerMarker(id, lat, lng) {
this.putMarker(
id,
@@ -170,6 +215,8 @@ class DashboardMap {
this.layer_groups.customer.removeLayer(markers[id]);
this.layer_groups.mobile_customer.removeLayer(markers[id]);
+
+ delete markers[id];
}
putMobileCustomerMarker(id, lat, lng) {
@@ -185,27 +232,37 @@ class DashboardMap {
}
putRiderAvailableMarker(id, lat, lng) {
- this.putMarker(
- id,
- lat,
- lng,
- this.rider_markers,
- this.options.icons.rider_available,
- this.layer_groups.rider_available,
- this.options.rider_popup_url
- );
+ var my = this;
+
+ my.getRiderName(id, function(name) {
+ my.putMarkerWithLabel(
+ id,
+ lat,
+ lng,
+ my.rider_markers,
+ my.options.icons.rider_available,
+ my.layer_groups.rider_available,
+ my.options.rider_popup_url,
+ name
+ );
+ });
}
putRiderActiveJOMarker(id, lat, lng) {
- this.putMarker(
- id,
- lat,
- lng,
- this.rider_markers,
- this.options.icons.rider_active_jo,
- this.layer_groups.rider_active_jo,
- this.options.rider_popup_url
- );
+ var my = this;
+
+ my.getRiderName(id, function(name) {
+ my.putMarkerWithLabel(
+ id,
+ lat,
+ lng,
+ my.rider_markers,
+ my.options.icons.rider_active_jo,
+ my.layer_groups.rider_active_jo,
+ my.options.rider_popup_url,
+ name
+ );
+ });
}
removeRiderMarker(id) {
@@ -219,6 +276,8 @@ class DashboardMap {
this.layer_groups.rider_active_jo.removeLayer(markers[id]);
this.layer_groups.rider_available.removeLayer(markers[id]);
+
+ delete markers[id];
}
loadLocations(location_url) {
@@ -252,6 +311,7 @@ class DashboardMap {
$.each(riders, function(id, data) {
var lat = data.latitude;
var lng = data.longitude;
+ var name = '';
if (data.has_jo)
my.putRiderActiveJOMarker(id, lat, lng);
@@ -262,4 +322,31 @@ class DashboardMap {
// console.log(rider_markers);
});
}
+
+ getRiderName(id, callback) {
+ var name = '';
+ var rider_url = this.options.rider_name_url.replace('[id]', id);
+
+ var my = this;
+
+ console.log('getting rider name for rider ' + id);
+
+ // check if we have it in cache
+ if (this.rider_names.hasOwnProperty(id)) {
+ name = this.rider_names[id];
+ callback(name);
+ } else {
+ // ajax call to get it
+ $.ajax({
+ method: "GET",
+ url: rider_url
+ }).done(function(response) {
+ name = response.rider_name;
+
+ // set name in cache
+ my.rider_names[id] = name;
+ callback(name);
+ });
+ }
+ }
}
diff --git a/public/assets/js/map_mqtt.js b/public/assets/js/map_mqtt.js
index 0e6bdb31..8a38e8ff 100644
--- a/public/assets/js/map_mqtt.js
+++ b/public/assets/js/map_mqtt.js
@@ -1,7 +1,8 @@
class MapEventHandler {
- constructor(options, dashmap) {
+ constructor(options, dashmap, ssl) {
this.options = options;
this.dashmap = dashmap;
+ this.ssl = ssl;
}
connect(user_id, host, port) {
@@ -9,20 +10,32 @@ class MapEventHandler {
var client_id = "dash-" + user_id + "-" + d.getMonth() + "-" + d.getDate() + "-" + d.getHours() + "-" + d.getMinutes() + "-" + d.getSeconds() + "-" + d.getMilliseconds();
console.log(client_id);
- this.mqtt = new Paho.MQTT.Client(host, port, client_id);
+ let protocol = 'ws';
+ if (this.ssl)
+ protocol = 'wss';
+
+ this.mqtt = new Paho.Client(protocol + '://' + host + ':' + port + '/mqtt', client_id);
var options = {
- // useSSL: true,
- timeout: 3,
+ useSSL: this.ssl,
+ timeout: 10,
+ keepAliveInterval: 10,
invocationContext: this,
onSuccess: this.onConnect.bind(this),
+ reconnect: true
};
this.mqtt.onMessageArrived = this.onMessage.bind(this);
+ this.mqtt.onConnectionLost = this.onConnectionLost;
console.log('connecting to mqtt server...');
this.mqtt.connect(options);
}
+ onConnectionLost(err) {
+ console.log('mqtt connection lost');
+ console.log(err);
+ }
+
onConnect(icontext) {
console.log('mqtt connected!');
var my = icontext.invocationContext;
@@ -35,6 +48,10 @@ class MapEventHandler {
// subscribe to rider status
console.log('subscribing to ' + my.options.channels.rider_status);
my.mqtt.subscribe(my.options.channels.rider_status);
+
+ // subscribe to rider availability
+ console.log('subscribing to ' + my.options.channels.rider_availability);
+ my.mqtt.subscribe(my.options.channels.rider_availability);
}
if (my.options.track_jo) {
@@ -55,7 +72,7 @@ class MapEventHandler {
onMessage(msg) {
// console.log(msg);
- console.log('received message');
+ // console.log('received message');
var channel = msg.destinationName;
var chan_split = channel.split('/');
@@ -73,13 +90,55 @@ class MapEventHandler {
}
handleRider(chan_split, payload) {
- console.log("rider message");
+ //console.log("rider message");
+ var rider_id = chan_split[1];
+ //console.log('url ' + this.dashmap.options.rider_availability_url);
switch (chan_split[2]) {
- case "location":
- console.log("got location for rider " + chan_split[1] + " - " + payload);
- var pl_split = payload.split(':');
- console.log(pl_split);
+ case "availability":
+ console.log("got availability for rider " + chan_split[1] + " - " + payload);
+ var obj = JSON.parse(payload);
+ var status = obj.status;
+ console.log("status " + status);
+ switch (status) {
+ case 'rider_offline':
+ this.dashmap.rider_availability[chan_split[1]] = false;
+ this.dashmap.removeRiderMarker(chan_split[1]);
+ break;
+ case 'rider_online':
+ this.dashmap.rider_availability[chan_split[1]] = true;
+ var lat = parseFloat(obj.latitude);
+ var lng = parseFloat(obj.longitude);
+
+ // cheeck if rider is available / unavailable
+ // TODO: make url not hardcoded
+ var dashmap = this.dashmap;
+ var url = dashmap.options.rider_availability_url;
+ var rider_availability_url = url.replace('[id]', chan_split[1]);
+ //console.log(rider_availability_url);
+ $.get(rider_availability_url).done(function(data) {
+ console.log('rider availability - ' + data);
+ switch (data) {
+ case 'available':
+ console.log('putting available marker ' + chan_split[1] + ' ' + lat + ':' + lng);
+ dashmap.switchRiderStatus(chan_split[1], 'available');
+ dashmap.putRiderAvailableMarker(chan_split[1], lat, lng);
+ break;
+ case 'unavailable':
+ console.log('putting active jo marker ' + chan_split[1] + ' ' + lat + ':' + lng);
+ dashmap.switchRiderStatus(chan_split[1], 'jo');
+ dashmap.putRiderActiveJOMarker(chan_split[1], lat, lng);
+ break;
+ }
+ });
+ break;
+ }
+ break;
+ case "location":
+ // console.log("got location for rider " + chan_split[1] + " - " + payload
+ var pl_split = payload.split(':');
+ // console.log(pl_split);
+
// check for correct format
if (pl_split.length != 2)
break;
@@ -87,8 +146,22 @@ class MapEventHandler {
var lat = parseFloat(pl_split[0]);
var lng = parseFloat(pl_split[1]);
- // TODO: check if available or not
- this.dashmap.putRiderAvailableMarker(chan_split[1], lat, lng);
+ var display_marker = true;
+ if (this.dashmap.rider_availability.hasOwnProperty(chan_split[1])) {
+ if (!this.dashmap.rider_availability[chan_split[1]]) {
+ console.log('NOT displaying marker for inactive rider');
+ display_marker = false;
+ }
+ } else {
+ console.log('rider not in availability check');
+ display_marker = false;
+ }
+
+ // TODO: cache rider availability (available vs active jo) status and check before displaying icon
+ // NOTE: we really should fix our terms since available can mean many things
+ if (display_marker) {
+ this.dashmap.putRiderAvailableMarker(chan_split[1], lat, lng);
+ }
break;
case "status":
console.log("got status for rider " + chan_split[1] + " - " + payload);
diff --git a/public/assets/js/notification.js b/public/assets/js/notification.js
new file mode 100644
index 00000000..9a4b169c
--- /dev/null
+++ b/public/assets/js/notification.js
@@ -0,0 +1,111 @@
+class NotificationHandler {
+ constructor(options) {
+ this.options = options;
+ }
+
+ clearAll() {
+ // clear notification count
+ document.getElementById('notif-count').innerHTML = '';
+
+ // remove notifications
+ document.getElementById('notif-body').innerHTML = '';
+ }
+
+ loadAll() {
+ console.log('loading notifications');
+ // ajax load
+ var self = this;
+ var notif_update_url = this.options['notif_ajax_update_url'];
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', this.options['notif_ajax_url']);
+ xhr.onload = function() {
+ if (xhr.status === 200) {
+ var data = JSON.parse(xhr.responseText);
+ var notifs = data.notifications;
+
+ // update notification unread count
+ var count_html = data.unread_count;
+ document.getElementById('notif-count').innerHTML = count_html;
+
+ // do we have any notifications?
+ if (notifs.length <= 0)
+ return;
+
+ // add actual notifications
+ var notif_body = document.getElementById('notif-body');
+ var notif_index = 0;
+ notifs.forEach(function(notif) {
+ var notif_date = moment(notif.date).fromNow();
+
+ var notif_html = '
';
+ notif_html += '
';
+ notif_html += '
';
+ notif_html += '' + notif.text + ''
+ notif_html += '';
+ notif_html += '
';
+ notif_html += notif_date;
+ notif_html += '';
+ notif_html += '
';
+
+ notif_body.insertAdjacentHTML('beforeend', notif_html);
+
+ document.getElementsByClassName('m-list-timeline__item')[notif_index].addEventListener('click', function(e) {
+ e.preventDefault();
+ $.ajax({
+ method: "POST",
+ url: notif_update_url,
+ data: {id: notif.id}
+ }).done(function(response) {
+ window.location.href = notif.link;
+ });
+ });
+
+ notif_index++;
+ });
+ }
+
+ };
+ xhr.send();
+ }
+
+ listen(user_id, host, port, use_ssl = false) {
+ var d = new Date();
+ var client_id = "dash-" + user_id + "-" + d.getMonth() + "-" + d.getDate() + "-" + d.getHours() + "-" + d.getMinutes() + "-" + d.getSeconds() + "-" + d.getMilliseconds();
+
+ this.mqtt = new Paho.MQTT.Client(host, port, client_id);
+ var options = {
+ useSSL: use_ssl,
+ timeout: 3,
+ invocationContext: this,
+ onSuccess: this.onConnect.bind(this)
+ }
+
+ this.mqtt.onMessageArrived = this.onMessage.bind(this);
+
+ this.mqtt.connect(options);
+ }
+
+ onConnect(icontext) {
+ console.log('notification mqtt connected');
+ var my = icontext.invocationContext;
+
+ // subscribe to general notifications
+ my.mqtt.subscribe('user/0/notification');
+ }
+
+ onMessage(msg) {
+ console.log('notification event received');
+ // we don't care about messasge, we update
+ this.clearAll();
+ this.loadAll();
+ }
+
+ getIcon(type_id) {
+ if (type_id in this.options['icons']) {
+ return this.options['icons'][type_id];
+ }
+
+ return this.options['default_icon'];
+ }
+
+}
diff --git a/resq_settings/cmb/menu.yaml b/resq_settings/cmb/menu.yaml
new file mode 100644
index 00000000..9f28b315
--- /dev/null
+++ b/resq_settings/cmb/menu.yaml
@@ -0,0 +1,179 @@
+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/resq_settings/cmb/messages.en.yaml b/resq_settings/cmb/messages.en.yaml
new file mode 100644
index 00000000..3e12543b
--- /dev/null
+++ b/resq_settings/cmb/messages.en.yaml
@@ -0,0 +1,27 @@
+# text
+title_login: Res-Q for CMB | Login
+block_title: Res-Q for CMB
+control_panel_sign_in: Sign-in to Control Panel
+alt_image_logo_login: Res-Q for CMB
+alt_image_dashboard: Res-Q for CMB
+copyright: Res-Q for CMB
+battery_size_tradein_brand: Trade-in Motolite
+battery_size_tradein_premium: Trade-in Premium
+battery_size_tradein_other: Trade-in Other
+add_cust_vehicle_battery_info: This vehicle is using a Motolite battery
+jo_title_pdf: Res-Q for CMB Job Order
+country_code_prefix: '+60'
+delivery_instructions_label: 'CarFix Details'
+
+# images
+image_logo_login: /assets/images/black-text-logo-01.png
+icon_login: /assets/images/battery-assist-bm-logo-32x32.png
+icon_base_32x32: /assets/images/black-text-logo-01-32x32.png
+icon_base_16x16: /assets/images/black-text-logo-01-16x16.png
+image_dashboard: /assets/images/century_logo.png
+image_jo_pdf: /public/assets/images/black-text-logo-01-115x115.png
+
+# default point for maps
+default_lat: 3.084216
+default_long: 101.6129996
+default_region: my
diff --git a/resq_settings/cmb/services.yaml b/resq_settings/cmb/services.yaml
new file mode 100644
index 00000000..3d9fd7a3
--- /dev/null
+++ b/resq_settings/cmb/services.yaml
@@ -0,0 +1,246 @@
+# 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'
+ jo_extra_upload_directory: '%kernel.project_dir%/public/uploads/jo_extra'
+ 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)%"
+ api_version: "%env(API_VERSION)%"
+
+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)%"
+ $dr_url: "https://resqaws.jankstudio.com/sms/delivery_receipt"
+
+ 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)%"
+ $upload_dir: "%jo_extra_upload_directory%"
+
+ 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)%"
+
+ # inventory manager
+ App\Service\InventoryManager:
+ arguments:
+ $api_url: "%env(INVENTORY_API_URL)%"
+ $api_ocp_key: "%env(INVENTORY_API_OCP)%"
+ $api_auth_prefix: "%env(INVENTORY_API_AUTH_TOKEN_PREFIX)%"
+ $api_auth_token: "%env(INVENTORY_API_AUTH_TOKEN)%"
+
+ # API logging
+ App\EventSubscriber\LogSubscriber:
+ arguments:
+ $api_log_flag: "%env(API_LOGGING)%"
diff --git a/resq_settings/resq/menu.yaml b/resq_settings/resq/menu.yaml
new file mode 100644
index 00000000..69f0b806
--- /dev/null
+++ b/resq_settings/resq/menu.yaml
@@ -0,0 +1,186 @@
+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: 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_in
+ acl: jo_in.list
+ label: Incoming
+ parent: joborder
+ - id: jo_proc
+ acl: jo_proc.list
+ label: Dispatch
+ parent: joborder
+ - id: jo_assign
+ acl: jo_assign.list
+ label: Rider Assignment
+ parent: joborder
+ - id: jo_fulfill
+ acl: jo_fulfill.list
+ label: Fulfillment
+ parent: joborder
+ - id: jo_open
+ acl: jo_open.list
+ label: Open
+ parent: joborder
+ - id: jo_all
+ acl: jo_all.list
+ label: View All
+ parent: joborder
+ - id: jo_hub.view
+ label: Hub View
+ 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/resq_settings/resq/messages.en.yaml b/resq_settings/resq/messages.en.yaml
new file mode 100644
index 00000000..c0fb8ca1
--- /dev/null
+++ b/resq_settings/resq/messages.en.yaml
@@ -0,0 +1,27 @@
+# text
+title_login: Motolite Res-Q | Login
+block_title: Motolite Res-Q
+control_panel_sign_in: Sign-in to Control Panel
+alt_image_logo_login: Res-Q
+alt_image_dashboard: Motolite
+copyright: Motolite Res-Q
+battery_size_tradein_brand: Trade-in Motolite
+battery_size_tradein_premium: Trade-in Premium
+battery_size_tradein_other: Trade-in Other
+add_cust_vehicle_battery_info: This vehicle is using a Motolite battery
+jo_title_pdf: Motolite Res-Q Job Order
+country_code_prefix: '+63'
+delivery_instructions_label: Delivery Instructions
+
+# images
+image_logo_login: /assets/images/logo-resq.png
+icon_login: /assets/demo/default/media/img/logo/favicon.ico
+icon_base_32x32: /assets/images/favicon/favicon-32x32.png
+icon_base_16x16: /assets/images/favicon/favicon-16x16.png
+image_dashboard: /assets/images/logo-motolite.png
+image_jo_pdf: /public/assets/images/logo-resq.png
+
+# default point for maps
+default_lat: 14.6091
+default_long: 121.0223
+default_region: ph
diff --git a/resq_settings/resq/services.yaml b/resq_settings/resq/services.yaml
new file mode 100644
index 00000000..7f568033
--- /dev/null
+++ b/resq_settings/resq/services.yaml
@@ -0,0 +1,244 @@
+# 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)%"
+ api_version: "%env(API_VERSION)%"
+
+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)%"
+ $dr_url: "https://resqaws.jankstudio.com/sms/delivery_receipt"
+
+ 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\ResqInvoiceGenerator: ~
+
+ # invoice generator interface
+ App\Service\InvoiceGeneratorInterface: "@App\\Service\\InvoiceGenerator\\ResqInvoiceGenerator"
+
+ # job order generator
+ App\Service\JobOrderHandler\ResqJobOrderHandler:
+ arguments:
+ $country_code: "%env(COUNTRY_CODE)%"
+
+ #job order generator interface
+ App\Service\JobOrderHandlerInterface: "@App\\Service\\JobOrderHandler\\ResqJobOrderHandler"
+
+ # customer generator
+ App\Service\CustomerHandler\ResqCustomerHandler:
+ arguments:
+ $country_code: "%env(COUNTRY_CODE)%"
+
+ # customer generator interface
+ App\Service\CustomerHandlerInterface: "@App\\Service\\CustomerHandler\\ResqCustomerHandler"
+
+ # rider assignment
+ App\Service\RiderAssignmentHandler\ResqRiderAssignmentHandler: ~
+
+ # rider assignment interface
+ App\Service\RiderAssignmentHandlerInterface: "@App\\Service\\RiderAssignmentHandler\\ResqRiderAssignmentHandler"
+
+ # rider API service
+ App\Service\RiderAPIHandler\ResqRiderAPIHandler:
+ arguments:
+ $country_code: "%env(COUNTRY_CODE)%"
+
+ App\Service\RiderAPIHandlerInterface: "@App\\Service\\RiderAPIHandler\\ResqRiderAPIHandler"
+
+ # map manager
+ #App\Service\GISManager\Bing: ~
+ App\Service\GISManager\OpenStreet: ~
+ #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)%"
+
+ # inventory manager
+ App\Service\InventoryManager:
+ arguments:
+ $api_url: "%env(INVENTORY_API_URL)%"
+ $api_ocp_key: "%env(INVENTORY_API_OCP)%"
+ $api_auth_prefix: "%env(INVENTORY_API_AUTH_TOKEN_PREFIX)%"
+ $api_auth_token: "%env(INVENTORY_API_AUTH_TOKEN)%"
+
+ # API logging
+ App\EventSubscriber\LogSubscriber:
+ arguments:
+ $api_log_flag: "%env(API_LOGGING)%"
diff --git a/src/Command/ConfigureResqCommand.php b/src/Command/ConfigureResqCommand.php
new file mode 100644
index 00000000..f4316056
--- /dev/null
+++ b/src/Command/ConfigureResqCommand.php
@@ -0,0 +1,55 @@
+setName('resq:configure')
+ ->setDescription('Copies the configuration files, depending on environment.')
+ ->setHelp('Copies the configuration files, depending on environment.')
+ ->addArgument('dir_name', InputArgument::REQUIRED, 'Name of source subdirectory.');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $dir_name = $input->getArgument('dir_name');
+
+ $filesystem = new Filesystem();
+ $current_dir = getcwd();
+
+ $source_dir = $current_dir . '/resq_settings/' . $dir_name . '/';
+ $config_dir = $current_dir . '/config/';
+ $translations_dir = $current_dir . '/translations/';
+
+ // copy services.yaml file
+ error_log('Copying services.yaml file...');
+ $filesystem->copy($source_dir . 'services.yaml', $config_dir . 'services.yaml', true);
+
+ // copy menu.yaml file
+ error_log('Copying menu.yaml file...');
+ $filesystem->copy($source_dir . 'menu.yaml', $config_dir . 'menu.yaml', true);
+
+ // copy messages.en.yaml file
+ error_log('Copying messages.en.yaml file...');
+ $filesystem->copy($source_dir . 'messages.en.yaml', $translations_dir . 'messages.en.yaml', true);
+
+ error_log('Done copying files.');
+
+ return 0;
+ }
+}
diff --git a/src/Command/CreateJOTestDataCommand.php b/src/Command/CreateJOTestDataCommand.php
new file mode 100644
index 00000000..7aafe444
--- /dev/null
+++ b/src/Command/CreateJOTestDataCommand.php
@@ -0,0 +1,173 @@
+em = $em;
+
+ parent::__construct();
+ }
+
+ protected function configure()
+ {
+ $this->setName('joborder:create_testdata')
+ ->setDescription('Create JO test data, given a rider id.')
+ ->setHelp('Create JO test data, given a rider id.')
+ ->addArgument('rider_id', InputArgument::REQUIRED, 'Rider id.');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $rider_id = $input->getArgument('rider_id');
+
+ $rider = $this->em->getRepository(Rider::class)->find($rider_id);
+
+ // get customer vehicles where plate number is not null
+ $cv_query = $this->em->createQuery('SELECT cv FROM App\Entity\CustomerVehicle cv WHERE cv.plate_number is not null');
+ $cv_results = $cv_query->getResult();
+
+ // get the first customer vehicle
+ $cv = current($cv_results);
+
+ // get batteries
+ $battery_results = $this->em->getRepository(Battery::class)->findAll();
+
+ // get the first battery
+ $battery = current($battery_results);
+
+ error_log($cv->getPlateNumber());
+ error_log($battery->getID());
+
+ if (empty($rider))
+ {
+ error_log('Rider not found.');
+ }
+ else
+ {
+ // get current date
+ $current_date = new DateTime();
+
+ // for this month JO
+ $current_year = $current_date->format('Y');
+ $current_month = $current_date->format('M');
+
+ // for last month JO
+ $date_interval = new DateInterval('P1M');
+ $last_month_date = $current_date->sub($date_interval);
+
+ $last_month_year = $last_month_date->format('Y');
+ $last_month_month = $last_month_date->format('M');
+
+ $time_schedule = $current_date->format('h:i A');
+
+ $this->createJobOrders($rider, $cv, $battery, $last_month_year, $last_month_month, $time_schedule);
+ $this->createJobOrders($rider, $cv, $battery, $current_year, $current_month, $time_schedule);
+ }
+
+ return 0;
+ }
+
+ protected function createJobOrders(Rider $rider, CustomerVehicle $cv, Battery $battery, $year, $month, $time)
+ {
+ // insert 15 JOs for last month
+ for ($i = 1; $i <= 15; $i++)
+ {
+ $jo = new JobOrder();
+
+ // set customer data
+ $jo->setCustomerVehicle($cv);
+ $jo->setCustomer($cv->getCustomer());
+
+ // set hub and rider data
+ $jo->setRider($rider);
+ $jo->setHub($rider->getHub());
+
+ // set JO details
+ $point = new Point(121.0223, 14.6091);
+ $s_type = CMBServiceType::BATTERY_REPLACEMENT_NEW;
+ $source = TransactionOrigin::CALL;
+ $advance_order = true;
+ $warranty_class = WarrantyClass::WTY_PRIVATE;
+ $status = JOStatus::FULFILLED;
+ $delivery_address = '#1234 Moogle Lane';
+ $mode_of_payment = ModeOfPayment::CASH;
+
+ // set invoice details
+ $invoice = new Invoice();
+ $invoice_item = new InvoiceItem();
+
+ // set invoice item details
+ $invoice_item->setBattery($battery)
+ ->setTitle($battery->getModel()->getName() . ' ' . $battery->getSize()->getName())
+ ->setQuantity(1)
+ ->setPrice($battery->getSellingPrice())
+ ->setInvoice($invoice);
+
+ $invoice->addItem($invoice_item);
+
+ $this->em->persist($invoice_item);
+
+ // set invoice details
+ $invoice->setTotalPrice($battery->getSellingPrice())
+ ->setStatus(InvoiceStatus::DRAFT)
+ ->setVATExclusivePrice($battery->getSellingPrice())
+ ->setDiscount(0.0)
+ ->setTradeIn(0.0)
+ ->setVAT(0.00);
+
+ $this->em->persist($invoice);
+
+ $jo->setInvoice($invoice);
+
+ // for last month
+ $date_schedule_date = $i . ' ' . $month . ' ' . $year . ' ' . $time;
+ error_log('Adding JO with date schedule ' . $date_schedule_date);
+ $date_schedule = DateTime::createFromFormat("d M Y h:i A", $date_schedule_date);
+
+ $jo->setDateSchedule($date_schedule)
+ ->setCoordinates($point)
+ ->setAdvanceOrder($advance_order)
+ ->setServiceType($s_type)
+ ->setWarrantyClass($warranty_class)
+ ->setSource($source)
+ ->setStatus($status)
+ ->setDeliveryAddress($delivery_address);
+
+ $this->em->persist($jo);
+ }
+
+ $this->em->flush();
+
+ }
+}
diff --git a/src/Command/ImportCMBBatteryDataCommand.php b/src/Command/ImportCMBBatteryDataCommand.php
index 79e84571..8a074a86 100644
--- a/src/Command/ImportCMBBatteryDataCommand.php
+++ b/src/Command/ImportCMBBatteryDataCommand.php
@@ -70,8 +70,8 @@ class ImportCMBBatteryDataCommand extends Command
error_log('Processing battery csv file...');
while (($fields = fgetcsv($fh)) !== false)
{
- // data starts at row 2
- if ($row_num < 2)
+ // data starts at row 1
+ if ($row_num < 1)
{
$row_num++;
continue;
@@ -90,22 +90,42 @@ class ImportCMBBatteryDataCommand extends Command
// [0] = battery manufacturer
// [1] = battery model
// [2] = battery size
- // if only 2, get both
- // [0] = battery manufacturer and battery model
- // [1] = battery size
// if 4,
// [0] = battery manufacturer
- // concatenate [1] and [2] for the battery model
+ // [1] = battery model
+ // [2] = extra info
// [3] = battery size
+ // OR
+ // [0] = battery manufacturer
+ // [1] = battery model
+ // [2] = battery size
+ // [3] = battery size --> this one would have ()
+ // if 5,
+ // [0] = battery manufacturer
+ // [1] = battery model
+ // [2] = extra info
+ // [3] = extra info
+ // [4] = battery size
$battery_manufacturer = '';
$battery_model = '';
$battery_size = '';
if (count($battery_info) == 3)
{
// sample: Century Marathoner 120-7L
+ // sample: Century Marathoner M42(60B20L)
$battery_manufacturer = trim($battery_info[0]);
$battery_model = trim($battery_info[1]);
- $battery_size = trim($battery_info[2]);
+
+ // check for parenthesis in battery_info[2]
+ if (strpos($battery_info[2], '(') === false)
+ {
+ // no parenthesis found
+ $battery_size = trim($battery_info[2]);
+ }
+ else
+ {
+ $battery_size = trim(str_replace('(', ' (', $battery_info[2]));
+ }
}
if (count($battery_info) == 2)
{
@@ -117,13 +137,32 @@ class ImportCMBBatteryDataCommand extends Command
if (count($battery_info) == 4)
{
// sample: Motolite Classic Wetcharged DIN100L
+ // sample: Century Excel NS60LS (60B24LS)
$battery_manufacturer = trim($battery_info[0]);
- $battery_model = trim($battery_info[1]) . ' ' . trim($battery_info[2]);
- $battery_size = trim($battery_info[3]);
+ $battery_model = trim($battery_info[1]);
+ // check for parenthesis in battery_info[3]
+ if (strpos($battery_info[3], '(') === false)
+ {
+ // no parenthesis found
+ $battery_size = trim($battery_info[3]);
+ }
+ else
+ {
+ // need to concatenate [2] and [3]
+ $battery_size = trim($battery_info[2]) . ' ' . trim($battery_info[3]);
+ }
+ }
+ if (count($battery_info) == 5)
+ {
+ // sample: Century Marathoner Max Wet NS40ZL
+ $battery_manufacturer = trim($battery_info[0]);
+ $battery_model = trim($battery_info[1]);
+ $battery_size = trim($battery_info[4]);
}
// check if battery size has ()
// if so, trim it to ignore the parenthesis and what's after (.
+ /*
$pos = stripos($battery_size, '(');
if ($pos == true)
{
@@ -133,7 +172,7 @@ class ImportCMBBatteryDataCommand extends Command
else
{
$clean_size = $battery_size;
- }
+ } */
//error_log('battery manufacturer ' . $battery_manufacturer);
//error_log('battery model ' . $battery_model);
@@ -143,24 +182,24 @@ class ImportCMBBatteryDataCommand extends Command
// when we add to db for manufacturer, model, and size, we do not use the normalized versions
$normalized_manu = $this->normalizeName($battery_manufacturer);
$normalized_model = $this->normalizeName($battery_model);
- $normalized_size = $this->normalizeName($clean_size);
+ $normalized_size = $this->normalizeName($battery_size);
// save battery manufacturer if not yet in system
if (!isset($this->bmanu_hash[$normalized_manu]))
{
- $this->addBatteryManufacturer($battery_manufacturer);
+ $this->addBatteryManufacturer(strtoupper($battery_manufacturer));
}
// save battery model if not yet in system
if (!isset($this->bmodel_hash[$normalized_model]))
{
- $this->addBatteryModel($battery_model);
+ $this->addBatteryModel(strtoupper($battery_model));
}
// save battery size if not yet in system
if (!isset($this->bsize_hash[$normalized_size]))
{
- $this->addBatterySize($clean_size);
+ $this->addBatterySize(strtoupper($battery_size));
}
// save battery if not yet in system
diff --git a/src/Command/ImportCMBBatteryModelSizeCommand.php b/src/Command/ImportCMBBatteryModelSizeCommand.php
new file mode 100644
index 00000000..33545c4e
--- /dev/null
+++ b/src/Command/ImportCMBBatteryModelSizeCommand.php
@@ -0,0 +1,365 @@
+em = $om;
+
+ // load existing battery data
+ $this->loadBatteryManufacturers();
+ $this->loadBatteryModels();
+ $this->loadBatterySizes();
+ $this->loadBatteries();
+
+ parent::__construct();
+ }
+
+ protected function configure()
+ {
+ $this->setName('cmbbatterymodelsize:import')
+ ->setDescription('Retrieve from a CSV file battery information.')
+ ->setHelp('Creates battery manufacturers, models, sizes based on data from imported CSV.')
+ ->addArgument('input_file', InputArgument::REQUIRED, 'Path to the CSV file.')
+ ->addArgument('output_file', InputArgument::REQUIRED, 'Path to the output CSV file for entries not added.');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ // check if battery manufacturers have been created
+ if (count($this->bmanu_hash) == 0)
+ {
+ // create the manufacturers
+ $this->createBatteryManufacturerData();
+
+ // reload the hash
+ $this->loadBatteryManufacturers();
+ }
+
+ // check if battery models have been created
+ if (count($this->bmodel_hash) == 0)
+ {
+ // create the battery models
+ $this->createBatteryModelData();
+
+ // reload the hash
+ $this->loadBatteryModels();
+ }
+
+ // get the sizes, vehicles, battery prices from the csv file
+ $csv_file = $input->getArgument('input_file');
+
+ // attempt to open file
+ try
+ {
+ $fh = fopen($csv_file, "r");
+ }
+ catch (Exception $e)
+ {
+ throw new Exception('The file "' . $csv_file . '" could not be read.');
+ }
+
+ $output_file = $input->getArgument('output_file');
+
+ // attempt to open file
+ try
+ {
+ $out_fh = fopen($output_file, "w");
+ }
+ catch (Exception $e)
+ {
+ throw new Exception('The file "' . $output_file . '" could not be read.');
+ }
+
+ // get entity manager
+ $em = $this->em;
+
+ $row_num = 0;
+ $not_added = [];
+ error_log('Processing battery model and size file...');
+ while (($fields = fgetcsv($fh)) !== false)
+ {
+ if ($row_num < 2)
+ {
+ $row_num++;
+ continue;
+ }
+
+ // get battery info from file
+ $marathoner_size = $this->normalizeName(trim($fields[self::F_BATT_MARATHONER_SIZE]));
+ $marathoner_price = trim($fields[self::F_BATT_MARATHONER_PRICE]);
+ $marathoner_tradein_price = trim($fields[self::F_BATT_MARATHONER_TRADEIN_PRICE]);
+
+ $classic_size = $this->normalizeName(trim($fields[self::F_BATT_CLASSIC_SIZE]));
+ $classic_price = trim($fields[self::F_BATT_CLASSIC_PRICE]);
+ $classic_tradein_price = trim($fields[self::F_BATT_CLASSIC_TRADEIN_PRICE]);
+
+ $excel_size = $this->normalizeName(trim($fields[self::F_BATT_EXCEL_SIZE]));
+ $excel_price = trim($fields[self::F_BATT_EXCEL_PRICE]);
+ $excel_tradein_price = trim($fields[self::F_BATT_EXCEL_TRADEIN_PRICE]);
+
+ $sdfc_size = $this->normalizeName(trim($fields[self::F_BATT_SDFC_SIZE]));
+ $sdfc_price = trim($fields[self::F_BATT_SDFC_PRICE]);
+ $sdfc_tradein_price = trim($fields[self::F_BATT_SDFC_TRADEIN_PRICE]);
+
+ // add the battery sizes
+ // check if size is empty or if price and tradein prices are N/A
+ if (!isset($this->bsize_hash[$marathoner_size]))
+ {
+ // ignore blank sizes
+ if ((strlen($marathoner_size) > 0) &&
+ (strlen($marathoner_price) > 0))
+ {
+ // non-numeric entries are ignored since these are N/A == they don't sell it
+ if ((is_numeric($marathoner_price)) &&
+ (is_numeric($marathoner_tradein_price)))
+ {
+ $this->addBatterySize($marathoner_size, $marathoner_price, $marathoner_tradein_price);
+ }
+ }
+ }
+ if (!isset($this->bsize_hash[$classic_size]))
+ {
+ if ((strlen($classic_size) > 0) &&
+ (strlen($classic_price) > 0))
+ {
+ // non-numeric entries are ignored since these are N/A == they don't sell it
+ if ((is_numeric($classic_price)) &&
+ (is_numeric($classic_tradein_price)))
+ {
+ $this->addBatterySize($classic_size, $classic_price, $classic_tradein_price);
+ }
+ }
+ }
+ if (!isset($this->bsize_hash[$excel_size]))
+ {
+ if ((strlen($excel_size) > 0) &&
+ (strlen($excel_price) > 0))
+ {
+ // non-numeric entries are ignored since these are N/A == they don't sell it
+ if ((is_numeric($excel_price)) &&
+ (is_numeric($excel_tradein_price)))
+ {
+ $this->addBatterySize($excel_size, $excel_price, $excel_tradein_price);
+ }
+ }
+
+ }
+ if (!isset($this->bsize_hash[$sdfc_size]))
+ {
+ if ((strlen($sdfc_size) > 0) &&
+ (strlen($sdfc_price) > 0))
+ {
+ // non-numeric entries are ignored since these are N/A == they don't sell it
+ if ((is_numeric($sdfc_price)) &&
+ (is_numeric($sdfc_tradein_price)))
+ {
+ $this->addBatterySize($sdfc_size, $sdfc_price, $sdfc_tradein_price);
+ }
+ }
+ }
+
+ $row_num++;
+ }
+
+ // output the battery sizes that were not added
+ if (count($not_added) > 0)
+ {
+ fputcsv($out_fh, [
+ 'Battery Model',
+ 'Battery Size',
+ 'Price',
+ 'Trade In Price',
+ 'Reason',
+ ]);
+ foreach($not_added as $row)
+ {
+ fputcsv($out_fh, $row);
+ }
+ }
+
+ fclose($out_fh);
+ }
+
+ protected function createBatteryManufacturerData()
+ {
+ foreach ($this->batt_manufacturers as $name)
+ {
+ $new_bmanu = new BatteryManufacturer();
+ $new_bmanu->setName($name);
+
+ $this->em->persist($new_bmanu);
+ }
+
+ $this->em->flush();
+ }
+
+ protected function createBatteryModelData()
+ {
+ foreach ($this->batt_models as $name)
+ {
+ $new_bmodel = new BatteryModel();
+ $new_bmodel->setName($name);
+
+ $this->em->persist($new_bmodel);
+ }
+
+ $this->em->flush();
+ }
+
+ protected function addBatterySize($size, $price, $tradein_price)
+ {
+ $new_bsize = new BatterySize();
+
+ $clean_size = strtoupper($size);
+ // check if size is M-42, if so, we need to change it to M42
+ if (strpos($clean_size, 'M-42') !== false)
+ {
+ $clean_size = strtoupper(str_replace('-', '', $size));
+ }
+
+ $new_bsize->setName(strtoupper($clean_size));
+ $new_bsize->setTIPriceMotolite($tradein_price);
+
+ $this->em->persist($new_bsize);
+
+ // add to hash
+ $this->bsize_hash[$size] = $new_bsize;
+
+ $this->em->flush();
+ }
+
+ protected function addInvalidEntry($model, $size, $price,
+ $tradein_price, $reason)
+ {
+ $entry = [
+ $model,
+ $size,
+ $price,
+ $tradein_price,
+ $reason,
+ ];
+
+ return $entry;
+ }
+
+
+ protected function loadBatteryManufacturers()
+ {
+ $this->bmanu_hash = [];
+
+ $batt_manufacturers = $this->em->getRepository(BatteryManufacturer::class)->findAll();
+ foreach ($batt_manufacturers as $batt_manu)
+ {
+ $name = $this->normalizeName($batt_manu->getName());
+ $this->bmanu_hash[$name] = $batt_manu;
+ }
+ }
+
+ protected function loadBatteryModels()
+ {
+ $this->bmodel_hash = [];
+
+ $batt_models = $this->em->getRepository(BatteryModel::class)->findAll();
+ foreach ($batt_models as $batt_model)
+ {
+ $name = $this->normalizeName($batt_model->getName());
+ $this->bmodel_hash[$name] = $batt_model;
+ }
+ }
+
+ protected function loadBatterySizes()
+ {
+ $this->bsize_hash = [];
+
+ $batt_sizes = $this->em->getRepository(BatterySize::class)->findAll();
+ foreach ($batt_sizes as $batt_size)
+ {
+ $name = $this->normalizeName($batt_size->getName());
+ $this->bsize_hash[$name] = $batt_size;
+ }
+ }
+
+ protected function loadBatteries()
+ {
+ $this->batt_hash = [];
+
+ $batts = $this->em->getRepository(Battery::class)->findAll();
+ foreach ($batts as $batt)
+ {
+ $brand = $this->normalizeName($batt->getManufacturer()->getName());
+ $model = $this->normalizeName($batt->getModel()->getName());
+ $size = $this->normalizeName($batt->getSize()->getName());
+
+ $this->batt_hash[$brand][$model][$size] = $batt;
+ }
+ }
+
+ protected function normalizeName($name)
+ {
+ $normalized_key = trim(strtolower($name));
+
+ return $normalized_key;
+ }
+}
diff --git a/src/Command/ImportCMBBatteryVehicleCompatibilityCommand.php b/src/Command/ImportCMBBatteryVehicleCompatibilityCommand.php
new file mode 100644
index 00000000..202b1c78
--- /dev/null
+++ b/src/Command/ImportCMBBatteryVehicleCompatibilityCommand.php
@@ -0,0 +1,373 @@
+em = $om;
+
+ // load existing battery data
+ $this->loadBatteryManufacturers();
+ $this->loadBatteryModels();
+ $this->loadBatterySizes();
+ $this->loadBatteries();
+
+ // load existing vehicle data
+ $this->loadVehicleManufacturers();
+ $this->loadVehicleMakes();
+
+ parent::__construct();
+ }
+
+ protected function configure()
+ {
+ $this->setName('cmbbatteryvehiclecompatibility:import')
+ ->setDescription('Retrieve from a CSV file vehicle and battery compatibility information.')
+ ->setHelp('Creates vehicles and their compatible batteries based on data from imported CSV.')
+ ->addArgument('file', InputArgument::REQUIRED, 'Path to the CSV file.')
+ ->addArgument('output_file', InputArgument::REQUIRED, 'Path to output file for vehicles not added.');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ // get the sizes, vehicles, battery prices from the csv file
+ $csv_file = $input->getArgument('file');
+
+ // attempt to open file
+ try
+ {
+ $fh = fopen($csv_file, "r");
+ }
+ catch (Exception $e)
+ {
+ throw new Exception('The file "' . $csv_file . '" could be read.');
+ }
+
+ $output_file = $input->getArgument('output_file');
+
+ // attempt to open file
+ try
+ {
+ $output_fh = fopen($output_file, "w");
+ }
+ catch (Exception $e)
+ {
+ throw new Exception('The file "' . $output_file . '" could be read.');
+ }
+
+
+ // get entity manager
+ $em = $this->em;
+
+ $row_num = 0;
+ error_log('Processing vehicle and battery compatibility file...');
+ while (($fields = fgetcsv($fh)) !== false)
+ {
+ $comp_batteries = [];
+ if ($row_num < 2)
+ {
+ $row_num++;
+ continue;
+ }
+
+ // get vehicle info from file
+ $manufacturer = trim(strtolower($fields[self::F_VEHICLE_MANUFACTURER]));
+ $make = trim(strtolower($fields[self::F_VEHICLE_MAKE]));
+ $year = trim($fields[self::F_VEHICLE_YEAR]);
+
+ // get battery info from file
+ $marathoner_size = $this->normalizeName(trim($fields[self::F_BATT_MARATHONER_SIZE]));
+ $marathoner_price = trim($fields[self::F_BATT_MARATHONER_PRICE]);
+ $marathoner_tradein_price = trim($fields[self::F_BATT_MARATHONER_TRADEIN_PRICE]);
+
+ $classic_size = $this->normalizeName(trim($fields[self::F_BATT_CLASSIC_SIZE]));
+ $classic_price = trim($fields[self::F_BATT_CLASSIC_PRICE]);
+ $classic_tradein_price = trim($fields[self::F_BATT_CLASSIC_TRADEIN_PRICE]);
+
+ $excel_size = $this->normalizeName(trim($fields[self::F_BATT_EXCEL_SIZE]));
+ $excel_price = trim($fields[self::F_BATT_EXCEL_PRICE]);
+ $excel_tradein_price = trim($fields[self::F_BATT_EXCEL_TRADEIN_PRICE]);
+
+ $sdfc_size = $this->normalizeName(trim($fields[self::F_BATT_SDFC_SIZE]));
+ $sdfc_price = trim($fields[self::F_BATT_SDFC_PRICE]);
+ $sdfc_tradein_price = trim($fields[self::F_BATT_SDFC_TRADEIN_PRICE]);
+
+ // get the compatible batteries
+ // get marathoner battery
+ if (strlen($marathoner_size) > 0)
+ {
+ // check if battery in system
+ if (isset($this->batt_hash[self::STR_CENTURY][self::STR_MARATHONER][$marathoner_size]))
+ $comp_batteries[] = $this->batt_hash[self::STR_CENTURY][self::STR_MARATHONER][$marathoner_size];
+ }
+
+ // get classic battery
+ if (strlen($classic_size) > 0)
+ {
+ // check if battery in system
+ if (isset($this->batt_hash[self::STR_MOTOLITE][self::STR_CLASSIC][$classic_size]))
+ $comp_batteries[] = $this->batt_hash[self::STR_MOTOLITE][self::STR_CLASSIC][$classic_size];
+ }
+
+ // get excel battery
+ if (strlen($excel_size) > 0)
+ {
+ // check if battery in system
+ if (isset($this->batt_hash[self::STR_CENTURY][self::STR_EXCEL][$excel_size]))
+ $comp_batteries[] = $this->batt_hash[self::STR_CENTURY][self::STR_EXCEL][$excel_size];
+ }
+
+ // get sdfc battery
+ if (strlen($sdfc_size) > 0)
+ {
+ // check if battery in system
+ if (isset($this->batt_hash[self::STR_CENTURY][self::STR_SDFC][$sdfc_size]))
+ $comp_batteries[] = $this->batt_hash[self::STR_CENTURY][self::STR_SDFC][$sdfc_size];
+ }
+
+ // check if vehicle manufacturer has been added
+ if (!isset($this->vmanu_hash[$manufacturer]))
+ $this->addVehicleManufacturer($manufacturer);
+
+ // check if vehicle make has been added
+ if (!isset($this->vmake_hash[$manufacturer][$make]))
+ {
+ $this->addVehicleMake($manufacturer, $make, $year, $comp_batteries);
+ }
+
+ $row_num++;
+ }
+
+ $em->flush();
+ }
+
+ protected function addVehicleManufacturer($name)
+ {
+ // save to db
+ $vehicle_manufacturer = new VehicleManufacturer();
+
+ $vehicle_manufacturer->setName(strtoupper($name));
+
+ $this->em->persist($vehicle_manufacturer);
+ $this->em->flush();
+
+ // add to hash
+ $this->vmanu_hash[$name] = $vehicle_manufacturer;
+ }
+
+ protected function addVehicleMake($manufacturer, $make, $year, $batteries)
+ {
+ // save to db
+ $vehicle = new Vehicle();
+
+ $vmanu = $this->vmanu_hash[$manufacturer];
+
+ // parse year from and year to
+ $year_from = '';
+ $year_to = '';
+
+ if (!empty($year))
+ {
+ $model_years = explode('-', $year);
+ $year_from = $model_years[0];
+ if (!empty($year_to))
+ $year_to = $model_years[1];
+
+ // check if $year_to is the string "Present"
+ // if so, set to 0, for now
+ if ($year_to == self::STR_PRESENT)
+ $year_to = 0;
+ }
+
+ $vehicle->setManufacturer($vmanu)
+ ->setMake(strtoupper($make))
+ ->setModelYearFrom($year_from)
+ ->setModelYearTo($year_to);
+
+ // add vehicle to battery
+ foreach ($batteries as $battery)
+ {
+ $battery->addVehicle($vehicle);
+ $this->em->persist($battery);
+ }
+
+ // add vehicle to manufacturer
+ $vmanu->addVehicle($vehicle);
+
+ $this->em->persist($vmanu);
+ $this->em->persist($vehicle);
+ $this->em->flush();
+
+ // add to hash
+ $this->vmake_hash[$manufacturer][$make] = $vehicle;
+ }
+
+ protected function loadBatteryManufacturers()
+ {
+ $this->bmanu_hash = [];
+
+ $batt_manufacturers = $this->em->getRepository(BatteryManufacturer::class)->findAll();
+ foreach ($batt_manufacturers as $batt_manu)
+ {
+ $name = $this->normalizeName($batt_manu->getName());
+ $this->bmanu_hash[$name] = $batt_manu;
+ }
+ }
+
+ protected function loadBatteryModels()
+ {
+ $this->bmodel_hash = [];
+
+ $batt_models = $this->em->getRepository(BatteryModel::class)->findAll();
+ foreach ($batt_models as $batt_model)
+ {
+ $name = $this->normalizeName($batt_model->getName());
+ $this->bmodel_hash[$name] = $batt_model;
+ }
+ }
+
+ protected function loadBatterySizes()
+ {
+ $this->bsize_hash = [];
+
+ $batt_sizes = $this->em->getRepository(BatterySize::class)->findAll();
+ foreach ($batt_sizes as $batt_size)
+ {
+ $name = $this->normalizeName($batt_size->getName());
+ $this->bsize_hash[$name] = $batt_size;
+ }
+ }
+
+ protected function loadBatteries()
+ {
+ $this->batt_hash = [];
+
+ $batts = $this->em->getRepository(Battery::class)->findAll();
+ foreach ($batts as $batt)
+ {
+ $brand = $this->normalizeName($batt->getManufacturer()->getName());
+ $model = $this->normalizeName($batt->getModel()->getName());
+ $size = $this->normalizeName($batt->getSize()->getName());
+
+ $this->batt_hash[$brand][$model][$size] = $batt;
+ }
+ }
+
+ protected function loadVehicleManufacturers()
+ {
+ $this->vmanu_hash = [];
+
+ $vmanus = $this->em->getRepository(VehicleManufacturer::class)->findAll();
+ foreach ($vmanus as $vmanu)
+ {
+ $name = $this->normalizeName($vmanu->getName());
+ $this->vmanu_hash[$name] = $vmanu;
+ }
+ }
+
+ protected function loadVehicleMakes()
+ {
+ $this->vmake_hash = [];
+
+ $vmakes = $this->em->getRepository(Vehicle::class)->findAll();
+ foreach ($vmakes as $vmake)
+ {
+ $manufacturer = $vmake->getManufacturer()->getName();
+ $make = $this->normalizeName($vmake->getMake());
+
+ $this->vmake_hash[$manufacturer][$make] = $vmake;
+ }
+ }
+
+ protected function normalizeName($name)
+ {
+ // check if name contains M-42. Need to convert to M42
+ if (strpos($name, self::STR_M_42) !== false)
+ {
+ // contains M-42
+ $changed_name = str_replace(self::STR_M_42, self::STR_M42, $name);
+ $normalized_key = strtolower($changed_name);
+ }
+ else
+ {
+ $normalized_key = trim(strtolower($name));
+ }
+
+ $normalized_key = trim(strtolower($name));
+
+ return $normalized_key;
+ }
+}
diff --git a/src/Command/ImportCMBLegacyJobOrderCommand.php b/src/Command/ImportCMBLegacyJobOrderCommand.php
new file mode 100644
index 00000000..e6726f25
--- /dev/null
+++ b/src/Command/ImportCMBLegacyJobOrderCommand.php
@@ -0,0 +1,164 @@
+em = $em;
+
+ parent::__construct();
+ }
+
+ public function configure()
+ {
+ $this->setName('cmblegacyjoborderdata:import')
+ ->setDescription('Retrieve from a CSV file CarFix data.')
+ ->setHelp('Creates legacy job order entries based on data from imported CSV.')
+ ->addArgument('file', InputArgument::REQUIRED, 'Path to the CSV file.');
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ $csv_file = $input->getArgument('file');
+
+ // attempt to open file
+ try
+ {
+ $fh = fopen($csv_file, "r");
+ }
+ catch (Exception $e)
+ {
+ throw new Exception('The file "' . $csv_file . '" could be read.');
+ }
+
+ // get entity manager
+ $em = $this->em;
+
+ $row_num = 0;
+ error_log('Processing CarFix data csv file...');
+ while (($fields = fgetcsv($fh)) !== false)
+ {
+ if ($row_num < 1)
+ {
+ $row_num++;
+ continue;
+ }
+
+ // get the information
+ $entry_num = trim($fields[self::F_INDEX]);
+ $date_create = trim($fields[self::F_CREATED_DATE]);
+ $case_number = trim($fields[self::F_CASE_NO]);
+ $insurer = trim($fields[self::F_INSURER]);
+ $vehicle_number = trim($fields[self::F_VEHICLE_NO]);
+ $car_model = trim($fields[self::F_CAR_MODEL]);
+ $nature_of_call = trim($fields[self::F_NATURE_OF_CALL]);
+ $service_needed = trim($fields[self::F_SERVICE_NEEDED]);
+ $location = trim($fields[self::F_LOCATION]);
+ $state = trim($fields[self::F_STATE]);
+ $driver = trim($fields[self::F_DRIVER]);
+ $truck = trim($fields[self::F_TRUCK]);
+ $workshop_arrival_time = trim($fields[self::F_WORKSHOP_ARRIVAL_TIME]);
+ $status = trim($fields[self::F_STATUS]);
+ $customer_name = trim($fields[self::F_CUSTOMER_NAME]);
+ $customer_mobile = trim($fields[self::F_CUSTOMER_PHONE_NO]);
+ $reference = trim($fields[self::F_OUR_REFERENCE]);
+ $odometer = trim($fields[self::F_ODOMETER]);
+ $batt_model = trim($fields[self::F_BATT_MODEL]);
+ $batt_size = trim($fields[self::F_BATT_SIZE]);
+ $trade_in = trim($fields[self::F_BATT_TRADE_IN]);
+ $replaced_by = trim($fields[self::F_REPLACED_BY]);
+ $remark = trim($fields[self::F_REMARK]);
+ $satisfaction = trim($fields[self::F_SATISFACTION]);
+
+ $data_entry = $this->processEntry($entry_num, $date_create, $case_number, $insurer, $vehicle_number, $car_model,
+ $nature_of_call, $service_needed, $location, $state, $driver, $truck, $workshop_arrival_time,
+ $status, $customer_name, $customer_mobile, $reference, $odometer, $batt_model, $batt_size,
+ $trade_in, $replaced_by, $remark, $satisfaction);
+
+ $legacy_data = new CMBLegacyJobOrderRow();
+ $legacy_data->setData($data_entry);
+
+ $this->em->persist($legacy_data);
+ }
+
+ $this->em->flush();
+ $this->em->clear();
+
+ return 0;
+ }
+
+ protected function processEntry($entry_num, $date_create, $case_number, $insurer, $vehicle_number, $car_model,
+ $nature_of_call, $service_needed, $location, $state, $driver, $truck, $workshop_arrival_time,
+ $status, $customer_name, $customer_mobile, $reference, $odometer, $batt_model, $batt_size,
+ $trade_in, $replaced_by, $remark, $satisfaction)
+ {
+ $data_entry = [
+ 'entry_num' => $entry_num,
+ 'created_date' => $date_create,
+ 'case_number' => $case_number,
+ 'insurer' => $insurer,
+ 'vehicle_number' => $vehicle_number,
+ 'car_model' => $car_model,
+ 'nature_of_call' => $nature_of_call,
+ 'service_needed' => $service_needed,
+ 'location' => $location,
+ 'state' => $state,
+ 'driver' => $driver,
+ 'truck' => $truck,
+ 'workshop_arrival_time' => $workshop_arrival_time,
+ 'status' => $status,
+ 'customer_name' => $customer_name,
+ 'customer_phone_number' => $customer_mobile,
+ 'reference' => $reference,
+ 'odometer' => $odometer,
+ 'batt_model' => $batt_model,
+ 'batt_size' => $batt_size,
+ 'batt_trade_in' => $trade_in,
+ 'replaced_by' => $replaced_by,
+ 'remark' => $remark,
+ 'satisfaction' => $satisfaction,
+ ];
+
+ return $data_entry;
+ }
+
+}
diff --git a/src/Command/ImportCMBVehicleCompatibilityCommand.php b/src/Command/ImportCMBVehicleCompatibilityCommand.php
deleted file mode 100644
index 4034aec8..00000000
--- a/src/Command/ImportCMBVehicleCompatibilityCommand.php
+++ /dev/null
@@ -1,449 +0,0 @@
-em = $om;
-
- // load existing battery data
- $this->loadBatteryManufacturers();
- $this->loadBatteryModels();
- $this->loadBatterySizes();
- $this->loadBatteries();
-
- // load existing vehicle data
- $this->loadVehicleManufacturers();
- $this->loadVehicleMakes();
-
- parent::__construct();
- }
-
- protected function configure()
- {
- $this->setName('cmbvehiclecompatibility:import')
- ->setDescription('Retrieve from a CSV file battery and vehicle information.')
- ->setHelp('Creates battery manufacturers, models, sizes, vehicle makes, and models based on data from imported CSV.')
- ->addArgument('file', InputArgument::REQUIRED, 'Path to the CSV file.');
- }
-
- protected function execute(InputInterface $input, OutputInterface $output)
- {
- $csv_file = $input->getArgument('file');
-
- // attempt to open file
- try
- {
- $fh = fopen($csv_file, "r");
- }
- catch (Exception $e)
- {
- throw new Exception('The file "' . $csv_file . '" could be read.');
- }
-
- // get entity manager
- $em = $this->em;
-
- $row_num = 0;
- error_log('Processing vehicle compatibility csv file...');
- while (($fields = fgetcsv($fh)) !== false)
- {
- $comp_batteries = [];
- if ($row_num < 2)
- {
- $row_num++;
- continue;
- }
-
- // initialize size battery array for cases where the battery size has '/'
- $sdfc_sizes = [];
- $ultramax_sizes = [];
- $motolite_sizes = [];
- $marathoner_sizes = [];
- $excel_sizes = [];
-
- // battery info
- $sdfc_size = trim($fields[self::F_BATT_SDFC]);
- $ultramax_size = trim($fields[self::F_BATT_ULTRAMAX]);
- $motolite_size = trim($fields[self::F_BATT_MOTOLITE]);
- $marathoner_size = trim($fields[self::F_BATT_MARATHONER]);
- $excel_size = trim($fields[self::F_BATT_EXCEL]);
-
- // check the sizes for '/'
- $pos = stripos($sdfc_size, '/');
- if ($pos == false)
- {
- // no '/' in size
- $sdfc_sizes[] = $this->normalizeName($sdfc_size);
- }
- else
- {
- // we have '/' in size so we have to explode
- $sizes = explode('/', $sdfc_size);
- foreach ($sizes as $size)
- {
- $sdfc_sizes[] = $this->normalizeName($size);
- }
- }
-
- $pos = stripos($motolite_size, '/');
- if ($pos == false)
- {
- // no '/' in size
- $motolite_sizes[] = $this->normalizeName($motolite_size);
- }
- else
- {
- // we have '/' in size so we have to explode
- $sizes = explode('/', $motolite_size);
- foreach ($sizes as $size)
- {
- $motolite_sizes[] = $this->normalizeName($size);
- }
- }
-
- $pos = stripos($marathoner_size, '/');
- if ($pos == false)
- {
- // no '/' in size
- $marathoner_sizes[] = $this->normalizeName($marathoner_size);
- }
- else
- {
- // we have '/' in size so we have to explode
- $sizes = explode('/', $marathoner_size);
- foreach ($sizes as $size)
- {
- $marathoner_sizes[] = $this->normalizeName($size);
- }
- }
-
- $pos = stripos($ultramax_size, '/');
- if ($pos == false)
- {
- // no '/' in size
- $ultramax_sizes[] = $this->normalizeName($ultramax_size);
- }
- else
- {
- // we have '/' in size so we have to explode
- $sizes = explode('/', $ultramax_size);
- foreach ($sizes as $size)
- {
- $ultramax_sizes[] = $this->normalizeName($size);
- }
- }
-
- $pos = stripos($excel_size, '/');
- if ($pos == false)
- {
- // no '/' in size
- $excel_sizes[] = $this->normalizeName($excel_size);
- }
- else
- {
- // we have '/' in size so we have to explode
- $sizes = explode('/', $excel_size);
- foreach ($sizes as $size)
- {
- $excel_sizes[] = $this->normalizeName($size);
- }
- }
-
-
- // normalize the battery manufacturers and battery models
- $norm_century = $this->normalizeName(self::STR_CENTURY);
- $norm_sdfc = $this->normalizeName(self::STR_SDFC);
- $norm_motolite = $this->normalizeName(self::STR_MOTOLITE);
- $norm_wetcharged = $this->normalizeName(self::STR_WETCHARGED);
- $norm_marathoner = $this->normalizeName(self::STR_MARATHONER);
- $norm_ultramax = $this->normalizeName(self::STR_ULTRAMAX);
- $norm_excel = $this->normalizeName(self::STR_EXCEL);
-
- //foreach($sdfc_sizes as $size)
- //{
- // error_log('sdfc size ' . $size);
- //}
- //foreach($motolite_sizes as $size)
- //{
- // error_log('motolite size ' . $size);
- //}
- //foreach($marathoner_sizes as $size)
- //{
- // error_log('marathoner size ' . $size);
- //}
-
- // vehicle info
- $manufacturer = trim($fields[self::F_VEHICLE_MANUFACTURER]);
- $make = trim($fields[self::F_VEHICLE_MAKE]);
- $year = trim($fields[self::F_VEHICLE_YEAR]);
-
- // vehicle data
- // check if vehicle manufacturer has been added
- if (!isset($this->vmanu_hash[$manufacturer]))
- $this->addVehicleManufacturer($manufacturer);
-
- // check if vehicle make has been added
- if (!isset($this->vmake_hash[$manufacturer][$make]))
- {
- foreach($sdfc_sizes as $size)
- {
- if (!(empty($size)))
- {
- if (isset($this->batt_hash[$norm_century][$norm_sdfc][$size]))
- $comp_batteries[] = $this->batt_hash[$norm_century][$norm_sdfc][$size];
- else
- error_log('Not in the system: ' . $norm_century . ' ' . $norm_sdfc . ' ' . $size);
- }
- }
- foreach($ultramax_sizes as $size)
- {
- if (!(empty($size)))
- {
- if (isset($this->batt_hash[$norm_ultramax][$norm_ultramax][$size]))
- $comp_batteries[] = $this->batt_hash[$norm_ultramax][$norm_ultramax][$size];
- else
- error_log('Not in the system: ' . $norm_ultramax . ' ' . $norm_ultramax . ' ' . $size);
- }
- }
- foreach($motolite_sizes as $size)
- {
- if (!(empty($size)))
- {
- if (isset($this->batt_hash[$norm_motolite][$norm_wetcharged][$size]))
- $comp_batteries[] = $this->batt_hash[$norm_motolite][$norm_wetcharged][$size];
- else
- error_log('Not in the system: ' . $norm_motolite . ' ' . $norm_wetcharged . ' ' . $size);
- }
- }
- foreach($marathoner_sizes as $size)
- {
- if (!(empty($size)))
- {
- if (isset($this->batt_hash[$norm_century][$norm_marathoner][$size]))
- $comp_batteries[] = $this->batt_hash[$norm_century][$norm_marathoner][$size];
- else
- error_log('Not in the system: ' . $norm_century . ' ' . $norm_marathoner . ' ' . $size);
- }
- }
- foreach($excel_sizes as $size)
- {
- if (!(empty($size)))
- {
- if (isset($this->batt_hash[$norm_excel][$norm_excel][$size]))
- $comp_batteries[] = $this->batt_hash[$norm_excel][$norm_excel][$size];
- else
- error_log('Not in the system: ' . $norm_excel . ' ' . $norm_excel . ' ' . $size);
- }
- }
- $this->addVehicleMake($manufacturer, $make, $year, $comp_batteries);
- }
-
- $row_num++;
- }
-
- return 0;
- }
-
- protected function addVehicleManufacturer($name)
- {
- // save to db
- $vehicle_manufacturer = new VehicleManufacturer();
-
- $vehicle_manufacturer->setName($name);
-
- $this->em->persist($vehicle_manufacturer);
- $this->em->flush();
-
- // add to hash
- $this->vmanu_hash[$name] = $vehicle_manufacturer;
- }
-
- protected function addVehicleMake($manufacturer, $make, $year, $batteries)
- {
- // save to db
- $vehicle = new Vehicle();
-
- $vmanu = $this->vmanu_hash[$manufacturer];
-
- // parse year from and year to
- $year_from = '';
- $year_to = '';
-
- if (!empty($year))
- {
- $model_years = explode('-', $year);
- $year_from = $model_years[0];
- if (!empty($year_to))
- $year_to = $model_years[1];
-
- // check if $year_to is the string "Present"
- // if so, set to 0, for now
- if ($year_to == self::STR_PRESENT)
- $year_to = 0;
- }
-
- $vehicle->setManufacturer($vmanu)
- ->setMake($make)
- ->setModelYearFrom($year_from)
- ->setModelYearTo($year_to);
-
- // add vehicle to battery
- foreach ($batteries as $battery)
- {
- $battery->addVehicle($vehicle);
- $this->em->persist($battery);
- }
-
- // add vehicle to manufacturer
- $vmanu->addVehicle($vehicle);
-
- $this->em->persist($vmanu);
- $this->em->persist($vehicle);
- $this->em->flush();
-
- // add to hash
- $this->vmake_hash[$manufacturer][$make] = $vehicle;
- }
-
- protected function loadBatteryManufacturers()
- {
- $this->bmanu_hash = [];
-
- $batt_manufacturers = $this->em->getRepository(BatteryManufacturer::class)->findAll();
- foreach ($batt_manufacturers as $batt_manu)
- {
- $name = $this->normalizeName($batt_manu->getName());
- $this->bmanu_hash[$name] = $batt_manu;
- }
- }
-
- protected function loadBatteryModels()
- {
- $this->bmodel_hash = [];
-
- $batt_models = $this->em->getRepository(BatteryModel::class)->findAll();
- foreach ($batt_models as $batt_model)
- {
- $name = $this->normalizeName($batt_model->getName());
- $this->bmodel_hash[$name] = $batt_model;
- }
- }
-
- protected function loadBatterySizes()
- {
- $this->bsize_hash = [];
-
- $batt_sizes = $this->em->getRepository(BatterySize::class)->findAll();
- foreach ($batt_sizes as $batt_size)
- {
- $name = $this->normalizeName($batt_size->getName());
- $this->bsize_hash[$name] = $batt_size;
- }
- }
-
- protected function loadBatteries()
- {
- $this->batt_hash = [];
-
- $batts = $this->em->getRepository(Battery::class)->findAll();
- foreach ($batts as $batt)
- {
- $brand = $this->normalizeName($batt->getManufacturer()->getName());
- $model = $this->normalizeName($batt->getModel()->getName());
- $size = $this->normalizeName($batt->getSize()->getName());
-
- $this->batt_hash[$brand][$model][$size] = $batt;
- }
- }
-
- protected function loadVehicleManufacturers()
- {
- $this->vmanu_hash = [];
-
- $vmanus = $this->em->getRepository(VehicleManufacturer::class)->findAll();
- foreach ($vmanus as $vmanu)
- {
- $name = $vmanu->getName();
- $this->vmanu_hash[$name] = $vmanu;
- }
- }
-
- protected function loadVehicleMakes()
- {
- $this->vmake_hash = [];
-
- $vmakes = $this->em->getRepository(Vehicle::class)->findAll();
- foreach ($vmakes as $vmake)
- {
- $manufacturer = $vmake->getManufacturer()->getName();
- $make = $vmake->getMake();
-
- $this->vmake_hash[$manufacturer][$make] = $vmake;
- }
- }
-
- protected function normalizeName($name)
- {
- // check for M-42. Need to convert to M42
- if (strcasecmp($name, self::STR_M_42) == 0)
- {
- $normalized_key = strtolower(self::STR_M42);
- }
- else
- {
- $normalized_key = trim(strtolower($name));
- }
-
- return $normalized_key;
- }
-
-}
diff --git a/src/Command/MigrateCMBLegacyJobOrderCommand.php b/src/Command/MigrateCMBLegacyJobOrderCommand.php
new file mode 100644
index 00000000..11da2652
--- /dev/null
+++ b/src/Command/MigrateCMBLegacyJobOrderCommand.php
@@ -0,0 +1,563 @@
+ 'workshop_arrival_time'
+ status = 'status'
+ cust_name = 'customer_name'
+ cust_mobile = 'customer_phone_number'
+ reference = 'reference'
+ odometer = 'odometer'
+ batt_model = 'batt_model'
+ batt_size = 'batt_size'
+ is_trade_in = 'batt_trade_in'
+ replaced_by = 'replaced_by'
+ remarks = 'remark'
+ satisfaction => 'satisfaction'
+ */
+
+ const STR_LAST_NAME = 'LEGACY';
+
+ protected $em;
+
+ protected $bmanu_hash;
+ protected $bmodel_hash;
+ protected $bsize_hash;
+ protected $batt_hash;
+
+ protected $vmanu_hash;
+ protected $vmake_hash;
+
+ protected $cv_hash;
+
+ public function __construct(EntityManagerInterface $em)
+ {
+ $this->em = $em;
+
+ // load existing battery data
+ $this->loadBatteryManufacturers();
+ $this->loadBatteryModels();
+ $this->loadBatterySizes();
+ $this->loadBatteries();
+
+ // load existing vehicle data
+ $this->loadVehicleManufacturers();
+ $this->loadVehicleMakes();
+
+ // load existing customer vehicle data
+ $this->loadCustomerVehicles();
+
+ parent::__construct();
+ }
+
+ public function configure()
+ {
+ $this->setName('cmblegacyjoborderdata:migrate')
+ ->setDescription('Retrieve database cmb legacy job orders and insert into job order.')
+ ->setHelp('Creates job orders based on data from imported CSV.')
+ ->addArgument('output_file', InputArgument::REQUIRED, 'Path to the output CSV file of the entries not added.');
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ $csv_file = $input->getArgument('output_file');
+
+ // attempt to open file
+ try
+ {
+ $fh = fopen($csv_file, "w");
+ }
+ catch (Exception $e)
+ {
+ throw new Exception('The file "' . $csv_file . '" could be opened.');
+ }
+
+ // get all legacy job orders
+ $query = $this->em->createQuery('SELECT legacy_jo_row FROM App\Entity\CMBLegacyJobOrderRow legacy_jo_row');
+ $result = $query->iterate();
+
+ $invalid_entries = [];
+ $added_entries = 0;
+ $total_entries = 0;
+ $total_inv_entries = 0;
+ foreach ($result as $row)
+ {
+ $entry = $row[0];
+ $jo_entry = $entry->getData();
+
+ $total_entries++;
+
+ // get the entry information
+ $entry_num = $jo_entry['entry_num'];
+ $date_create = $jo_entry['created_date'];
+ $case_number = $jo_entry['case_number'];
+ $insurer = $jo_entry['insurer'];
+ $plate_number = $this->cleanPlateNumber($jo_entry['vehicle_number']);
+ $car_model = $this->normalizeName($jo_entry['car_model']);
+ $nature_of_call = $jo_entry['nature_of_call'];
+ $service_needed = $jo_entry['service_needed'];
+ $location = $jo_entry['location'];
+ $state = $jo_entry['state'];
+ $driver = $jo_entry['driver'];
+ $truck = $jo_entry['truck'];
+ $workshop_arrival_time = $jo_entry['workshop_arrival_time'];
+ $status = $jo_entry['status'];
+ $customer_name = $jo_entry['customer_name'];
+ $customer_mobile = $jo_entry['customer_phone_number'];
+ $reference = $jo_entry['reference'];
+ $odometer = $jo_entry['odometer'];
+ $batt_model = $this->normalizeName($jo_entry['batt_model']);
+ $batt_size = $this->normalizeName($jo_entry['batt_size']);
+ $batt_trade_in = $jo_entry['batt_trade_in'];
+ $replaced_by = $jo_entry['replaced_by'];
+ $remark = $jo_entry['remark'];
+ $satisfaction = $jo_entry['satisfaction'];
+
+ // check vehicle info if valid
+ $v_status = $this->processVehicleInfo($car_model);
+ if ($v_status != null)
+ {
+ //error_log($v_status . ' ' . $car_model);
+ $invalid_entries[] = $this->addInvalidEntry($entry_num, $date_create, $case_number, $insurer, $plate_number, $car_model,
+ $nature_of_call, $service_needed, $location, $state, $driver, $truck, $workshop_arrival_time,
+ $status, $customer_name, $customer_mobile, $reference, $odometer, $batt_model, $batt_size,
+ $batt_trade_in, $replaced_by, $remark, $satisfaction, $v_status);
+
+ $total_inv_entries++;
+ // move to next entry
+ continue;
+ }
+
+ // check battery info if valid
+ $batt_status = $this->processBatteryInfo($batt_model, $batt_size);
+ if ($batt_status != null)
+ {
+ //error_log($batt_status . ' ' . $batt_model . ' ' . $batt_size);
+ $invalid_entries[] = $this->addInvalidEntry($entry_num, $date_create, $case_number, $insurer, $plate_number, $car_model,
+ $nature_of_call, $service_needed, $location, $state, $driver, $truck, $workshop_arrival_time,
+ $status, $customer_name, $customer_mobile, $reference, $odometer, $batt_model, $batt_size,
+ $batt_trade_in, $replaced_by, $remark, $satisfaction, $batt_status);
+
+ $total_inv_entries++;
+ // move to next entry
+ continue;
+ }
+
+ // check if mobile phone number has letters or spaces or special characters
+ if (!(preg_match('/^\d+$/', $customer_mobile)))
+ {
+ // not a valid mobile number
+ //error_log('invalid number ' . $customer_mobile);
+ $invalid_entries[] = $this->addInvalidEntry($entry_num, $date_create, $case_number, $insurer, $plate_number, $car_model,
+ $nature_of_call, $service_needed, $location, $state, $driver, $truck, $workshop_arrival_time,
+ $status, $customer_name, $customer_mobile, $reference, $odometer, $batt_model, $batt_size,
+ $batt_trade_in, $replaced_by, $remark, $satisfaction, 'Invalid mobile number');
+
+ $total_inv_entries++;
+ // move to next entry
+ continue;
+ }
+
+ // create job order
+ $cmb_legacy_jo = new CMBLegacyJobOrder();
+
+ // date_create
+ $created_date = DateTime::createFromFormat("d-m-Y H:i", $date_create);
+ $cmb_legacy_jo->setTransDate($created_date);
+
+ // parse vehicle info
+ // get vehicle from hash
+ $v_array = explode(' ', $car_model);
+
+ // manufacturer
+ $v_manufacturer = trim($v_array[0]);
+
+ // get model
+ $model_info = '';
+ $v_model = '';
+ for ($i = 1; $i < count($v_array); $i++)
+ {
+ $model_info = $model_info . ' ' . trim($v_array[$i]);
+ }
+
+ $v_model = trim($model_info);
+
+ // delivery address = location + state
+ $delivery_address = $location . ', ' . $state;
+
+ // check if trade in
+ $trade_in = false;
+ if (strtolower($batt_trade_in) == 'yes')
+ $trade_in = true;
+
+ $cmb_legacy_jo->setCaseNumber($case_number)
+ ->setInsurer($insurer)
+ ->setNatureOfCall($nature_of_call)
+ ->setTransType($service_needed)
+ ->setCarBrand(strtoupper($v_manufacturer))
+ ->setCarMake(strtoupper($v_model))
+ ->setCustName($customer_name)
+ ->setCustMobile($customer_mobile)
+ ->setAddress($delivery_address)
+ ->setDriver($driver)
+ ->setTruck($truck)
+ ->setWorkshopArrivalTime($workshop_arrival_time)
+ ->setPlateNumber($plate_number)
+ ->setStatus($status)
+ ->setReference($reference)
+ ->setOdometer($odometer)
+ ->setBatteryModel(strtoupper($batt_model))
+ ->setBatterySize(strtoupper($batt_size))
+ ->setIsTradeIn($trade_in)
+ ->setReplacedBy($replaced_by)
+ ->setSatisfaction($satisfaction);
+
+ // plate number == vehicle_number. Use as key to cv_hash
+ // check if plate number has been added
+ if (!isset($this->cv_hash[$plate_number]))
+ {
+ $cust = $this->addCustomer($customer_name, $customer_mobile);
+ $cv = $this->addCustomerVehicle($plate_number, $car_model, $cust);
+ }
+
+ $this->em->persist($cmb_legacy_jo);
+ $this->em->detach($row[0]);
+
+ $added_entries++;
+ }
+
+ $this->em->flush();
+ $this->em->clear();
+
+ // output the entries that were not added
+ if (count($invalid_entries) > 0)
+ {
+ fputcsv($fh, [
+ 'Entry Num',
+ 'Created Date',
+ 'Case Number',
+ 'Insurer',
+ 'Vehicle Number',
+ 'Car Model',
+ 'Nature of Call',
+ 'Service Needed',
+ 'Location',
+ 'State',
+ 'Driver',
+ 'Truck',
+ 'Workshop Arrival Time',
+ 'Status',
+ 'Customer Name',
+ 'Customer Phone Number',
+ 'Reference',
+ 'Odometer',
+ 'Batt Model',
+ 'Batt Size',
+ 'Batt Trade In',
+ 'Replaced By',
+ 'Remark',
+ 'Satisfaction',
+ 'Reason',
+
+ ]);
+
+ foreach($invalid_entries as $row)
+ {
+ fputcsv($fh, $row);
+ }
+ }
+
+ fclose($fh);
+
+ error_log('Processed ' . $total_entries . ' entries.');
+ error_log('Added ' . $added_entries . ' entries.');
+ error_log('Did not add ' . $total_inv_entries . ' entries.');
+
+ return 0;
+ }
+
+ protected function processVehicleInfo($car_model)
+ {
+ // vehicle manufacturer is the first entry
+ // vehicle model is the 2nd entry + whatever follows
+ $v_array = explode(' ', $car_model);
+
+ // manufacturer
+ $v_manufacturer = trim($v_array[0]);
+
+ // get model
+ $model_info = '';
+ $v_model = '';
+ for ($i = 1; $i < count($v_array); $i++)
+ {
+ $model_info = $model_info . ' ' . trim($v_array[$i]);
+ }
+
+ $v_model = trim($model_info);
+ //error_log($v_manufacturer . ' ' . $v_model);
+
+ // check if manufacturer is in hash
+ if (!isset($this->vmanu_hash[$v_manufacturer]))
+ {
+ //error_log($v_manufacturer . ' invalid.');
+ return 'Vehicle manufacturer not in system.';
+ }
+
+ // check if manufacturer and make is in hash
+ if (!isset($this->vmake_hash[$v_manufacturer][$v_model]))
+ {
+ //error_log($v_model . ' invalid.');
+ return 'Vehicle model not in system.';
+ }
+ // car model info valid
+ return null;
+ }
+
+ protected function processBatteryInfo($batt_model, $batt_size)
+ {
+ // check if battery model is in hash
+ if (!isset($this->bmodel_hash[$batt_model]))
+ return 'Battery model not in system.';
+
+ // check if battery size is in hash
+ if (!isset($this->bsize_hash[$batt_size]))
+ return 'Battery size not in system.';
+
+ // battery info valid
+ return null;
+ }
+
+ protected function addInvalidEntry($entry_num, $date_create, $case_number, $insurer, $plate_number, $car_model,
+ $nature_of_call, $service_needed, $location, $state, $driver, $truck, $workshop_arrival_time,
+ $status, $customer_name, $customer_mobile, $reference, $odometer, $batt_model, $batt_size,
+ $batt_trade_in, $replaced_by, $remark, $satisfaction, $v_status)
+ {
+ $inv_entry = [
+ 'number' => $entry_num,
+ 'created_date' => $date_create,
+ 'case_number' => $case_number,
+ 'insurer' => $insurer,
+ 'vehicle_number' => $plate_number,
+ 'car_model' => $car_model,
+ 'nature_of_call' => $nature_of_call,
+ 'service_needed' => $service_needed,
+ 'location' => $location,
+ 'state' => $state,
+ 'driver' => $driver,
+ 'truck' => $truck,
+ 'workshop_arrival_time' => $workshop_arrival_time,
+ 'status' => $status,
+ 'customer_name' => $customer_name,
+ 'customer_phone_number' => $customer_mobile,
+ 'reference' => $reference,
+ 'odometer' => $odometer,
+ 'batt_model' => $batt_model,
+ 'batt_size' => $batt_size,
+ 'batt_trade_in' => $batt_trade_in,
+ 'replaced_by' => $replaced_by,
+ 'remark' => $remark,
+ 'satisfaction' => $satisfaction,
+ 'reason' => $v_status,
+ ];
+
+ return $inv_entry;
+ }
+
+ protected function addCustomer($name, $mobile)
+ {
+ $new_cust = new Customer();
+
+ $new_cust->setFirstName($name)
+ ->setLastName(self::STR_LAST_NAME)
+ ->setPhoneMobile($mobile);
+
+ $this->em->persist($new_cust);
+
+ return $new_cust;
+ }
+
+ protected function addCustomerVehicle($plate_num, $car_model, $customer)
+ {
+ $new_cv = new CustomerVehicle();
+
+ // get vehicle from hash
+ $v_array = explode(' ', $car_model);
+
+ // manufacturer
+ $v_manufacturer = trim($v_array[0]);
+
+ // get model
+ $model_info = '';
+ $v_model = '';
+ for ($i = 1; $i < count($v_array); $i++)
+ {
+ $model_info = $model_info . ' ' . trim($v_array[$i]);
+ }
+
+ $v_model = trim($model_info);
+
+ $vehicle = $this->vmake_hash[$v_manufacturer][$v_model];
+
+ $new_cv->setCustomer($customer)
+ ->setPlateNumber($plate_num)
+ ->setStatusCondition(VehicleStatusCondition::BRAND_NEW)
+ ->setModelYear('')
+ ->setColor('')
+ ->setFuelType(FuelType::GAS)
+ ->setHasMotoliteBattery(true)
+ ->setVehicle($vehicle);
+
+ $this->em->persist($new_cv);
+
+ // add customer vehicle to cv_hash
+ $this->cv_hash[$plate_num] = $new_cv;
+
+ return $new_cv;
+ }
+
+ protected function loadBatteryManufacturers()
+ {
+ $this->bmanu_hash = [];
+
+ $batt_manufacturers = $this->em->getRepository(BatteryManufacturer::class)->findAll();
+ foreach ($batt_manufacturers as $batt_manu)
+ {
+ $name = $this->normalizeName($batt_manu->getName());
+ $this->bmanu_hash[$name] = $batt_manu;
+ }
+ }
+
+ protected function loadBatteryModels()
+ {
+ $this->bmodel_hash = [];
+
+ $batt_models = $this->em->getRepository(BatteryModel::class)->findAll();
+ foreach ($batt_models as $batt_model)
+ {
+ $name = $this->normalizeName($batt_model->getName());
+ $this->bmodel_hash[$name] = $batt_model;
+ }
+ }
+
+ protected function loadBatterySizes()
+ {
+ $this->bsize_hash = [];
+
+ $batt_sizes = $this->em->getRepository(BatterySize::class)->findAll();
+ foreach ($batt_sizes as $batt_size)
+ {
+ $name = $this->normalizeName($batt_size->getName());
+ $this->bsize_hash[$name] = $batt_size;
+ }
+ }
+
+ protected function loadBatteries()
+ {
+ $this->batt_hash = [];
+
+ $batts = $this->em->getRepository(Battery::class)->findAll();
+ foreach ($batts as $batt)
+ {
+ $brand = $this->normalizeName($batt->getManufacturer()->getName());
+ $model = $this->normalizeName($batt->getModel()->getName());
+ $size = $this->normalizeName($batt->getSize()->getName());
+
+ $this->batt_hash[$brand][$model][$size] = $batt;
+ }
+ }
+
+ protected function loadVehicleManufacturers()
+ {
+ $this->vmanu_hash = [];
+
+ $vmanus = $this->em->getRepository(VehicleManufacturer::class)->findAll();
+ foreach ($vmanus as $vmanu)
+ {
+ $name = $this->normalizeName($vmanu->getName());
+ $this->vmanu_hash[$name] = $vmanu;
+ }
+ }
+
+ protected function loadVehicleMakes()
+ {
+ $this->vmake_hash = [];
+
+ $vmakes = $this->em->getRepository(Vehicle::class)->findAll();
+ foreach ($vmakes as $vmake)
+ {
+ $manufacturer = $this->normalizeName($vmake->getManufacturer()->getName());
+ $make = $this->normalizeName($vmake->getMake());
+
+ $this->vmake_hash[$manufacturer][$make] = $vmake;
+ }
+ }
+
+ protected function loadCustomerVehicles()
+ {
+ $this->cv_hash = [];
+
+ $cvs = $this->em->getRepository(CustomerVehicle::class)->findAll();
+ foreach ($cvs as $cv)
+ {
+ $plate_number = $this->cleanPlateNumber($cv->getPlateNumber());
+
+ $this->cv_hash[$plate_number] = $cv;
+ }
+ }
+
+ protected function normalizeName($name)
+ {
+ $normalized_key = trim(strtolower($name));
+
+ return $normalized_key;
+ }
+
+ protected function cleanPlateNumber($plate_number)
+ {
+ // remove spaces and make upper case
+ $clean_plate_number = strtoupper(str_replace(' ' , '', $plate_number));
+
+ return $clean_plate_number;
+ }
+
+}
diff --git a/src/Command/UpdateCMBMigratedCustomerCommand.php b/src/Command/UpdateCMBMigratedCustomerCommand.php
new file mode 100644
index 00000000..6189962f
--- /dev/null
+++ b/src/Command/UpdateCMBMigratedCustomerCommand.php
@@ -0,0 +1,54 @@
+em = $em;
+
+ parent::__construct();
+ }
+
+ protected function configure()
+ {
+ $this->setName('cmbcustomer:updatecustomer')
+ ->setDescription('Set customer last name.')
+ ->setHelp('Set the customer last name. ');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ error_log('Updating customer last name...');
+
+ // get all customers
+ $cust_results = $this->em->getRepository(Customer::class)->findBy(['last_name' => '']);
+
+ foreach ($cust_results as $cust)
+ {
+ $cust->setLastName(self::STR_LAST_NAME);
+ }
+
+ $this->em->flush();
+ $this->em->clear();
+
+
+ return 0;
+ }
+
+}
diff --git a/src/Command/UpdateCMBMigratedCustomerVehicleBatteryCommand.php b/src/Command/UpdateCMBMigratedCustomerVehicleBatteryCommand.php
new file mode 100644
index 00000000..eb6facfe
--- /dev/null
+++ b/src/Command/UpdateCMBMigratedCustomerVehicleBatteryCommand.php
@@ -0,0 +1,169 @@
+em = $em;
+
+ // load existing batteries
+ $this->loadBatteries();
+
+ parent::__construct();
+ }
+
+ protected function configure()
+ {
+ $this->setName('cmbcustomervehicle:updatebattery')
+ ->setDescription('Update customer vehicle battery information.')
+ ->setHelp('Updates the customer vehicle battery based on migrated data. ')
+ ->addArgument('output_file', InputArgument::REQUIRED, 'Path to the output CSV file of the entries not updated.');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $csv_file = $input->getArgument('output_file');
+
+ // attempt to open file
+ try
+ {
+ $fh = fopen($csv_file, "w");
+ }
+ catch (Exception $e)
+ {
+ throw new Exception('The file "' . $csv_file . '" could be opened.');
+ }
+
+ error_log('Updating customer vehicles...');
+
+ // get all customer vehicles
+ $cv_results = $this->em->getRepository(CustomerVehicle::class)->findAll();
+
+ $not_updated = [];
+ foreach ($cv_results as $cv)
+ {
+ $plate_number = $cv->getPlateNumber();
+
+ // find cmb legacy job order using plate number
+ $cmb_legacy_results = $this->em->getRepository(CMBLegacyJobOrder::class)->findBy(['plate_number' => $plate_number], ['trans_date' => 'DESC']);
+ if ($cmb_legacy_results != null)
+ {
+ // get the latest entry
+ $cmb_legacy = current($cmb_legacy_results);
+
+ // get battery model and size
+ $battery_model = $this->normalizeName($cmb_legacy->getBatteryModel());
+ $battery_size = $this->normalizeName($cmb_legacy->getBatterySize());
+
+ // figure out manufacturer using model
+ // century = marathoner, excel, sdfc
+ // motolite = classic
+ $battery_manu = self::STR_CENTURY;
+ if ($battery_model == self::STR_CLASSIC)
+ $battery_manu = self::STR_MOTOLITE;
+
+ // get battery
+ $cv_battery = null;
+ if (isset($this->batt_hash[$battery_manu][$battery_model][$battery_size]))
+ $cv_battery = $this->batt_hash[$battery_manu][$battery_model][$battery_size];
+
+ if ($cv_battery != null)
+ {
+ //error_log('found battery for ' . $plate_number . ' battery ' . $cv_battery->getModel()->getName() . ' ' . $cv_battery->getSize()->getName());
+ $cv->setCurrentBattery($cv_battery);
+ }
+ else
+ {
+ //error_log('no battery for ' . $battery_model . ' ' . $battery_size);
+ $not_updated[] = $this->addNotUpdatedEntry($cv, $battery_manu, $battery_model, $battery_size);
+ }
+ }
+ }
+
+ $this->em->flush();
+ $this->em->clear();
+
+ // output the entries that were not added
+ if (count($not_updated) > 0)
+ {
+ fputcsv($fh, [
+ 'Customer Vehicle ID',
+ 'Plate Number',
+ 'Battery Manufacturer',
+ 'Battery Model',
+ 'Battery Size',
+ 'Reason',
+ ]);
+
+ foreach($not_updated as $row)
+ {
+ fputcsv($fh, $row);
+ }
+ }
+
+ return 0;
+ }
+
+ protected function loadBatteries()
+ {
+ $this->batt_hash = [];
+
+ $batts = $this->em->getRepository(Battery::class)->findAll();
+ foreach ($batts as $batt)
+ {
+ $brand = $this->normalizeName($batt->getManufacturer()->getName());
+ $model = $this->normalizeName($batt->getModel()->getName());
+ $size = $this->normalizeName($batt->getSize()->getName());
+
+ $this->batt_hash[$brand][$model][$size] = $batt;
+ }
+ }
+
+ protected function addNotUpdatedEntry($cv, $manu, $model, $size)
+ {
+ $entry = [
+ $cv->getID(),
+ $cv->getPlateNumber(),
+ strtoupper($manu),
+ strtoupper($model),
+ strtoupper($size),
+ 'Battery not in system.',
+ ];
+
+ return $entry;
+ }
+
+ protected function normalizeName($name)
+ {
+ $normalized_key = trim(strtolower($name));
+
+ return $normalized_key;
+ }
+
+}
diff --git a/src/Controller/CMBRAPIController.php b/src/Controller/CMBRAPIController.php
new file mode 100644
index 00000000..6ddf842e
--- /dev/null
+++ b/src/Controller/CMBRAPIController.php
@@ -0,0 +1,317 @@
+register($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function login(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->login($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function logout(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->logout($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function acceptJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->acceptJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function cancelJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->cancelJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function arrive(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->arrive($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function performJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->performJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function hubArrive(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->hubArrive($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function payment(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->payment($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function available(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->available($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getPromos(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getPromos($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getBatteries(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getBatteries($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function changeService(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->changeService($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getJobOrderHistory(Request $req, $period, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getJobOrderHistory($req, $period);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getAssignedJobOrders(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getAssignedJobOrders($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function setJobOrderInTransit(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->setJobOrderInTransit($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function generateInvoice(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->generateInvoice($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function goOnline(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->goOnline($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function goOffline(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->goOffline($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function startJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->startJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function completeJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->completeJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function setActiveJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->setActiveJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function rejectJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->rejectJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function setOdometer(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->setOdometer($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function uploadFinishPhotos(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->uploadFinishPhotos($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getStatus(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getStatus($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getOngoingJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getOngoingJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getPaymentMethods(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getPaymentMethods($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function getCancelReasons(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->getCancelReasons($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ public function verifyJobOrder(Request $req, RiderAPIHandlerInterface $rapi_handler)
+ {
+ $data = $rapi_handler->verifyJobOrder($req);
+
+ $res = $this->generateResultFromHandler($data);
+
+ return $res->getReturnResponse();
+ }
+
+ protected function generateResultFromHandler($data)
+ {
+ $res = new NewAPIResult();
+
+ if (isset($data['error']))
+ {
+ $message = $data['error'];
+ $title = $data['title'];
+
+ $res->setError(true)
+ ->setErrorTitle($title)
+ ->setErrorMessage($message);
+ }
+ else
+ {
+ $res->setData($data);
+ }
+
+ return $res;
+ }
+
+}
diff --git a/src/Controller/HubController.php b/src/Controller/HubController.php
index edc6e599..5f1c56a5 100644
--- a/src/Controller/HubController.php
+++ b/src/Controller/HubController.php
@@ -346,7 +346,7 @@ class HubController extends Controller
$riders = [];
// TODO: remove this later when we don't get all available riders
- $riders_limit = 5;
+ $riders_limit = 50;
$num_riders = 0;
foreach ($available_riders as $rider)
diff --git a/src/Controller/JobOrderController.php b/src/Controller/JobOrderController.php
index f23fc73c..1b7a451f 100644
--- a/src/Controller/JobOrderController.php
+++ b/src/Controller/JobOrderController.php
@@ -286,6 +286,11 @@ class JobOrderController extends Controller
$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 if ($tier == 'behind_schedule')
+ {
+ $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]);
@@ -764,7 +769,10 @@ class JobOrderController extends Controller
// use invoice object values in a json friendly array
$invoice = [
- 'discount' => number_format($iobj->getDiscount(), 2),
+ // TODO: CMB needs to have no decimal places for discount.
+ // Resq requires the two decimal places since discount is computed.
+ //'discount' => number_format($iobj->getDiscount(), 2),
+ 'discount' => number_format($iobj->getDiscount(), 0),
'trade_in' => number_format($iobj->getTradeIn(), 2), // TODO: computations not done yet for this on invoice creator
'price' => number_format($iobj->getVATExclusivePrice(), 2),
'vat' => number_format($iobj->getVAT(), 2),
@@ -867,14 +875,14 @@ class JobOrderController extends Controller
return $this->render($template, $params);
}
- public function oneStepSubmit(Request $req, JobOrderHandlerInterface $jo_handler)
+ public function oneStepSubmit(Request $req, JobOrderHandlerInterface $jo_handler, MQTTClient $mclient)
{
$this->denyAccessUnlessGranted('jo_onestep.form', null, 'No access.');
// initialize error list
$error_array = [];
$id = -1;
- $error_array = $jo_handler->processOneStepJobOrder($req, $id);
+ $error_array = $jo_handler->processOneStepJobOrder($req, $id, $mclient);
// check if any errors were found
if (!empty($error_array)) {
@@ -913,12 +921,12 @@ class JobOrderController extends Controller
return $this->render($template, $params);
}
- public function oneStepEditSubmit(Request $req, JobOrderHandlerInterface $jo_handler, $id)
+ public function oneStepEditSubmit(Request $req, JobOrderHandlerInterface $jo_handler, $id, MQTTClient $mclient)
{
$this->denyAccessUnlessGranted('jo_onestep.edit', null, 'No access.');
$error_array = [];
- $error_array = $jo_handler->processOneStepJobOrder($req, $id);
+ $error_array = $jo_handler->processOneStepJobOrder($req, $id, $mclient);
// check if any errors were found
if (!empty($error_array)) {
@@ -1209,6 +1217,21 @@ class JobOrderController extends Controller
}
+ /**
+ * @Menu(selected="jo_behind_schedule")
+ */
+ public function listBehindSchedule(JobOrderHandlerInterface $jo_handler)
+ {
+ $this->denyAccessUnlessGranted('jo_behind_schedule.list', null, 'No access.');
+
+ $template = $jo_handler->getTwigTemplate('jo_behind_schedule');
+
+ $params = $jo_handler->getOtherParameters();
+ $params['table_refresh_rate'] = $this->container->getParameter('job_order_refresh_interval');
+
+ return $this->render($template, $params);
+ }
+
protected function autoAssignHubAndRider($jo, EntityManagerInterface $em,
MapTools $map_tools, InventoryManager $im)
{
diff --git a/src/Controller/NotificationController.php b/src/Controller/NotificationController.php
new file mode 100644
index 00000000..64150c51
--- /dev/null
+++ b/src/Controller/NotificationController.php
@@ -0,0 +1,111 @@
+sub(new DateInterval('PT10M'));
+ $notifs = [
+ [
+ 'id' => 1,
+ 'type' => 'jo_new',
+ 'date' => $date->format('Y-m-d\TH:i:s.000P'),
+ 'text' => 'Sample incoming job order',
+ 'link' => '#',
+ ], [
+ 'id' => 2,
+ 'type' => 'rider_accept',
+ 'date' => $date->format('Y-m-d\TH:i:s.000P'),
+ 'text' => 'Sample rider has accepted job order',
+ 'link' => '#',
+ ], [
+ 'id' => 3,
+ 'type' => 'jo_cancel',
+ 'date' => $date->format('Y-m-d\TH:i:s.000P'),
+ 'text' => 'Customer has cancelled job order.',
+ 'link' => '#',
+ ], [
+ 'id' => 3,
+ 'type' => 'rider_reject',
+ 'date' => $date->format('Y-m-d\TH:i:s.000P'),
+ 'text' => 'Rider has rejected job order. Job order needs to be reassigned.',
+ 'link' => '#',
+ ],
+ ];
+ $sample_data = [
+ 'count' => 4,
+ 'notifications' => $notifs,
+ ];
+ */
+
+ $notifs = $em->getRepository(Notification::class)->findBy(['user_id' => 0]);
+ $notif_data = [];
+
+ $unread_count = 0;
+ foreach ($notifs as $notif)
+ {
+ if (!($notif->isRead()))
+ $unread_count++;
+
+ $notif_data[] = [
+ 'id' => $notif->getID(),
+ 'type' => 'jo_new',
+ 'is_read' => $notif->isRead(),
+ 'is_fresh' => $notif->isFresh(),
+ 'date' => $notif->getDateCreate()->format('Y-m-d\TH:i:s.000P'),
+ 'text' => $notif->getMessage(),
+ 'link' => $notif->getURL(),
+ ];
+ }
+
+
+ $sample_data = [
+ 'count' => count($notif_data),
+ 'unread_count' => $unread_count,
+ 'notifications' => $notif_data,
+ ];
+
+ $res = new JsonResponse($sample_data);
+
+ return $res;
+ }
+
+ // TODO: security
+ public function ajaxUpdate(EntityManagerInterface $em, Request $req)
+ {
+ $notif_id = $req->request->get('id');
+ error_log($notif_id);
+
+ $notif = $em->getRepository(Notification::class)->find($notif_id);
+
+ if ($notif != null)
+ {
+ // TODO: fresh is if unread and still within x hours
+ // but for now fresh and unread are both the same
+ $notif->setIsRead(true);
+ $notif->setIsFresh(false);
+
+ $em->persist($notif);
+ $em->flush();
+ }
+
+ $res = new JsonResponse();
+
+ return $res;
+ }
+}
diff --git a/src/Controller/RiderController.php b/src/Controller/RiderController.php
index 43f5d6d6..763d4900 100644
--- a/src/Controller/RiderController.php
+++ b/src/Controller/RiderController.php
@@ -596,4 +596,38 @@ class RiderController extends Controller
return $this->redirecttoRoute('rider_update', ['id' => $rider->getID()]);
}
+
+ /**
+ * @ParamConverter("rider", class="App\Entity\Rider")
+ */
+ public function ajaxAvailable(EntityManagerInterface $em, Rider $rider)
+ {
+ $jo = $rider->getRiderActiveJobOrder();
+ if ($jo == null || $jo->isClosed())
+ $avail = 'available';
+ else
+ $avail = 'unavailable';
+
+ $response = new Response(
+ $avail,
+ Response::HTTP_OK,
+ ['content-type' => 'text/plain']
+ );
+ return $response;
+ }
+
+ /**
+ * @ParamConverter("rider", class="App\Entity\Rider")
+ */
+ public function ajaxRiderName(EntityManagerInterface $em, Rider $rider)
+ {
+ $rider_name = '';
+ if ($rider != null)
+ $rider_name = $rider->getFullName();
+
+ return $this->json([
+ 'rider_name' => $rider_name,
+ ]);
+ }
+
}
diff --git a/src/Entity/CMBLegacyJobOrder.php b/src/Entity/CMBLegacyJobOrder.php
new file mode 100644
index 00000000..6f9c0826
--- /dev/null
+++ b/src/Entity/CMBLegacyJobOrder.php
@@ -0,0 +1,395 @@
+trans_date = new DateTime();
+ }
+
+ public function setID($id)
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ public function getID()
+ {
+ return $this->id;
+ }
+
+ public function setTransDate(DateTime $trans_date)
+ {
+ $this->trans_date = $trans_date;
+ return $this;
+ }
+
+ public function getTransDate()
+ {
+ return $this->trans_date;
+ }
+
+ public function setTransType($trans_type)
+ {
+ $this->trans_type = $trans_type;
+ return $this;
+ }
+
+ public function getTransType()
+ {
+ return $this->trans_type;
+ }
+
+ public function setCaseNumber($case_number)
+ {
+ $this->case_number = $case_number;
+ return $this;
+ }
+
+ public function getCaseNumber()
+ {
+ return $this->case_number;
+ }
+
+ public function setInsurer($insurer)
+ {
+ $this->insurer = $insurer;
+ return $this;
+ }
+
+ public function getInsurer()
+ {
+ return $this->insurer;
+ }
+
+ public function setNatureOfCall($nature_of_call)
+ {
+ $this->nature_of_call = $nature_of_call;
+ return $this;
+ }
+
+ public function getNatureOfCall()
+ {
+ return $this->nature_of_call;
+ }
+
+ public function setPlateNumber($plate_number)
+ {
+ $this->plate_number = $plate_number;
+ return $this;
+ }
+
+ public function getPlateNumber()
+ {
+ return $this->plate_number;
+ }
+
+ public function setCarBrand($car_brand)
+ {
+ $this->car_brand = $car_brand;
+ return $this;
+ }
+
+ public function getCarBrand()
+ {
+ return $this->car_brand;
+ }
+
+ public function setCarMake($car_make)
+ {
+ $this->car_make = $car_make;
+ return $this;
+ }
+
+ public function getCarMake()
+ {
+ return $this->car_make;
+ }
+
+ public function setAddress($address)
+ {
+ $this->address = $address;
+ return $this;
+ }
+
+ public function getAddress()
+ {
+ return $this->address;
+ }
+
+ public function setDriver($driver)
+ {
+ $this->driver = $driver;
+ return $this;
+ }
+
+ public function getDriver()
+ {
+ return $this->driver;
+ }
+
+ public function setTruck($truck)
+ {
+ $this->truck = $truck;
+ return $this;
+ }
+
+ public function getTruck()
+ {
+ return $this->truck;
+ }
+
+ public function setWorkshopArrivalTime($workshop_arrival_time)
+ {
+ $this->workshop_arrival_time = $workshop_arrival_time;
+ return $this;
+ }
+
+ public function getWorkshopArrivalTime()
+ {
+ return $this->workshop_arrival_time;
+ }
+
+ public function setStatus($status)
+ {
+ $this->status = $status;
+ return $this;
+ }
+
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ public function setCustName($cust_name)
+ {
+ $this->cust_name = $cust_name;
+ return $this;
+ }
+
+ public function getCustName()
+ {
+ return $this->cust_name;
+ }
+
+ public function setCustMobile($cust_mobile)
+ {
+ $this->cust_mobile = $cust_mobile;
+ return $this;
+ }
+
+ public function getCustMobile()
+ {
+ return $this->cust_mobile;
+ }
+
+ public function setReference($reference)
+ {
+ $this->reference = $reference;
+ return $this;
+ }
+
+ public function getReference()
+ {
+ return $this->reference;
+ }
+
+ public function setOdometer($odometer)
+ {
+ $this->odometer = $odometer;
+ return $this;
+ }
+
+ public function getOdometer()
+ {
+ return $this->odometer;
+ }
+
+ public function setBatteryModel($batt_model)
+ {
+ $this->batt_model = $batt_model;
+ return $this;
+ }
+
+ public function getBatteryModel()
+ {
+ return $this->batt_model;
+ }
+
+ public function setBatterySize($batt_size)
+ {
+ $this->batt_size = $batt_size;
+ return $this;
+ }
+
+ public function getBatterySize()
+ {
+ return $this->batt_size;
+ }
+
+ public function setIsTradeIn($flag_trade_in = true)
+ {
+ $this->flag_trade_in = $flag_trade_in;
+ return $this;
+ }
+
+ public function isTradeIn()
+ {
+ return $this->flag_trade_in;
+ }
+
+ public function setReplacedBy($replaced_by)
+ {
+ $this->replaced_by = $replaced_by;
+ return $this;
+ }
+
+ public function getReplacedBy()
+ {
+ return $this->replaced_by;
+ }
+
+ public function setSatisfaction($satisfaction)
+ {
+ $this->satisfaction = $satisfaction;
+ return $this;
+ }
+
+ public function getSatisfaction()
+ {
+ return $this->satisfaction;
+ }
+
+}
diff --git a/src/Entity/CMBLegacyJobOrderRow.php b/src/Entity/CMBLegacyJobOrderRow.php
new file mode 100644
index 00000000..0a6fdc35
--- /dev/null
+++ b/src/Entity/CMBLegacyJobOrderRow.php
@@ -0,0 +1,57 @@
+data = [];
+ }
+
+ public function addData($id, $value)
+ {
+ $this->data[$id] = $value;
+ return $this;
+ }
+
+ public function setData(array $data_array)
+ {
+ $this->data = $data_array;
+ return $this;
+ }
+
+ public function getDataById($id)
+ {
+ // return null if we don't have it
+ if (!isset($this->data[$id]))
+ return null;
+
+ return $this->data[$id];
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+}
diff --git a/src/Entity/Customer.php b/src/Entity/Customer.php
index 9ba043c9..c0016ec5 100644
--- a/src/Entity/Customer.php
+++ b/src/Entity/Customer.php
@@ -34,7 +34,7 @@ class Customer
// first name
/**
- * @ORM\Column(type="string", length=80)
+ * @ORM\Column(type="string", length=100)
* @Assert\NotBlank()
*/
protected $first_name;
@@ -42,7 +42,6 @@ class Customer
// last name
/**
* @ORM\Column(type="string", length=80)
- * @Assert\NotBlank()
*/
protected $last_name;
@@ -66,7 +65,7 @@ class Customer
// mobile phone
/**
- * @ORM\Column(type="string", length=30)
+ * @ORM\Column(type="string", length=50)
*/
protected $phone_mobile;
diff --git a/src/Entity/JOExtra.php b/src/Entity/JOExtra.php
new file mode 100644
index 00000000..7770c150
--- /dev/null
+++ b/src/Entity/JOExtra.php
@@ -0,0 +1,140 @@
+other_images = new ArrayCollection();
+ }
+
+ public function getID()
+ {
+ return $this->id;
+ }
+
+ public function setImage1Filename($image_filename)
+ {
+ $this->image_1_filename = $image_filename;
+ return $this;
+ }
+
+ public function getImage1Filename()
+ {
+ return $this->image_1_filename;
+ }
+
+ public function setImage2Filename($image_filename)
+ {
+ $this->image_2_filename = $image_filename;
+ return $this;
+ }
+
+ public function getImage2Filename()
+ {
+ return $this->image_2_filename;
+ }
+
+ public function setImage3Filename($image_filename)
+ {
+ $this->image_3_filename = $image_filename;
+ return $this;
+ }
+
+ public function getImage3Filename()
+ {
+ return $this->image_3_filename;
+ }
+
+ public function setImage4Filename($image_filename)
+ {
+ $this->image_4_filename = $image_filename;
+ return $this;
+ }
+
+ public function getImage4Filename()
+ {
+ return $this->image_4_filename;
+ }
+
+ public function getOtherImages()
+ {
+ return $this->other_images;
+ }
+
+ public function setOtherImages(array $images)
+ {
+ $this->other_images = new ArrayCollection();
+
+ foreach ($images as $image_filename)
+ {
+ $this->other_images->add($image_filename);
+ }
+
+ return $this;
+ }
+
+ public function clearOtherImages()
+ {
+ $this->other_images = new ArrayCollection();
+ return $this;
+ }
+
+ public function setCustomerSignature($sig_file)
+ {
+ $this->cust_signature = $sig_file;
+ return $this;
+ }
+
+ public function getCustomerSignature()
+ {
+ return $this->cust_signature;
+ }
+}
diff --git a/src/Entity/JobOrder.php b/src/Entity/JobOrder.php
index 8bcdbb77..d1cf59b9 100644
--- a/src/Entity/JobOrder.php
+++ b/src/Entity/JobOrder.php
@@ -334,6 +334,30 @@ class JobOrder
*/
protected $phone_mobile;
+ // link to JOExtra
+ /**
+ * @ORM\OneToOne(targetEntity="JOExtra")
+ */
+ protected $jo_extra;
+
+ // date that the status last changed
+ /**
+ * @ORM\Column(type="datetime", nullable=true)
+ */
+ protected $date_status_change;
+
+ // insurer/responsible party handling the onsite job
+ /**
+ * @ORM\Column(type="string", length=80, nullable=true)
+ */
+ protected $responsible_party;
+
+ // rider plate number since rider can change vehicles
+ /**
+ * @ORM\Column(type="string", length=10, nullable=true)
+ */
+ protected $rider_plate_num;
+
public function __construct()
{
$this->date_create = new DateTime();
@@ -583,6 +607,7 @@ class JobOrder
{
// TODO: validate status
$this->status = $status;
+ $this->date_status_change = new DateTime();
return $this;
}
@@ -596,6 +621,11 @@ class JobOrder
return JOStatus::getName($this->status);
}
+ public function getDateStatusChange()
+ {
+ return $this->date_status_change;
+ }
+
public function setDeliveryInstructions($delivery_instructions)
{
$this->delivery_instructions = $delivery_instructions;
@@ -796,6 +826,11 @@ class JobOrder
$this->makeRiderAvailable();
}
+ public function perform()
+ {
+ $this->setStatus(JOStatus::PERFORMED);
+ }
+
public function fulfill()
{
$this->setStatus(JOStatus::FULFILLED)
@@ -962,5 +997,38 @@ class JobOrder
return $this->phone_mobile;
}
+ public function setJOExtra(JOExtra $jo_extra)
+ {
+ $this->jo_extra = $jo_extra;
+ return $this;
+ }
+
+ public function getJOExtra()
+ {
+ return $this->jo_extra;
+ }
+
+ public function setResponsibleParty($responsible_party)
+ {
+ $this->responsible_party = $responsible_party;
+ return $this;
+ }
+
+ public function getResponsibleParty()
+ {
+ return $this->responsible_party;
+ }
+
+ public function setRiderPlateNum($rider_plate_num)
+ {
+ $this->rider_plate_num = $rider_plate_num;
+ return $this;
+ }
+
+ public function getRiderPlateNum()
+ {
+ return $this->rider_plate_num;
+ }
+
}
diff --git a/src/Entity/Notification.php b/src/Entity/Notification.php
new file mode 100644
index 00000000..012bcb15
--- /dev/null
+++ b/src/Entity/Notification.php
@@ -0,0 +1,148 @@
+date_create = new DateTime();
+ $this->flag_read = false;
+ $this->flag_fresh = true;
+ }
+
+ public function getID()
+ {
+ return $this->id;
+ }
+
+ public function getDateCreate()
+ {
+ return $this->date_create;
+ }
+
+ public function setUserID($user_id)
+ {
+ $this->user_id = $user_id;
+ return $this;
+ }
+
+ public function getUserID()
+ {
+ return $this->user_id;
+ }
+
+ public function setIcon($icon)
+ {
+ $this->icon = $icon;
+ return $this;
+ }
+
+ public function getIcon()
+ {
+ return $this->icon;
+ }
+
+ public function setMessage($message)
+ {
+ $this->message = $message;
+ return $this;
+ }
+
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ public function setURL($url)
+ {
+ $this->url = $url;
+ return $this;
+ }
+
+ public function getURL()
+ {
+ return $this->url;
+ }
+
+ public function setIsRead($bool = true)
+ {
+ $this->flag_read = $bool;
+ return $this;
+ }
+
+ public function isRead()
+ {
+ return $this->flag_read;
+ }
+
+ public function setIsFresh($bool = true)
+ {
+ $this->flag_fresh = $bool;
+ return $this;
+ }
+
+ public function isFresh()
+ {
+ return $this->flag_fresh;
+ }
+}
diff --git a/src/Entity/Rider.php b/src/Entity/Rider.php
index 502d93bc..6416a443 100644
--- a/src/Entity/Rider.php
+++ b/src/Entity/Rider.php
@@ -347,6 +347,11 @@ class Rider
return $this->job_orders->matching($criteria)[0];
}
+ public function getRiderActiveJobOrder()
+ {
+ return $this->active_job_order;
+ }
+
public function getOpenJobOrders()
{
$active_status = [
diff --git a/src/Ramcar/CMBCancelReason.php b/src/Ramcar/CMBCancelReason.php
new file mode 100644
index 00000000..b681a40a
--- /dev/null
+++ b/src/Ramcar/CMBCancelReason.php
@@ -0,0 +1,22 @@
+ 'Accident',
+ 'breakdown' => 'Breakdown',
+ 'fuel_runout' => 'Fuel Runout',
+ 'wrong_battery_size' => 'Wrong Battery Size',
+ 'customer_cancel' => 'Cancelled by Customer',
+ 'others' => 'Others'
+ ];
+}
diff --git a/src/Ramcar/CMBJOEventType.php b/src/Ramcar/CMBJOEventType.php
new file mode 100644
index 00000000..631b434b
--- /dev/null
+++ b/src/Ramcar/CMBJOEventType.php
@@ -0,0 +1,42 @@
+ 'Created',
+ 'hub_assign' => 'Assigned to Hub',
+ 'rider_assign' => 'Assigned Rider',
+ 'cancel' => 'Cancelled',
+ 'fulfill' => 'Fulfilled',
+ 'open_edit' => 'Open Edit',
+ 'requeue' => 'Requeue',
+ 'accept' => 'Rider Accept',
+ 'rider_in_transit' => 'Rider in Transit',
+ 'arrive' => 'Rider Arrive',
+ 'rider_start' => 'Rider Start',
+ 'rider_edit' => 'Rider Edit',
+ 'paid' => 'Paid',
+ 'perform' => 'Perform',
+ 'finish' => 'Finish',
+ 'rider_upload_photo' => 'Rider Upload Photo',
+ ];
+}
diff --git a/src/Ramcar/CMBModeOfPayment.php b/src/Ramcar/CMBModeOfPayment.php
index c7ae9908..63bb9a2d 100644
--- a/src/Ramcar/CMBModeOfPayment.php
+++ b/src/Ramcar/CMBModeOfPayment.php
@@ -5,12 +5,18 @@ namespace App\Ramcar;
class CMBModeOfPayment extends NameValue
{
const CASH = 'cash';
+ const SHOPEE = 'shopee';
+ const LAZADA = 'lazada';
const CREDIT_CARD = 'credit_card';
- const BANK_TRANSFER = 'bank_transfer';
+ const ONLINE_TRANSFER = 'online_transfer';
+ const E_WALLET = 'e_wallet';
const COLLECTION = [
'cash' => 'Cash',
+ 'shopee' => 'Shopee',
+ 'lazada' => 'Lazada',
'credit_card' => 'Credit Card',
- 'bank_transfer' => 'Bank Transfer',
+ 'online_transfer' => 'Online Transfer',
+ 'e_wallet' => 'E-Wallet',
];
}
diff --git a/src/Ramcar/CMBServiceType.php b/src/Ramcar/CMBServiceType.php
index 63d18f96..607b9c43 100644
--- a/src/Ramcar/CMBServiceType.php
+++ b/src/Ramcar/CMBServiceType.php
@@ -6,11 +6,13 @@ class CMBServiceType extends NameValue
{
const BATTERY_REPLACEMENT_NEW = 'battery_new';
const BATTERY_REPLACEMENT_WARRANTY = 'battery_warranty';
+ const WARRANTY_CLAIM = 'warranty_claim';
const JUMPSTART = 'jumpstart';
const COLLECTION = [
'battery_new' => 'Battery Sales',
- 'battery_warranty' => 'Under Warranty',
+ 'battery_warranty' => 'Warranty Replacement',
+ 'warranty_claim' => 'Warranty Claim',
'jumpstart' => 'Jumpstart',
];
}
diff --git a/src/Ramcar/CMBTransactionOrigin.php b/src/Ramcar/CMBTransactionOrigin.php
new file mode 100644
index 00000000..7ea6a02a
--- /dev/null
+++ b/src/Ramcar/CMBTransactionOrigin.php
@@ -0,0 +1,25 @@
+ 'Hotline',
+ 'online' => 'Online',
+ 'facebook' => 'Facebook',
+ 'vip' => 'VIP',
+ 'mobile_app' => 'Mobile App',
+ 'walk_in' => 'Walk-in',
+ 'whatsapp' => 'Whatsapp',
+ ];
+}
diff --git a/src/Ramcar/JOStatus.php b/src/Ramcar/JOStatus.php
index 5ba8f5d1..08177198 100644
--- a/src/Ramcar/JOStatus.php
+++ b/src/Ramcar/JOStatus.php
@@ -4,13 +4,15 @@ namespace App\Ramcar;
class JOStatus extends NameValue
{
- const PENDING = 'pending'; // NOTE: JO has no hub assigned
- const RIDER_ASSIGN = 'rider_assign'; // NOTE: JO has hub assigned but no rider assigned
- const ASSIGNED = 'assigned'; // NOTE: JO has hub and rider assigned
- const IN_TRANSIT = 'in_transit'; // NOTE: JO's rider is on his way
- const IN_PROGRESS = 'in_progress'; // NOTE: JO fulfillment in progress
- const CANCELLED = 'cancelled'; // NOTE: JO is cancelled
- const FULFILLED = 'fulfilled'; // NOTE: JO is fulfilled
+ const PENDING = 'pending'; // JO has no hub assigned
+ const RIDER_ASSIGN = 'rider_assign'; // JO has hub assigned but no rider assigned
+ const ASSIGNED = 'assigned'; // JO has hub and rider assigned
+ const IN_TRANSIT = 'in_transit'; // Rider is on his way
+ const IN_PROGRESS = 'in_progress'; // Rider has arrived at customer's location and fulfillment is in progress
+ const PERFORMED = 'performed'; // Rider has finished performing JO task / service
+ const PAID = 'paid'; // Rider has finished collecting payment for JO
+ const CANCELLED = 'cancelled'; // JO is cancelled
+ const FULFILLED = 'fulfilled'; // JO is fulfilled
const COLLECTION = [
'pending' => 'For Dispatch',
@@ -18,6 +20,8 @@ class JOStatus extends NameValue
'assigned' => 'Assigned',
'in_transit' => 'In Transit',
'in_progress' => 'In Progress',
+ 'performed' => 'Service Performed',
+ 'paid' => 'Customer Paid',
'cancelled' => 'Cancelled',
'fulfilled' => 'Completed',
];
diff --git a/src/Ramcar/NewAPIResult.php b/src/Ramcar/NewAPIResult.php
new file mode 100644
index 00000000..5c46bc21
--- /dev/null
+++ b/src/Ramcar/NewAPIResult.php
@@ -0,0 +1,92 @@
+err_title = '';
+ $this->err_flag = false;
+ $this->err_message = '';
+ $this->data = [];
+ }
+
+ public function setError($flag = true)
+ {
+ $this->err_flag = $flag;
+ return $this;
+ }
+
+ public function isError()
+ {
+ return $this->err_flag;
+ }
+
+ public function setErrorMessage($message)
+ {
+ $this->err_message = $message;
+ return $this;
+ }
+
+ public function getErrorMessage()
+ {
+ return $this->err_message;
+ }
+
+ public function setData($data)
+ {
+ $this->data = $data;
+ return $this;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function setErrorTitle($title)
+ {
+ $this->err_title = $title;
+ return $this;
+ }
+
+ public function getErrorTitle()
+ {
+ return $this->err_title;
+ }
+
+
+ public function getReturnResponse()
+ {
+ if ($this->isError())
+ $status = 'error';
+ else
+ $status = 'success';
+
+ if (count($this->data) == 0)
+ $this->data = new \stdClass();
+
+ $return_data = [
+ 'error' => [
+ 'title' => $this->err_title,
+ 'status' => $status,
+ 'message' => $this->err_message
+ ],
+ 'data' => $this->data
+ ];
+
+ // $json_data = json_encode($return_data, JSON_NUMERIC_CHECK);
+ $json_data = json_encode($return_data);
+ $json = new Response($json_data);
+
+ return $json;
+ }
+}
diff --git a/src/Service/CustomerHandler/CMBCustomerHandler.php b/src/Service/CustomerHandler/CMBCustomerHandler.php
index 371f674b..28612897 100644
--- a/src/Service/CustomerHandler/CMBCustomerHandler.php
+++ b/src/Service/CustomerHandler/CMBCustomerHandler.php
@@ -189,7 +189,7 @@ class CMBCustomerHandler implements CustomerHandlerInterface
$cust_vehicle = new CustomerVehicle();
$cust_vehicle->setName($vehicle->name)
->setVehicle($vobj)
- ->setPlateNumber($vehicle->plate_number)
+ ->setPlateNumber(trim($vehicle->plate_number))
->setModelYear($vehicle->model_year)
->setColor('')
->setStatusCondition('')
@@ -416,8 +416,14 @@ class CMBCustomerHandler implements CustomerHandlerInterface
// add filters to count query
if (!empty($term)) {
- $tquery->where('q.plate_number like :search')
- ->setParameter('search', $term . '%');
+ //$tquery->where('q.plate_number like :search')
+ // ->setParameter('search', $term . '%');
+ // TODO: this is really slow. Need to optimize
+ $tquery->innerJoin('q.customer', 'c')
+ ->where('q.plate_number like :search')
+ ->orWhere('c.phone_mobile = :number')
+ ->setParameter('search', $term . '%')
+ ->setParameter('number', $term);
/*
$tquery->where('match_against (q.plate_number, :search \'in boolean mode\') > 0.1')
->setParameter('search', $term . '*');
@@ -448,8 +454,14 @@ class CMBCustomerHandler implements CustomerHandlerInterface
// add filters if needed
if (!empty($term)) {
- $query->where('q.plate_number like :search')
- ->setParameter('search', $term . '%');
+ // TODO: this is really slow. Need to optimize
+ $query->innerJoin('q.customer', 'cust')
+ ->where('q.plate_number like :search')
+ ->orWhere('cust.phone_mobile = :number')
+ ->setParameter('search', $term . '%')
+ ->setParameter('number', $term);
+ //$query->where('q.plate_number like :search')
+ // ->setParameter('search', $term . '%');
/*
$query->where('match_against (q.plate_number, :search \'in boolean mode\') > 0.1')
->setParameter('search', $term . '*');
@@ -594,7 +606,7 @@ class CMBCustomerHandler implements CustomerHandlerInterface
protected function generateYearOptions()
{
$start_year = 1950;
- return range($start_year, date("Y") + 1);
+ return range($start_year, date("Y"));
}
diff --git a/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php b/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php
index 534600fb..f2e75a78 100644
--- a/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php
+++ b/src/Service/InvoiceGenerator/CMBInvoiceGenerator.php
@@ -86,6 +86,10 @@ class CMBInvoiceGenerator implements InvoiceGeneratorInterface
case CMBServiceType::BATTERY_REPLACEMENT_WARRANTY:
$this->processWarranty($total, $criteria, $invoice);
break;
+ case CMBServiceType::WARRANTY_CLAIM:
+ // TODO: this will change once we confirm what needs to be computed
+ $this->processOtherServices($total, $invoice, $stype);
+ break;
//case ServiceType::POST_RECHARGED:
// $this->processRecharge($total, $invoice);
// break;
@@ -270,7 +274,7 @@ class CMBInvoiceGenerator implements InvoiceGeneratorInterface
}
// check if discount is greater than 50 or negative number
- if (($discount > 50) || ($discount < 0))
+ if (($discount > 60) || ($discount < 0))
return 'Invalid discount specified';
$criteria->setDiscount($discount);
@@ -363,9 +367,10 @@ class CMBInvoiceGenerator implements InvoiceGeneratorInterface
if (!isset($con_batts[$batt_id]))
$con_batts[$batt->getID()] = [
'batt' => $batt,
- 'qty' => 0
+ 'qty' => $qty
];
- $con_batts[$batt_id]['qty']++;
+ else
+ $con_batts[$batt_id]['qty'] += $qty;
// no trade-in
diff --git a/src/Service/JobOrderHandler/CMBJobOrderHandler.php b/src/Service/JobOrderHandler/CMBJobOrderHandler.php
index 862d431b..7712f426 100644
--- a/src/Service/JobOrderHandler/CMBJobOrderHandler.php
+++ b/src/Service/JobOrderHandler/CMBJobOrderHandler.php
@@ -31,12 +31,12 @@ use App\Entity\ServiceCharge;
use App\Ramcar\InvoiceCriteria;
use App\Ramcar\CMBServiceType;
use App\Ramcar\CMBTradeInType;
-use App\Ramcar\JOEventType;
+use App\Ramcar\CMBJOEventType;
use App\Ramcar\JOStatus;
use App\Ramcar\CMBWarrantyClass;
use App\Ramcar\DiscountApply;
use App\Ramcar\CMBModeOfPayment;
-use App\Ramcar\TransactionOrigin;
+use App\Ramcar\CMBTransactionOrigin;
use App\Ramcar\FacilitatedType;
use App\Ramcar\JORejectionReason;
@@ -147,7 +147,8 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$order = $datatable['sort']['sort'] ?? 'asc';
$query->orderBy('q.' . $datatable['sort']['field'], $order);
} else {
- $query->orderBy('q.date_schedule', 'asc');
+ //$query->orderBy('q.date_schedule', 'asc');
+ $query->orderBy('q.date_schedule', 'desc');
}
// get rows for this page
@@ -177,13 +178,35 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// get rider information
$rider_name = '';
$rider_plate_number = '';
+ $rider_id = '';
$rider = $orow->getRider();
if (!empty($rider))
{
+ $rider_id = $rider->getID();
$rider_name = $rider->getFullName();
$rider_plate_number = $rider->getPlateNumber();
}
+ // get time when JO was started by rider
+ $start_event_type = CMBJOEventType::RIDER_START;
+ $date_start = '';
+ $start_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $start_event_type, 'rider' => $rider_id, 'job_order' => $orow->getID()], ['date_happen' => 'DESC']);
+ if ($start_results != null)
+ {
+ $jo_event = current($start_results);
+ $date_start = $jo_event->getDateHappen()->format('d M Y g:i A');
+ }
+
+ // get time when JO was finished by rider
+ $finish_event_type = CMBJOEventType::RIDER_FINISH;
+ $date_finish = '';
+ $finish_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $finish_event_type, 'rider' => $rider_id, 'job_order' => $orow->getID()], ['date_happen' => 'DESC']);
+ if ($finish_results != null)
+ {
+ $jo_event = current($finish_results);
+ $date_finish = $jo_event->getDateHappen()->format('d M Y g:i A');
+ }
+
// add row data
$row['id'] = $orow->getID();
$row['customer_name'] = $orow->getCustomer()->getFirstName() . ' ' . $orow->getCustomer()->getLastName();
@@ -194,16 +217,18 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$row['status'] = $statuses[$orow->getStatus()];
$row['flag_advance'] = $orow->isAdvanceOrder();
$row['plate_number'] = $orow->getCustomerVehicle()->getPlateNumber();
- $row['is_mobile'] = $orow->getSource() == TransactionOrigin::MOBILE_APP;
+ $row['is_mobile'] = $orow->getSource() == CMBTransactionOrigin::MOBILE_APP;
$row['car_model'] = $car_model;
$row['rider_name'] = $rider_name;
$row['rider_plate_number'] = $rider_plate_number;
+ $row['date_start'] = $date_start;
+ $row['date_finish'] = $date_finish;
- $processor = $orow->getProcessedBy();
- if ($processor == null)
- $row['processor'] = '';
+ $creator = $orow->getCreatedBy();
+ if ($creator == null)
+ $row['creator'] = '';
else
- $row['processor'] = $orow->getProcessedBy()->getFullName();
+ $row['creator'] = $orow->getCreatedBy()->getFullName();
$assignor = $orow->getAssignedBy();
if ($assignor == null)
@@ -350,7 +375,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
->setTier2Notes($req->request->get('tier2_notes'))
->setDeliveryAddress($req->request->get('delivery_address'))
->setORName($req->request->get('or_name'))
- ->setPromoDetail($req->request->get('promo_detail'))
+ ->setPromoDetail($req->request->get('promo_detail', ''))
->setModeOfPayment($req->request->get('mode_of_payment'))
->setLandmark($req->request->get('landmark'));
@@ -400,7 +425,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::CREATE)
+ ->setTypeID(CMBJOEventType::CREATE)
->setJobOrder($jo);
if ($user != null)
@@ -416,7 +441,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
return $error_array;
}
- public function processOneStepJobOrder(Request $req, $id)
+ public function processOneStepJobOrder(Request $req, $id, MQTTClient $mclient)
{
// initialize error list
$error_array = [];
@@ -424,33 +449,40 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$em = $this->em;
$jo = $em->getRepository(JobOrder::class)->find($id);
+ $old_jo_status = null;
+ $old_rider = null;
if (empty($jo))
{
// new job order
$jo = new JobOrder();
}
+ else
+ {
+ // need to get old values of rider and status to see if we need to change JO status or not
+ $old_rider = $jo->getRider();
+ $old_jo_status = $jo->getStatus();
+ }
// 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 responsible party is present
+ if (empty($req->request->get('responsible_party')))
+ $error_array['responsible_party'] = 'Responsible party is required.';
+
// check if new customer
if ($req->request->get('new_customer', false))
{
- if (empty($req->request->get('customer_customer_notes')))
- {
- $error_array['customer_customer_notes'] = 'Customer notes cannot be null.';
- }
-
// validate mobile phone
- $valid_mobile = $this->cust_handler->validateMobileNumber($req->request->get('customer_phone_mobile'));
+ $valid_mobile = $this->cust_handler->validateMobileNumber($req->request->get('phone_mobile'));
if (!($valid_mobile))
- $error_array['customer_phone_mobile'] = 'Invalid mobile phone number.';
+ $error_array['phone_mobile'] = 'Invalid mobile phone number.';
// check if plate number is in request
- if (empty($req->request->get('cv_plate')))
- $error_array['cv_plate'] = 'Plate number is required.';
+ if (empty(trim($req->request->get('plate_number'))))
+ $error_array['plate_number'] = 'Plate number is required.';
// find the vehicle using vid
$new_vehicle = $em->getRepository(Vehicle::class)->find($req->request->get('vid'));
@@ -464,17 +496,17 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$new_cust = new Customer();
$new_cv = new CustomerVehicle();
- $new_cust->setLastName($req->request->get('customer_last_name'))
- ->setFirstName($req->request->get('customer_first_name'))
- ->setPhoneMobile($req->request->get('customer_phone_mobile'))
- ->setPhoneLandline($req->request->get('customer_phone_landline'))
- ->setPhoneOffice($req->request->get('customer_phone_office'))
- ->setPhoneFax($req->request->get('customer_phone_fax'))
- ->setCustomerNotes($req->request->get('customer_customer_notes'));
+ $new_cust->setLastName($req->request->get('last_name'))
+ ->setFirstName($req->request->get('first_name'))
+ ->setPhoneMobile($req->request->get('phone_mobile'))
+ ->setPhoneLandline($req->request->get('phone_landline'))
+ ->setPhoneOffice($req->request->get('phone_office'))
+ ->setPhoneFax($req->request->get('phone_fax'))
+ ->setCustomerNotes($req->request->get('customer_notes'));
$new_cv->setCustomer($new_cust)
->setVehicle($new_vehicle)
- ->setPlateNumber($req->request->get('cv_plate'))
+ ->setPlateNumber(trim($req->request->get('plate_number')))
->setModelYear($req->request->get('cv_year'))
->setColor('')
->setStatusCondition('')
@@ -526,6 +558,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
}
// check if hub AND rider is selected
+ $rider_plate_number = '';
if ((empty($req->request->get('hub_id'))) &&
(empty($req->request->get('rider_id')))) {
$error_array['hub'] = 'No hub selected.';
@@ -544,36 +577,47 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
if (empty($rider)) {
$error_array['rider'] = 'Invalid rider specified.';
+ } else {
+ // check if rider is still available
+ if (!($rider->isAvailable()))
+ $error_array['rider'] = 'Selected rider is unavailable.';
+
+ $rider_plate_number = $rider->getPlateNumber();
}
}
}
}
- // set priority based on rider's existing open job orders
- $rider_jos = $rider->getOpenJobOrders();
-
- // get maximum priority then add 1
- // NOTE: this can be a bit buggy due to concurrency issues
- // ideally have to lock jo table, but that isn't feasible right now
- $priority = 0;
- foreach ($rider_jos as $rider_jo)
- {
- if ($priority < $rider_jo->getPriority())
- $priority = $rider_jo->getPriority() + 1;
- }
+ // delivery address
+ if (empty($req->request->get('delivery_address')))
+ $error_array['delivery_address'] = 'Delivery address is required.';
// get discount and set to meta
- $discount = $req->request->get('invoice_discount', []);
-
- // check if discount is greater than 50 or negative number
- if (($discount > 50) || ($discount < 0))
+ $discount = $req->request->get('invoice_discount');
+
+ if (($discount > 60) || ($discount < 0))
+ {
$error_array['invoice_discount'] = 'Invalid discount specified';
+ }
// get list of service charges
$service_charges = $req->request->get('service_charges', []);
if (empty($error_array))
{
+ // set priority based on rider's existing open job orders
+ $rider_jos = $rider->getOpenJobOrders();
+
+ // get maximum priority then add 1
+ // NOTE: this can be a bit buggy due to concurrency issues
+ // ideally have to lock jo table, but that isn't feasible right now
+ $priority = 0;
+ foreach ($rider_jos as $rider_jo)
+ {
+ if ($priority < $rider_jo->getPriority())
+ $priority = $rider_jo->getPriority() + 1;
+ }
+
// get current user
$user = $this->security->getUser();
@@ -589,22 +633,47 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
->setServiceType($stype)
->setWarrantyClass($req->request->get('warranty_class'))
->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'))
+ ->setPromoDetail($req->request->get('promo_detail', ''))
->setModeOfPayment($req->request->get('mode_of_payment'))
->setLandmark($req->request->get('landmark'))
->setHub($hub)
->setRider($rider)
- ->setPriority($priority);
+ ->setPriority($priority)
+ ->setResponsibleParty($req->request->get('responsible_party', ''))
+ ->setRiderPlateNum($rider_plate_number);
$jo->addMeta('discount', $discount);
$jo->addMeta('service_charges', $service_charges);
+ // TODO: what happens if hub and rider are changed
+ // and JO is already in_transit or in_progress?
+
+ // retain old jo status if it's an update JO
+ // check old rider if it is also a reassignment
+ // old_rider should be null if JO has been rejected
+ if (($old_rider == null) && ($old_jo_status == null))
+ $jo->setStatus(JOStatus::ASSIGNED);
+ else
+ {
+ error_log('not a new JO');
+ $new_rider = $jo->getRider();
+ if ($new_rider != $old_rider)
+ {
+ // reassignment
+ $jo->setStatus(JOStatus::ASSIGNED);
+ }
+ else
+ {
+ if ($old_jo_status != null)
+ $jo->setStatus($old_jo_status);
+ }
+ }
+
// check if user is null, meaning call to create came from API
if ($user != null)
{
@@ -651,7 +720,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::CREATE)
+ ->setTypeID(CMBJOEventType::CREATE)
->setJobOrder($jo);
if ($user != null)
@@ -661,6 +730,21 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$em->persist($event);
$em->flush();
+
+ // check if JO has been reassigned
+ if ($old_rider != $jo->getRider())
+ //if ($old_jo_status != $jo->getStatus())
+ {
+ error_log('JO has been reassigned');
+ // TODO: refactor later
+ $channel = 'rider/' . $rider->getID() . '/events';
+ $payload = [
+ 'event' => 'new_jo',
+ 'jo_id' => $jo->getID(),
+ ];
+
+ $mclient->publish($channel, json_encode($payload));
+ }
}
}
@@ -775,7 +859,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::HUB_ASSIGN)
+ ->setTypeID(CMBJOEventType::HUB_ASSIGN)
->setJobOrder($obj);
if ($user != null)
@@ -874,7 +958,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::RIDER_ASSIGN)
+ ->setTypeID(CMBJOEventType::RIDER_ASSIGN)
->setJobOrder($obj);
if ($user != null)
@@ -945,7 +1029,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::FULFILL)
+ ->setTypeID(CMBJOEventType::FULFILL)
->setJobOrder($obj);
// get current user
@@ -1043,7 +1127,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::CANCEL)
+ ->setTypeID(CMBJOEventType::CANCEL)
->setJobOrder($obj);
// get current user
@@ -1153,7 +1237,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// add event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::HUB_ASSIGN)
+ ->setTypeID(CMBJOEventType::HUB_ASSIGN)
->setJobOrder($obj);
if ($user != null)
@@ -1353,7 +1437,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// add event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::RIDER_ASSIGN)
+ ->setTypeID(CMBJOEventType::RIDER_ASSIGN)
->setJobOrder($obj);
if ($user != null)
@@ -1425,9 +1509,19 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
public function initializeOneStepForm()
{
- $params['obj'] = new JobOrder();
+ $new_jo = new JobOrder();
+
+ // set time schedule
+ $date_schedule = new DateTime();
+
+ // add 45 minutes to time
+ $date_schedule->add(new DateInterval('PT45M'));
+ $new_jo->setDateSchedule($date_schedule);
+
+ $params['obj'] = $new_jo;
$params['mode'] = 'onestep';
$params['jo_service_charges'] = [];
+ $params['current_date'] = new DateTime();
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
@@ -1448,6 +1542,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$params['mode'] = 'onestep-edit';
$params['cvid'] = $obj->getCustomerVehicle()->getID();
$params['vid'] = $obj->getCustomerVehicle()->getVehicle()->getID();
+ $params['current_date'] = new DateTime();
// get service charges
$sc_array = [];
@@ -1465,6 +1560,53 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$params['jo_service_charges'] = $sc_array;
+ // get odometer
+ $odometer = $obj->getMeta('odometer');
+
+ $params['odometer'] = $odometer;
+
+ // get customer email used in JO
+ $email = $obj->getMeta('customer_email');
+
+ $params['email'] = $email;
+
+ // get images if any
+ $jo_extra = $obj->getJOExtra();
+ $pic_array = [];
+ $params['signature'] = null;
+ if ($jo_extra != null)
+ {
+ $img_1 = $jo_extra->getImage1Filename();
+ $img_2 = $jo_extra->getImage2Filename();
+ $img_3 = $jo_extra->getImage3Filename();
+ $img_4 = $jo_extra->getImage4Filename();
+
+ $other_images = $jo_extra->getOtherImages();
+
+ $cust_signature = $jo_extra->getCustomerSignature();
+
+ if ($img_1 != null)
+ $pic_array['image_1'] = $img_1;
+ if ($img_2 != null)
+ $pic_array['image_2'] = $img_2;
+ if ($img_3 != null)
+ $pic_array['image_3'] = $img_3;
+ if ($img_4 != null)
+ $pic_array['image_4'] = $img_4;
+
+ if ($other_images != null)
+ {
+ foreach ($other_images as $img)
+ {
+ $pic_array['other_images'][] = $img;
+ }
+ }
+
+ $params['signature'] = $cust_signature;
+ }
+
+ $params['jo_pictures'] = $pic_array;
+
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
@@ -1587,7 +1729,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// get template to display
// check transaction origin if walkin
- if ($obj->getSource() == TransactionOrigin::WALK_IN)
+ if ($obj->getSource() == CMBTransactionOrigin::WALK_IN)
$params['template'] = $this->getTwigTemplate('jo_walkin_form');
else
$params['template'] = $this->getTwigTemplate('jo_onestep_form');
@@ -2427,8 +2569,19 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
public function initializeWalkinForm()
{
- $params['obj'] = new JobOrder();
+ $new_jo = new JobOrder();
+
+ // set time schedule
+ $date_schedule = new DateTime();
+
+ // add 45 minutes to time
+ $date_schedule->add(new DateInterval('PT45M'));
+ $new_jo->setDateSchedule($date_schedule);
+
+ $params['obj'] = $new_jo;
$params['mode'] = 'walk-in';
+ $params['discounts'] = $this->generateDiscountOptions();
+ $params['current_date'] = new DateTime();
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
@@ -2454,22 +2607,21 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$jo = new JobOrder();
}
+ // check if responsible party is present
+ if (empty($req->request->get('responsible_party')))
+ $error_array['responsible_party'] = 'Responsible party is required.';
+
// check if new customer
if ($req->request->get('new_customer', false))
{
- if (empty($req->request->get('customer_customer_notes')))
- {
- $error_array['customer_customer_notes'] = 'Customer notes cannot be null.';
- }
-
// validate mobile phone
- $valid_mobile = $this->cust_handler->validateMobileNumber($req->request->get('customer_phone_mobile'));
+ $valid_mobile = $this->cust_handler->validateMobileNumber($req->request->get('phone_mobile'));
if (!($valid_mobile))
- $error_array['customer_phone_mobile'] = 'Invalid mobile phone number.';
+ $error_array['phone_mobile'] = 'Invalid mobile phone number.';
// check if plate number is in request
- if (empty($req->request->get('cv_plate')))
- $error_array['cv_plate'] = 'Plate number is required.';
+ if (empty(trim($req->request->get('plate_number'))))
+ $error_array['plate_number'] = 'Plate number is required.';
// find the vehicle using vid
$new_vehicle = $em->getRepository(Vehicle::class)->find($req->request->get('vid'));
@@ -2484,17 +2636,17 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$new_cust = new Customer();
$new_cv = new CustomerVehicle();
- $new_cust->setLastName($req->request->get('customer_last_name'))
- ->setFirstName($req->request->get('customer_first_name'))
- ->setPhoneMobile($req->request->get('customer_phone_mobile'))
- ->setPhoneLandline($req->request->get('customer_phone_landline'))
- ->setPhoneOffice($req->request->get('customer_phone_office'))
- ->setPhoneFax($req->request->get('customer_phone_fax'))
- ->setCustomerNotes($req->request->get('customer_customer_notes'));
+ $new_cust->setLastName($req->request->get('last_name'))
+ ->setFirstName($req->request->get('first_name'))
+ ->setPhoneMobile($req->request->get('phone_mobile'))
+ ->setPhoneLandline($req->request->get('phone_landline'))
+ ->setPhoneOffice($req->request->get('phone_office'))
+ ->setPhoneFax($req->request->get('phone_fax'))
+ ->setCustomerNotes($req->request->get('customer_notes'));
$new_cv->setCustomer($new_cust)
->setVehicle($new_vehicle)
- ->setPlateNumber($req->request->get('cv_plate'))
+ ->setPlateNumber(trim($req->request->get('plate_number')))
->setModelYear($req->request->get('cv_year'))
->setColor('')
->setStatusCondition('')
@@ -2563,8 +2715,8 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// get discount and set to meta
$discount = $req->request->get('invoice_discount');
- // check if discount is greater than 50 or negative number
- if (($discount > 50) || ($discount < 0))
+ // check if discount is greater than 60 or negative number
+ if (($discount > 60) || ($discount < 0))
$error_array['invoice_discount'] = 'Invalid discount specified';
if (empty($error_array))
@@ -2584,13 +2736,14 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
->setTier1Notes($req->request->get('tier1_notes'))
->setTier2Notes($req->request->get('tier2_notes'))
->setORName($req->request->get('or_name'))
- ->setPromoDetail($req->request->get('promo_detail'))
+ ->setPromoDetail($req->request->get('promo_detail', ''))
->setModeOfPayment($req->request->get('mode_of_payment'))
->setLandmark($req->request->get('landmark'))
->setDeliveryAddress('Walk-in')
->setLandmark('Walk-in')
->setCoordinates($hub_coordinates)
- ->setHub($hub);
+ ->setHub($hub)
+ ->setResponsibleParty($req->request->get('responsible_party', ''));
$jo->addMeta('discount', $discount);
@@ -2640,7 +2793,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
// the event
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::CREATE)
+ ->setTypeID(CMBJOEventType::CREATE)
->setJobOrder($jo);
if ($user != null)
@@ -2719,6 +2872,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$params['mode'] = 'walk-in-edit';
$params['cvid'] = $obj->getCustomerVehicle()->getID();
$params['vid'] = $obj->getCustomerVehicle()->getVehicle()->getID();
+ $params['current_date'] = new DateTime();
$this->fillDropdownParameters($params);
$this->fillFormTags($params);
@@ -2756,7 +2910,10 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$params['trade_in_types'] = CMBTradeInType::getCollection();
$params['facilitated_types'] = FacilitatedType::getCollection();
$params['facilitated_hubs'] = $fac_hubs;
- $params['sources'] = TransactionOrigin::getCollection();
+ $params['sources'] = CMBTransactionOrigin::getCollection();
+
+ $params['model_years'] = $this->generateYearOptions();
+ $params['discounts'] = $this->generateDiscountOptions();
}
protected function initFormTags(&$params)
@@ -2843,6 +3000,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$this->template_hash['jo_walkin_form'] = 'job-order/cmb.form.walkin.html.twig';
$this->template_hash['jo_walkin_edit_form'] = 'job-order/cmb.form.walkin.html.twig';
$this->template_hash['jo_popup'] = 'job-order/cmb.popup.html.twig';
+ $this->template_hash['jo_behind_schedule'] = 'job-order/cmb.list.behindschedule.html.twig';
}
protected function checkTier($tier)
@@ -2898,6 +3056,14 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
$unlock_route = '';
$jo_status = '';
break;
+ case 'behind_schedule':
+ $tier_key = 'jo_behind_schedule';
+ $tier_name = 'Behind Schedule';
+ $rows_route = 'jo_behind_schedule_rows';
+ $edit_route = '';
+ $unlock_route = '';
+ $jo_status = JOStatus::ASSIGNED;
+ break;
default:
throw new AccessDeniedHttpException('No access.');
}
@@ -3028,6 +3194,53 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
->setParameter('date_start', $date_start)
->setParameter('date_end', $date_end);
}
+ break;
+ case 'behind_schedule':
+ if (isset($datatable['query']['data-rows-search']))
+ {
+ $query->innerJoin('q.cus_vehicle', 'cv')
+ ->innerJoin('q.customer', 'c')
+ ->where('cv.plate_number like :filter')
+ ->orWhere('c.phone_mobile like :filter')
+ ->orWhere('c.first_name like :filter or c.last_name like :filter')
+ ->setParameter('filter', $datatable['query']['data-rows-search'] . '%');
+ }
+ if (isset($datatable['query']['rider']))
+ {
+ $query->innerJoin('q.rider', 'r')
+ ->andWhere('r.id = :rider_id')
+ ->setParameter('rider_id', $datatable['query']['rider']);
+ }
+ if (isset($datatable['query']['schedule_date']))
+ {
+ $start = $datatable['query']['schedule_date'][0] . ' ' . '00:00:00';
+ $end = $datatable['query']['schedule_date'][1] . ' ' . '23:59:00';
+
+ $date_start = DateTime::createFromFormat('m/d/Y H:i:s', $start);
+ $date_end = DateTime::createFromFormat('m/d/Y H:i:s', $end);
+
+ $query->andWhere('q.date_schedule >= :date_start')
+ ->andWhere('q.date_schedule <= :date_end')
+ ->setParameter('date_start', $date_start)
+ ->setParameter('date_end', $date_end);
+ }
+ // status should be assigned
+ $query->andWhere('q.status = :status')
+ ->setParameter('status', $status);
+
+ $current_datetime = new DateTime();
+ $other_date = new DateTime();
+
+ $interval = new DateInterval('PT15M');
+ $other_date->add($interval);
+
+ //error_log('current datetime ' . $current_datetime->format('Y-m-d H:i:s'));
+ //error_log('other_date ' . $other_date->format('Y-m-d H:i:s'));
+
+ $query->andWhere('q.date_schedule < :current_datetime OR q.date_schedule <= :other_date')
+ ->setParameter('current_datetime', $current_datetime)
+ ->setParameter('other_date', $other_date);
+
break;
default:
$query->where('q.status = :status')
@@ -3042,9 +3255,21 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
throw new NotFoundHttpException('The item does not exist');
// check transaction origin
- if ($jo->getSource() == TransactionOrigin::WALK_IN)
+ if ($jo->getSource() == CMBTransactionOrigin::WALK_IN)
return 'jo_walkin_edit_form';
else
return 'jo_onestep_edit_form';
}
+
+ protected function generateDiscountOptions()
+ {
+ $discount_start = 0;
+ return range($discount_start, 60);
+ }
+
+ protected function generateYearOptions()
+ {
+ $start_year = 1950;
+ return range($start_year, date("Y"));
+ }
}
diff --git a/src/Service/JobOrderHandlerInterface.php b/src/Service/JobOrderHandlerInterface.php
index 590a0926..22320c18 100644
--- a/src/Service/JobOrderHandlerInterface.php
+++ b/src/Service/JobOrderHandlerInterface.php
@@ -25,7 +25,7 @@ interface JobOrderHandlerInterface
public function generateJobOrder(Request $req, int $id);
// process one step job order
- public function processOneStepJobOrder(Request $req, int $id);
+ //public function processOneStepJobOrder(Request $req, int $id, MQTTClient $mclient);
// dispatch job order
public function dispatchJobOrder(Request $req, int $id, MQTTClient $mclient);
diff --git a/src/Service/NotificationManager.php b/src/Service/NotificationManager.php
new file mode 100644
index 00000000..86de0348
--- /dev/null
+++ b/src/Service/NotificationManager.php
@@ -0,0 +1,48 @@
+redis = $redis_prov->getRedisClient();
+ $this->redis_mqtt_key = $redis_mqtt_key;
+ $this->em = $em;
+ }
+
+ // set user_id to 0 for all
+ public function sendNotification($user_id, $msg, $url)
+ {
+ // send mqtt
+ $chan = $this->getChannel($user_id);
+ $data = $chan . '|' . $msg;
+ $this->redis->lpush($this->redis_mqtt_key, $data);
+
+ // create notif
+ $notif = new Notification();
+ $notif->setUserID($user_id)
+ ->setIcon('')
+ ->setMessage($msg)
+ ->setURL($url);
+
+ // save to db
+ $this->em->persist($notif);
+ $this->em->flush();
+ }
+
+ protected function getChannel($user_id)
+ {
+ return str_replace('{user_id}', $user_id, self::NOTIF_KEY );
+ }
+}
diff --git a/src/Service/RiderAPIHandler/CMBRiderAPIHandler.php b/src/Service/RiderAPIHandler/CMBRiderAPIHandler.php
index 3c0236e1..9a7c0b28 100644
--- a/src/Service/RiderAPIHandler/CMBRiderAPIHandler.php
+++ b/src/Service/RiderAPIHandler/CMBRiderAPIHandler.php
@@ -5,14 +5,16 @@ namespace App\Service\RiderAPIHandler;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use App\Ramcar\CMBServiceType;
-use App\Ramcar\CMBTradeInType;
+use App\Ramcar\TradeInType;
use App\Ramcar\JOStatus;
-use App\Ramcar\JOEventType;
+use App\Ramcar\CMBJOEventType;
use App\Ramcar\InvoiceStatus;
-use App\Ramcar\ModeOfPayment;
+use App\Ramcar\CMBModeOfPayment;
use App\Ramcar\InvoiceCriteria;
+use App\Ramcar\CMBCancelReason;
use App\Service\RiderAPIHandlerInterface;
use App\Service\RedisClientProvider;
@@ -21,6 +23,8 @@ use App\Service\MQTTClient;
use App\Service\WarrantyHandler;
use App\Service\JobOrderHandlerInterface;
use App\Service\InvoiceGeneratorInterface;
+use App\Service\RiderTracker;
+use App\Service\NotificationManager;
use App\Entity\RiderSession;
use App\Entity\Rider;
@@ -30,12 +34,16 @@ use App\Entity\Promo;
use App\Entity\Battery;
use App\Entity\BatteryModel;
use App\Entity\BatterySize;
-use App\Entity\Warranty;
+use App\Entity\JobOrder;
+use App\Entity\JOExtra;
use DateTime;
+use DateInterval;
class CMBRiderAPIHandler implements RiderAPIHandlerInterface
{
+ // NOTE: Rider's constructor sets flag_available and flag_active to true, by default.
+
protected $em;
protected $redis;
protected $ef;
@@ -46,12 +54,17 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
protected $jo_handler;
protected $ic;
protected $session;
+ protected $upload_dir;
+ protected $nm;
+ protected $router;
public function __construct(EntityManagerInterface $em, RedisClientProvider $redis,
EncoderFactoryInterface $ef, RiderCache $rcache,
string $country_code, MQTTClient $mclient,
WarrantyHandler $wh, JobOrderHandlerInterface $jo_handler,
- InvoiceGeneratorInterface $ic)
+ InvoiceGeneratorInterface $ic, string $upload_dir,
+ RiderTracker $rider_tracker, NotificationManager $nm,
+ UrlGeneratorInterface $router)
{
$this->em = $em;
$this->redis = $redis;
@@ -62,6 +75,10 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$this->wh = $wh;
$this->jo_handler = $jo_handler;
$this->ic = $ic;
+ $this->upload_dir = $upload_dir;
+ $this->rider_tracker = $rider_tracker;
+ $this->nm = $nm;
+ $this->router = $router;
// one device = one session, since we have control over the devices
// when a rider logs in, we just change the rider assigned to the device
@@ -82,11 +99,16 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
{
$params = implode(', ', $missing);
$data = [
+ 'title' => 'Failed Registration',
'error' => 'Missing parameter(s): ' . $params
];
return $data;
}
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
// retry until we get a unique id
while (true)
{
@@ -143,12 +165,16 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
];
$data = $this->checkParamsAndKey($req, $required_params);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Login';
return $data;
+ }
// check if session has a rider already
if ($this->session->hasRider())
{
$data = [
+ 'title' => 'Failed Login',
'error' => 'Another rider is already logged in. Please logout first.'
];
return $data;
@@ -159,6 +185,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
if ($rider == null)
{
$data = [
+ 'title' => 'Failed Login',
'error' => 'Invalid username or password.'
];
return $data;
@@ -169,20 +196,26 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
if (!$encoder->isPasswordValid($rider->getPassword(), $req->request->get('pass'), ''))
{
$data = [
+ 'title' => 'Failed Login',
'error' => 'Invalid username or password.'
];
return $data;
}
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
// assign rider to session
$this->session->setRider($rider);
- $rider->setAvailable(true);
+ //$rider->setAvailable(true);
+ $rider->setActive(true);
$rider_id = $rider->getID();
// cache rider location (default to hub)
// TODO: figure out longitude / latitude default
- $this->rcache->addActiveRider($rider_id, 0, 0);
+ // $this->rcache->addActiveRider($rider_id, 0, 0);
// TODO: log rider logging in
@@ -225,14 +258,34 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$required_params = [];
$data = $this->checkParamsAndKey($req, $required_params);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Logout';
return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
// make rider unavailable
$rider = $this->session->getRider();
+
+ // check if rider has assigned JOs
+ $assigned_jos = $this->em->getRepository(JobOrder::class)->findBy(['rider' => $rider->getID(), 'status' => JOStatus::ASSIGNED]);
+ if ($assigned_jos != null)
+ {
+ $data = [
+ 'title' => 'Failed Logout',
+ 'error' => 'Rider has assigned job orders.'
+ ];
+ return $data;
+ }
+
$rider->setAvailable(false);
+ $rider->setActive(false);
// remove from cache
- $this->rcache->removeActiveRider($rider->getID());
+ // $this->rcache->removeActiveRider($rider->getID());
// remove rider from session
$this->session->setRider(null);
@@ -244,45 +297,481 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
return $data;
}
- public function getJobOrder(Request $req)
+ public function goOnline(Request $req)
{
- // get the job order of the rider assigned to this session
$required_params = [];
$data = $this->checkParamsAndKey($req, $required_params);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Go Online';
return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ // set rider to available to take on JOs
+ $rider = $this->session->getRider();
+
+ $rider->setAvailable(true);
+
+ $this->em->flush();
+
+ // cache rider location (default to hub)
+ // TODO: figure out longitude / latitude default
+ $rider_id = $rider->getID();
+ $this->rcache->addActiveRider($rider_id, 0, 0);
+
+ // send mqtt event to put rider on map
+ // get rider coordinates from redis
+ $coord = $this->rider_tracker->getRiderLocation($rider->getID());
+
+ $lng = $coord->getLongitude();
+ $lat = $coord->getLatitude();;
+ $channel = 'rider/' . $rider->getID() . '/availability';
+ $payload = [
+ 'status' => 'rider_online',
+ 'longitude' => $lng,
+ 'latitude' => $lat,
+ ];
+ $this->mclient->publish($channel, json_encode($payload));
+
+ return $data;
+ }
+
+ public function goOffline(Request $req)
+ {
+ $required_params = [];
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Go Offline';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ // set rider to unavailable to take on JOs
+ $rider = $this->session->getRider();
+
+ // check if rider has assigned JOs
+ $assigned_jos = $this->em->getRepository(JobOrder::class)->findBy(['rider' => $rider->getID(), 'status' => JOStatus::ASSIGNED]);
+ if ($assigned_jos != null)
+ {
+ $data = [
+ 'title' => 'Failed Go Offline',
+ 'error' => 'Rider has assigned job orders.'
+ ];
+ return $data;
+ }
+
+ $rider->setAvailable(false);
+
+ $this->em->flush();
+
+ // remove from cache
+ $this->rcache->removeActiveRider($rider->getID());
+
+ // send mqtt event to remove rider from map
+ $channel = 'rider/' . $rider->getID() . '/availability';
+ $payload = [
+ 'status' => 'rider_offline'
+ ];
+ $this->mclient->publish($channel, json_encode($payload));
+
+ return $data;
+
+ }
+
+ public function getJobOrderHistory(Request $req, $period)
+ {
+ $required_params = [];
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Job Order History';
+ return $data;
+ }
// are we logged in?
if (!$this->session->hasRider())
{
$data = [
+ 'title' => 'Failed Get Job Order History',
'error' => 'No logged in rider.'
];
return $data;
}
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
$rider = $this->session->getRider();
- // do we have a job order?
- $jo = $rider->getActiveJobOrder();
- if ($jo == null)
+ // get JOs assigned to rider for the month given
+ // setup start date and end dates
+ // get current year
+ $current_date = new DateTime();
+
+ if ($period == 'lastmonth')
{
- $data = [
- 'job_order' => null
- ];
+ $date_interval = new DateInterval('P1M');
+ $period_date = $current_date->sub($date_interval);
}
else
- {
- $coord = $jo->getCoordinates();
- $cust = $jo->getCustomer();
- $cv = $jo->getCustomerVehicle();
- $v = $cv->getVehicle();
- $inv = $jo->getInvoice();
- $promo = $inv->getPromo();
+ $period_date = $current_date;
- // invoice items
- $inv_items = [];
- foreach ($inv->getItems() as $item)
+ $period_year = $period_date->format('Y');
+ $period_month = $period_date->format('m');
+
+ // get number of days in month requested
+ $last_day = cal_days_in_month(CAL_GREGORIAN, $period_month, $period_year);
+
+ $s_date = $period_year . '-' . $period_month . '-01 00:00:00';
+ $e_date = $period_year . '-' . $period_month . '-' . $last_day . ' 23:59:59';
+
+ $qb = $this->em->getRepository(JobOrder::class)->createQueryBuilder('j');
+
+ $query = $qb->innerJoin('j.rider', 'r')
+ ->where('j.date_schedule >= :start')
+ ->andWhere('j.date_schedule <= :end')
+ ->andWhere('r.id = :rider_id')
+ ->setParameter('start', $s_date)
+ ->setParameter('end', $e_date)
+ ->setParameter('rider_id', $rider->getID())
+ ->getQuery();
+
+ $jo_results = $query->getResult();
+
+ $jo_data = [];
+
+ if (!(empty($jo_results)))
+ {
+ foreach ($jo_results as $jo)
+ {
+ // TODO: refactor this to call formatJobOrderData
+ $coord = $jo->getCoordinates();
+ $cust = $jo->getCustomer();
+ $cv = $jo->getCustomerVehicle();
+ $v = $cv->getVehicle();
+ $inv = $jo->getInvoice();
+ $promo = $inv->getPromo();
+
+ // invoice items
+ $inv_items = [];
+ foreach ($inv->getItems() as $item)
+ {
+ // if more than 1, split it into one of each of the same item
+ $item_qty = $item->getQuantity();
+ for ($i = 0; $i < $item_qty; $i++)
+ {
+ $item_batt = $item->getBattery();
+ if ($item_batt == null)
+ $batt_id = null;
+ else
+ $batt_id = $item_batt->getID();
+
+ $inv_items[] = [
+ 'id' => $item->getID(),
+ 'title' => $item->getTitle(),
+ 'qty' => 1,
+ 'price' => $item->getPrice(),
+ 'batt_id' => $batt_id,
+ ];
+ }
+ }
+
+ // promo
+ if ($promo != null)
+ {
+ $promo_data = [
+ 'id' => $promo->getID(),
+ 'name' => $promo->getName(),
+ 'code' => $promo->getCode(),
+ 'discount_rate' => $promo->getDiscountRate(),
+ 'discount_apply' => $promo->getDiscountApply(),
+ ];
+ }
+ else
+ {
+ $promo_data = null;
+ }
+
+ $trade_in_type = $jo->getTradeInType();
+ if (empty($trade_in_type))
+ $trade_in_type = 'none';
+
+ // get time when JO was accepted by rider
+ $accept_event_type = CMBJOEventType::RIDER_ACCEPT;
+ $rider_id = $this->session->getRider()->getID();
+ $jo_id = $jo->getID();
+ $date_accept = '';
+
+ $accept_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $accept_event_type, 'rider' => $rider_id, 'job_order' => $jo_id], ['date_happen' => 'DESC']);
+ if ($accept_results != null)
+ {
+ $jo_event = current($accept_results);
+ $date_accept = $jo_event->getDateHappen()->format('Ymd H:i:s');
+ }
+
+ // get time when JO was started by rider
+ $start_event_type = CMBJOEventType::RIDER_START;
+ $date_start = '';
+ $start_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $start_event_type, 'rider' => $rider_id, 'job_order' => $jo_id], ['date_happen' => 'DESC']);
+ if ($start_results != null)
+ {
+ $jo_event = current($start_results);
+ $date_start = $jo_event->getDateHappen()->format('Ymd H:i:s');
+ }
+
+ // get time when JO was completed by rider
+ $complete_event_type = CMBJOEventType::RIDER_FINISH;
+ $date_complete = '';
+ $complete_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $complete_event_type, 'rider' => $rider_id, 'job_order' => $jo_id], ['date_happen' => 'DESC']);
+ if ($complete_results != null)
+ {
+ $jo_event = current($complete_results);
+ $date_complete = $jo_event->getDateHappen()->format('Ymd H:i:s');
+ }
+ else
+ {
+ // find the time when photos were uploaded
+ $upload_event_type = CMBJOEventType::RIDER_UPLOAD_PHOTO;
+ $upload_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $upload_event_type, 'rider' => $rider_id, 'job_order' => $jo_id], ['date_happen' => 'DESC']);
+ if ($upload_results != null)
+ {
+ $jo_event = current($upload_results);
+ $date_complete = $jo_event->getDateHappen()->format('Ymd H:i:s');
+ }
+ }
+
+ $jo_data[] = [
+ 'job_order' => [
+ 'id' => $jo->getID(),
+ 'service_type' => $jo->getServiceType(),
+ 'date_schedule' => $jo->getDateSchedule()->format('Ymd H:i:s'),
+ 'longitude' => $coord->getLongitude(),
+ 'latitude' => $coord->getLatitude(),
+ 'status' => $jo->getStatus(),
+ 'customer' => [
+ 'title' => $cust->getTitle(),
+ 'first_name' => $cust->getFirstName(),
+ 'last_name' => $cust->getLastName(),
+ 'phone_mobile' => $this->country_code . $cust->getPhoneMobile(),
+ 'phone_landline' => $this->country_code . $cust->getPhoneLandline(),
+ ],
+ 'vehicle' => [
+ 'manufacturer' => $v->getManufacturer()->getName(),
+ 'make' => $v->getMake(),
+ 'model' => $cv->getModelYear(),
+ 'plate_number' => $cv->getPlateNumber(),
+ 'color' => $cv->getColor(),
+ ],
+ 'or_num' => $jo->getORNum(),
+ 'or_name' => $jo->getORName(),
+ 'delivery_instructions' => $jo->getDeliveryInstructions(),
+ 'delivery_address' => $jo->getDeliveryAddress(),
+ 'landmark' => $jo->getLandmark(),
+ 'invoice' => [
+ 'discount' => $inv->getDiscount(),
+ 'trade_in' => $inv->getTradeIn(),
+ 'total_price' => $inv->getTotalPrice(),
+ 'vat' => $inv->getVat(),
+ 'items' => $inv_items,
+ ],
+ 'mode_of_payment' => $jo->getModeOfPayment(),
+ 'mode_of_payment_display' => CMBModeOfPayment::getName($jo->getModeOfPayment()),
+ 'trade_in_type' => $trade_in_type,
+ 'promo' => $promo_data,
+ // TODO: load the actual
+ 'has_warranty_doc' => false,
+ 'flag_coolant' => $jo->hasCoolant(),
+ 'has_motolite' => $cv->hasMotoliteBattery(),
+ // date and time accepted and started
+ 'date_accept' => $date_accept,
+ 'date_start' => $date_start,
+ 'date_complete' => $date_complete,
+ ]
+ ];
+ }
+ }
+
+ $data = [
+ 'jo_history' => $jo_data,
+ ];
+
+ return $data;
+ }
+
+ public function getAssignedJobOrders(Request $req)
+ {
+ $required_params = [];
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Assigned Job Orders';
+ return $data;
+ }
+
+ // are we logged in?
+ if (!$this->session->hasRider())
+ {
+ $data = [
+ 'title' => 'Failed Get Assigned Job Orders',
+ 'error' => 'No logged in rider.'
+ ];
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $rider = $this->session->getRider();
+
+ $qb = $this->em->getRepository(JobOrder::class)->createQueryBuilder('j');
+
+ $query = $qb->innerJoin('j.rider', 'r')
+ ->andWhere('r.id = :rider_id')
+ ->andWhere('j.status = :status')
+ ->setParameter('rider_id', $rider->getID())
+ ->setParameter('status', JOStatus::ASSIGNED)
+ ->getQuery();
+
+ $jo_results = $query->getResult();
+
+ $jo_data = [];
+
+ if (!(empty($jo_results)))
+ {
+ foreach ($jo_results as $jo)
+ {
+ // TODO: refactor this to call formatJobOrderData
+ $coord = $jo->getCoordinates();
+ $cust = $jo->getCustomer();
+ $cv = $jo->getCustomerVehicle();
+ $v = $cv->getVehicle();
+ $inv = $jo->getInvoice();
+ $promo = $inv->getPromo();
+
+ // invoice items
+ $inv_items = [];
+ foreach ($inv->getItems() as $item)
+ {
+ // if more than 1, split it into one of each of the same item
+ $item_qty = $item->getQuantity();
+ for ($i = 0; $i < $item_qty; $i++)
+ {
+ $item_batt = $item->getBattery();
+ if ($item_batt == null)
+ $batt_id = null;
+ else
+ $batt_id = $item_batt->getID();
+
+ $inv_items[] = [
+ 'id' => $item->getID(),
+ 'title' => $item->getTitle(),
+ 'qty' => 1,
+ 'price' => $item->getPrice(),
+ 'batt_id' => $batt_id,
+ ];
+ }
+ }
+
+ // promo
+ if ($promo != null)
+ {
+ $promo_data = [
+ 'id' => $promo->getID(),
+ 'name' => $promo->getName(),
+ 'code' => $promo->getCode(),
+ 'discount_rate' => $promo->getDiscountRate(),
+ 'discount_apply' => $promo->getDiscountApply(),
+ ];
+ }
+ else
+ {
+ $promo_data = null;
+ }
+
+ $trade_in_type = $jo->getTradeInType();
+ if (empty($trade_in_type))
+ $trade_in_type = 'none';
+
+ $jo_data[] = [
+ 'job_order' => [
+ 'id' => $jo->getID(),
+ 'service_type' => $jo->getServiceType(),
+ 'date_schedule' => $jo->getDateSchedule()->format('Ymd H:i:s'),
+ 'longitude' => $coord->getLongitude(),
+ 'latitude' => $coord->getLatitude(),
+ 'status' => $jo->getStatus(),
+ 'customer' => [
+ 'title' => $cust->getTitle(),
+ 'first_name' => $cust->getFirstName(),
+ 'last_name' => $cust->getLastName(),
+ 'phone_mobile' => $this->country_code . $cust->getPhoneMobile(),
+ 'phone_landline' => $this->country_code . $cust->getPhoneLandline(),
+ ],
+ 'vehicle' => [
+ 'manufacturer' => $v->getManufacturer()->getName(),
+ 'make' => $v->getMake(),
+ 'model' => $cv->getModelYear(),
+ 'plate_number' => $cv->getPlateNumber(),
+ 'color' => $cv->getColor(),
+ ],
+ 'or_num' => $jo->getORNum(),
+ 'or_name' => $jo->getORName(),
+ 'delivery_instructions' => $jo->getDeliveryInstructions(),
+ 'delivery_address' => $jo->getDeliveryAddress(),
+ 'landmark' => $jo->getLandmark(),
+ 'invoice' => [
+ 'discount' => $inv->getDiscount(),
+ 'trade_in' => $inv->getTradeIn(),
+ 'total_price' => $inv->getTotalPrice(),
+ 'vat' => $inv->getVat(),
+ 'items' => $inv_items,
+ ],
+ 'mode_of_payment' => $jo->getModeOfPayment(),
+ 'mode_of_payment_display' => CMBModeOfPayment::getName($jo->getModeOfPayment()),
+ 'trade_in_type' => $trade_in_type,
+ 'promo' => $promo_data,
+ // TODO: load the actual
+ 'has_warranty_doc' => false,
+ 'flag_coolant' => $jo->hasCoolant(),
+ 'has_motolite' => $cv->hasMotoliteBattery(),
+ ]
+ ];
+ }
+ }
+
+ $data = [
+ 'assigned_jos' => $jo_data,
+ ];
+
+ return $data;
+ }
+
+ protected function formatJobOrderData($req, $jo)
+ {
+ $coord = $jo->getCoordinates();
+ $cust = $jo->getCustomer();
+ $cv = $jo->getCustomerVehicle();
+ $v = $cv->getVehicle();
+ $inv = $jo->getInvoice();
+ $promo = $inv->getPromo();
+
+ // invoice items
+ $inv_items = [];
+ foreach ($inv->getItems() as $item)
+ {
+ // if more than 1, split it into one of each of the same item
+ $item_qty = $item->getQuantity();
+ for ($i = 0; $i < $item_qty; $i++)
{
$item_batt = $item->getBattery();
if ($item_batt == null)
@@ -290,90 +779,258 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
else
$batt_id = $item_batt->getID();
- $inv_items[] = [
+ $inv_items[] = [
'id' => $item->getID(),
'title' => $item->getTitle(),
- 'qty' => $item->getQuantity(),
+ 'qty' => 1,
'price' => $item->getPrice(),
'batt_id' => $batt_id,
];
}
+ }
- // promo
- if ($promo != null)
- {
- $promo_data = [
- 'id' => $promo->getID(),
- 'name' => $promo->getName(),
- 'code' => $promo->getCode(),
- 'discount_rate' => $promo->getDiscountRate(),
- 'discount_apply' => $promo->getDiscountApply(),
- ];
- }
- else
- {
- $promo_data = null;
- }
-
- $trade_in_type = $jo->getTradeInType();
- if (empty($trade_in_type))
- $trade_in_type = 'none';
-
- $data = [
- 'job_order' => [
- 'id' => $jo->getID(),
- 'service_type' => $jo->getServiceType(),
- 'date_schedule' => $jo->getDateSchedule()->format('Ymd H:i:s'),
- 'longitude' => $coord->getLongitude(),
- 'latitude' => $coord->getLatitude(),
- 'status' => $jo->getStatus(),
- 'customer' => [
- 'title' => $cust->getTitle(),
- 'first_name' => $cust->getFirstName(),
- 'last_name' => $cust->getLastName(),
- 'phone_mobile' => $this->country_code . $cust->getPhoneMobile(),
- ],
- 'vehicle' => [
- 'manufacturer' => $v->getManufacturer()->getName(),
- 'make' => $v->getMake(),
- 'model' => $cv->getModelYear(),
- 'plate_number' => $cv->getPlateNumber(),
- 'color' => $cv->getColor(),
- ],
- 'or_num' => $jo->getORNum(),
- 'or_name' => $jo->getORName(),
- 'delivery_instructions' => $jo->getDeliveryInstructions(),
- 'delivery_address' => $jo->getDeliveryAddress(),
- 'landmark' => $jo->getLandmark(),
- 'invoice' => [
- 'discount' => $inv->getDiscount(),
- 'trade_in' => $inv->getTradeIn(),
- 'total_price' => $inv->getTotalPrice(),
- 'vat' => $inv->getVat(),
- 'items' => $inv_items,
- ],
- 'mode_of_payment' => $jo->getModeOfPayment(),
- 'trade_in_type' => $trade_in_type,
- 'promo' => $promo_data,
- // TODO: load the actual
- 'has_warranty_doc' => false,
- 'flag_coolant' => $jo->hasCoolant(),
- 'has_motolite' => $cv->hasMotoliteBattery(),
- ]
+ // promo
+ if ($promo != null)
+ {
+ $promo_data = [
+ 'id' => $promo->getID(),
+ 'name' => $promo->getName(),
+ 'code' => $promo->getCode(),
+ 'discount_rate' => $promo->getDiscountRate(),
+ 'discount_apply' => $promo->getDiscountApply(),
];
}
+ else
+ {
+ $promo_data = null;
+ }
+
+ $trade_in_type = $jo->getTradeInType();
+ if (empty($trade_in_type))
+ $trade_in_type = 'none';
+
+ // get date of status last change
+ if ($jo->getDateStatusChange() == null)
+ $date_status_change = null;
+ else
+ $date_status_change = $jo->getDateStatusChange()->format('Ymd H:i:s');
+
+ // get odometer reading
+ $odo = $jo->getMeta('odometer');
+ if ($odo <= 0)
+ $odo = 0;
+
+ $images = [
+ 'image_1' => null,
+ 'image_2' => null,
+ 'image_3' => null,
+ 'image_4' => null,
+ 'others' => [],
+ ];
+ $jo_extra = $jo->getJOExtra();
+ if ($jo_extra != null)
+ {
+ $images['image_1'] = $this->getURLExtraImage($req, $jo_extra->getImage1Filename());
+ $images['image_2'] = $this->getURLExtraImage($req, $jo_extra->getImage2Filename());
+ $images['image_3'] = $this->getURLExtraImage($req, $jo_extra->getImage3Filename());
+ $images['image_4'] = $this->getURLExtraImage($req, $jo_extra->getImage4Filename());
+
+ // other images
+ $other_images = [];
+ foreach ($jo_extra->getOtherImages() as $others)
+ {
+ $other_images[] = $this->getURLExtraImage($req, $others);
+ }
+ $images['others'] = $other_images;
+ }
+
+ // customer email
+ if ($jo->getMeta('customer_email') == null)
+ $cust_email = '';
+ else
+ $cust_email = $jo->getMeta('customer_email');
+
+ // get time when JO was accepted by rider
+ $accept_event_type = CMBJOEventType::RIDER_ACCEPT;
+ $rider_id = $this->session->getRider()->getID();
+ $jo_id = $jo->getID();
+ $date_accept = '';
+
+ $accept_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $accept_event_type, 'rider' => $rider_id, 'job_order' => $jo_id], ['date_happen' => 'DESC']);
+ if ($accept_results != null)
+ {
+ $jo_event = current($accept_results);
+ $date_accept = $jo_event->getDateHappen()->format('Ymd H:i:s');
+ }
+
+ // get time when JO was started by rider
+ $start_event_type = CMBJOEventType::RIDER_START;
+ $date_start = '';
+ $start_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $start_event_type, 'rider' => $rider_id, 'job_order' => $jo_id], ['date_happen' => 'DESC']);
+ if ($start_results != null)
+ {
+ $jo_event = current($start_results);
+ $date_start = $jo_event->getDateHappen()->format('Ymd H:i:s');
+ }
+
+ // get time when JO was completed by rider
+ $complete_event_type = CMBJOEventType::RIDER_FINISH;
+ $date_complete = '';
+ $complete_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $complete_event_type, 'rider' => $rider_id, 'job_order' => $jo_id], ['date_happen' => 'DESC']);
+ if ($complete_results != null)
+ {
+ $jo_event = current($complete_results);
+ $date_complete = $jo_event->getDateHappen()->format('Ymd H:i:s');
+ }
+ else
+ {
+ // find the time when photos were uploaded
+ $upload_event_type = CMBJOEventType::RIDER_UPLOAD_PHOTO;
+ $upload_results = $this->em->getRepository(JOEvent::class)->findby(['type_id' => $upload_event_type, 'rider' => $rider_id, 'job_order' => $jo_id], ['date_happen' => 'DESC']);
+ if ($upload_results != null)
+ {
+ $jo_event = current($upload_results);
+ $date_complete = $jo_event->getDateHappen()->format('Ymd H:i:s');
+ }
+ }
+
+ $data = [
+ 'id' => $jo->getID(),
+ 'service_type' => $jo->getServiceType(),
+ 'date_schedule' => $jo->getDateSchedule()->format('Ymd H:i:s'),
+ 'longitude' => $coord->getLongitude(),
+ 'latitude' => $coord->getLatitude(),
+ 'status' => $jo->getStatus(),
+ 'date_status_change' => $date_status_change,
+ 'customer' => [
+ 'title' => $cust->getTitle(),
+ 'first_name' => $cust->getFirstName(),
+ 'last_name' => $cust->getLastName(),
+ 'phone_mobile' => $this->country_code . $cust->getPhoneMobile(),
+ 'phone_landline' => $this->country_code . $cust->getPhoneLandline(),
+ ],
+ 'vehicle' => [
+ 'manufacturer' => $v->getManufacturer()->getName(),
+ 'make' => $v->getMake(),
+ 'model' => $cv->getModelYear(),
+ 'plate_number' => $cv->getPlateNumber(),
+ 'color' => $cv->getColor(),
+ ],
+ 'or_num' => $jo->getORNum(),
+ 'or_name' => $jo->getORName(),
+ 'delivery_instructions' => $jo->getDeliveryInstructions(),
+ 'delivery_address' => $jo->getDeliveryAddress(),
+ 'landmark' => $jo->getLandmark(),
+ 'invoice' => [
+ 'discount' => $inv->getDiscount(),
+ 'trade_in' => $inv->getTradeIn(),
+ 'total_price' => $inv->getTotalPrice(),
+ 'vat' => $inv->getVat(),
+ 'items' => $inv_items,
+ ],
+ 'mode_of_payment' => $jo->getModeOfPayment(),
+ 'mode_of_payment_display' => CMBModeOfPayment::getName($jo->getModeOfPayment()),
+ 'trade_in_type' => $trade_in_type,
+ 'promo' => $promo_data,
+ // TODO: load the actual
+ 'has_warranty_doc' => false,
+ 'flag_coolant' => $jo->hasCoolant(),
+ 'has_motolite' => $cv->hasMotoliteBattery(),
+
+ // BEGIN: cmb specific details
+
+ // odometer
+ 'odometer' => $odo,
+
+ // images
+ 'finish_photos' => $images,
+
+ // customer email
+ 'customer_email' => $cust_email,
+
+ // date and time accepted and started
+ 'date_accept' => $date_accept,
+ 'date_start' => $date_start,
+ 'date_complete' => $date_complete,
+
+ // END: cmb specific details
+ ];
+
+ return $data;
+ }
+
+ public function getJobOrder(Request $req)
+ {
+ $required_params = [
+ 'jo_id'
+ ];
+ $data = $this->checkJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Job Order';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $jo_data = $this->formatJobOrderData($req, $jo);
+
+ $data = [
+ 'job_order' => $jo_data
+ ];
return $data;
}
public function acceptJobOrder(Request $req)
{
- $required_params = ['jo_id'];
+ $required_params = [
+ 'jo_id'
+ ];
$data = $this->checkJO($req, $required_params, $jo);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Accept Job Order';
return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ // TODO: put JO in job queue
// TODO: refactor this into a jo handler class, so we don't have to repeat for control center
+ // TODO: send mqtt event (?)
+ // add event log
+ $rider = $this->session->getRider();
+ $event = new JOEvent();
+ $event->setDateHappen(new DateTime())
+ ->setTypeID(CMBJOEventType::RIDER_ACCEPT)
+ ->setJobOrder($jo)
+ ->setRider($rider);
+ $this->em->persist($event);
+
+ $this->em->flush();
+
+ return $data;
+ }
+
+ public function setJobOrderInTransit(Request $req)
+ {
+ $required_params = ['jo_id'];
+ $data = $this->checkActiveJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Set Job Order in Transit';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
// set jo status to in transit
$jo->setStatus(JOStatus::IN_TRANSIT);
@@ -384,7 +1041,81 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$rider = $this->session->getRider();
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::RIDER_ACCEPT)
+ ->setTypeID(CMBJOEventType::RIDER_IN_TRANSIT)
+ ->setJobOrder($jo)
+ ->setRider($rider);
+ $this->em->persist($event);
+
+ $this->em->flush();
+
+ return $data;
+
+ }
+
+ public function cancelJobOrder(Request $req)
+ {
+ $required_params = [
+ 'jo_id',
+ 'cancel_reason'
+ ];
+ $data = $this->checkJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Job Order Cancellation';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $cancel_reason = $req->request->get('cancel_reason');
+
+ $jo->cancel($cancel_reason);
+
+ // add event log
+ $rider = $this->session->getRider();
+ $event = new JOEvent();
+ $event->setDateHappen(new DateTime())
+ ->setTypeID(CMBJOEventType::REQUEUE)
+ ->setJobOrder($jo)
+ ->setRider($rider);
+ $this->em->persist($event);
+
+ $this->em->flush();
+
+ // notification
+ $notif_url = $this->router->generate('jo_onestep_edit_form', ['id' => $jo->getID()]);
+ $cancel_reason = CMBCancelReason::getName($jo->getCancelReason());
+ $this->nm->sendNotification(0, 'Job order #' . $jo->getID() . ' has been cancelled by rider. Reason: ' . $cancel_reason . '.', $notif_url);
+
+ return $data;
+ }
+
+ public function performJobOrder(Request $req)
+ {
+ $required_params = [
+ 'jo_id',
+ ];
+
+ $data = $this->checkJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Job Order Perform';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $jo->perform();
+
+ // add event log
+ $rider = $this->session->getRider();
+ $event = new JOEvent();
+ $event->setDateHappen(new DateTime())
+ ->setTypeID(CMBJOEventType::PERFORM)
->setJobOrder($jo)
->setRider($rider);
$this->em->persist($event);
@@ -394,26 +1125,39 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
return $data;
}
- public function cancelJobOrder(Request $req)
+ public function rejectJobOrder(Request $req)
{
- $required_params = ['jo_id'];
+ $required_params = [
+ 'jo_id'
+ ];
$data = $this->checkJO($req, $required_params, $jo);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Job Order Rejection';
return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
- // $jo->cancel("rider cancelled");
- // requeue it, instead of cancelling it
$jo->requeue();
+ // set rider to null
+ $jo->clearRider();
+
// add event log
$rider = $this->session->getRider();
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::REQUEUE)
+ ->setTypeID(CMBJOEventType::REQUEUE)
->setJobOrder($jo)
->setRider($rider);
$this->em->persist($event);
+ // set rider's active JO to null
+ $rider->setActiveJobOrder();
+
$this->em->flush();
// send mqtt event
@@ -424,15 +1168,31 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
];
$this->mclient->sendEvent($jo, $payload);
+ // send available event to change rider icon status
+ $channel = 'rider/' . $rider->getID() . '/status';
+ $rider_status = 'available';
+
+ $this->mclient->publish($channel, $rider_status);
+
+ $notif_url = $this->router->generate('jo_onestep_edit_form', ['id' => $jo->getID()]);
+ $this->nm->sendNotification(0, 'Job order #' . $jo->getID() . ' has been rejected by rider.', $notif_url);
+
return $data;
}
public function arrive(Request $req)
{
$required_params = ['jo_id'];
- $data = $this->checkJO($req, $required_params, $jo);
+ $data = $this->checkActiveJO($req, $required_params, $jo);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Arrive';
return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
// TODO: refactor this into a jo handler class, so we don't have to repeat for control center
@@ -443,7 +1203,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$rider = $this->session->getRider();
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::RIDER_ARRIVE)
+ ->setTypeID(CMBJOEventType::RIDER_ARRIVE)
->setJobOrder($jo)
->setRider($rider);
$this->em->persist($event);
@@ -484,7 +1244,9 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
return $data;
}
- // TODO: tag rider as available
+ // tag rider as available
+ $rider = $this->session->getRider();
+ $rider->setAvailable(true);
$this->em->flush();
@@ -493,91 +1255,43 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
public function payment(Request $req)
{
- $required_params = ['jo_id'];
- $data = $this->checkJO($req, $required_params, $jo);
+ $required_params = [
+ 'jo_id',
+ 'mode_of_payment',
+ ];
+ $data = $this->checkActiveJO($req, $required_params, $jo);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Payment';
return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ // add mode of payment
+ $mode = $req->request->get('mode_of_payment');
+ $jo->setModeOfPayment($mode);
+
+ // set status to paid
+ $jo->setStatus(JOStatus::PAID);
+
// set invoice to paid
$jo->getInvoice()->setStatus(InvoiceStatus::PAID);
- /*
- // set jo status to fulfilled
- $jo->setStatus(JOStatus::FULFILLED);
- */
- $jo->fulfill();
-
// add event log
$rider = $this->session->getRider();
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::FULFILL)
+ ->setTypeID(CMBJOEventType::PAID)
->setJobOrder($jo)
->setRider($rider);
$this->em->persist($event);
- // TODO: tag rider as unavailable
-
- // save to customer vehicle battery record
- $this->jo_handler->updateVehicleBattery($jo);
-
$this->em->flush();
- // create warranty
- if($this->jo_handler->checkIfNewBattery($jo))
- {
- $serial = $jo->getCustomerVehicle()->getWarrantyCode();
- $warranty_class = $jo->getWarrantyClass();
- $first_name = $jo->getCustomer()->getFirstName();
- $last_name = $jo->getCustomer()->getLastName();
- $mobile_number = $jo->getCustomer()->getPhoneMobile();
-
- // check if date fulfilled is null
- if ($jo->getDateFulfill() == null)
- $date_purchase = $jo->getDateCreate();
- else
- $date_purchase = $jo->getDateFulfill();
-
- // validate plate number
- // $plate_number = $this->wh->cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber());
- $plate_number = Warranty::cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber());
- if ($plate_number != false)
- {
- $batt_list = array();
- $invoice = $jo->getInvoice();
- if (!empty($invoice))
- {
- // get battery
- $invoice_items = $invoice->getItems();
- foreach ($invoice_items as $item)
- {
- $battery = $item->getBattery();
- if ($battery != null)
- {
- $batt_list[] = $item->getBattery();
- }
- }
- }
-
- $this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
- }
- }
-
- // send mqtt event (fulfilled)
- $rider = $this->session->getRider();
- $image_url = $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/assets/images/user.gif';
- if ($rider->getImageFile() != null)
- $image_url = $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/uploads/' . $rider->getImageFile();
-
- $payload = [
- 'event' => 'fulfilled',
- 'jo_id' => $jo->getID(),
- 'driver_image' => $image_url,
- 'driver_name' => $rider->getFullName(),
- 'driver_id' => $rider->getID(),
- ];
- $this->mclient->sendEvent($jo, $payload);
-
return $data;
}
@@ -586,7 +1300,10 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$required_params = [];
$data = $this->checkParamsAndKey($req, $required_params);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Available Rider';
return $data;
+ }
// make rider available
$this->session->getRider()->setAvailable(true);
@@ -602,7 +1319,10 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$required_params = [];
$data = $this->checkParamsAndKey($req, $required_params);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Promos';
return $data;
+ }
$promos = $this->em->getRepository(Promo::class)->findAll();
@@ -629,7 +1349,10 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$required_params = [];
$data = $this->checkParamsAndKey($req, $required_params);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Batteries';
return $data;
+ }
$batts = $this->em->getRepository(Battery::class)->findAll();
$models = $this->em->getRepository(BatteryModel::class)->findAll();
@@ -678,17 +1401,21 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$this->debugRequest($req);
// allow rider to change service, promo, battery and trade-in options
- $required_params = ['jo_id', 'stype_id', 'promo_id'];
+ $required_params = ['jo_id', 'service_type', 'promo_id'];
$data = $this->checkJO($req, $required_params, $jo);
if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Service Change';
return $data;
+ }
// check service type
- $stype_id = $req->request->get('stype_id');
- if (!CMBServiceType::validate($stype_id))
+ $service_type = $req->request->get('service_type');
+ if (!CMBServiceType::validate($service_type))
{
$data = [
- 'error' => 'Invalid service type - ' . $stype_id
+ 'title' => 'Failed Service Change',
+ 'error' => 'Invalid service type - ' . $service_type
];
return $data;
}
@@ -704,6 +1431,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
if ($promo == null)
{
$data = [
+ 'title' => 'Failed Service Change',
'error' => 'Invalid promo id - ' . $promo_id
];
return $data;
@@ -742,6 +1470,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
if ($battery == null)
{
$data = [
+ 'title' => 'Failed Service Change',
'error' => 'Invalid battery id - ' . $batt_id
];
return $data;
@@ -750,18 +1479,19 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
// check trade in
$trade_in = $req->request->get('trade_in');
- if (!CMBTradeInType::validate($trade_in))
+ if (!TradeInType::validate($trade_in))
$trade_in = null;
// check mode of payment
+ // TODO: defaulting to credit_card for now if mode of payment is invalid
$mode = $req->request->get('mode_of_payment');
- if (!ModeOfPayment::validate($mode))
- $mode = ModeOfPayment::CASH;
+ if (!CMBModeOfPayment::validate($mode))
+ $mode = CMBModeOfPayment::CASH;
$jo->setModeOfPayment($mode);
// generate new invoice
$crit = new InvoiceCriteria();
- $crit->setServiceType($stype_id);
+ $crit->setServiceType($service_type);
$crit->setCustomerVehicle($cv);
$crit->setHasCoolant($jo->hasCoolant());
@@ -782,7 +1512,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$this->em->flush();
// save job order
- $jo->setServiceType($stype_id);
+ $jo->setServiceType($service_type);
// save invoice
$jo->setInvoice($invoice);
@@ -792,7 +1522,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$rider = $this->session->getRider();
$event = new JOEvent();
$event->setDateHappen(new DateTime())
- ->setTypeID(JOEventType::RIDER_EDIT)
+ ->setTypeID(CMBJOEventType::RIDER_EDIT)
->setJobOrder($jo)
->setRider($rider);
$this->em->persist($event);
@@ -803,6 +1533,680 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
return $data;
}
+ public function generateInvoice(Request $req)
+ {
+ $required_params = ['jo_id'];
+ $data = $this->checkActiveJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Invoice Generation';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $inv = $jo->getInvoice();
+ $promo = $inv->getPromo();
+
+ // invoice items
+ $inv_items = [];
+ foreach ($inv->getItems() as $item)
+ {
+ // if more than 1, split it into one of each of the same item
+ $item_qty = $item->getQuantity();
+ for ($i = 0; $i < $item_qty; $i++)
+ {
+ $item_batt = $item->getBattery();
+ if ($item_batt == null)
+ $batt_id = null;
+ else
+ $batt_id = $item_batt->getID();
+
+ $inv_items[] = [
+ 'id' => $item->getID(),
+ 'title' => $item->getTitle(),
+ 'qty' => 1,
+ 'price' => $item->getPrice(),
+ 'batt_id' => $batt_id,
+ ];
+ }
+ }
+
+ // promo
+ if ($promo != null)
+ {
+ $promo_data = [
+ 'id' => $promo->getID(),
+ 'name' => $promo->getName(),
+ 'code' => $promo->getCode(),
+ 'discount_rate' => $promo->getDiscountRate(),
+ 'discount_apply' => $promo->getDiscountApply(),
+ ];
+ }
+ else
+ {
+ $promo_data = null;
+ }
+
+ $trade_in_type = $jo->getTradeInType();
+ if (empty($trade_in_type))
+ $trade_in_type = 'none';
+
+ $data = [
+ 'invoice' => [
+ 'id' => $inv->getID(),
+ 'discount' => $inv->getDiscount(),
+ 'trade_in' => $inv->getTradeIn(),
+ 'total_price' => $inv->getTotalPrice(),
+ 'vat' => $inv->getVat(),
+ 'items' => $inv_items,
+ 'trade_in_type' => $trade_in_type,
+ 'promo' => $promo_data,
+ ]
+ ];
+
+ return $data;
+ }
+
+ public function startJobOrder(Request $req)
+ {
+ $required_params = ['jo_id'];
+ $data = $this->checkActiveJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Job Order Start';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ // set jo status to in progress
+ $jo->setStatus(JOStatus::IN_PROGRESS);
+
+ // add event log
+ $rider = $this->session->getRider();
+ $event = new JOEvent();
+ $event->setDateHappen(new DateTime())
+ ->setTypeID(CMBJOEventType::RIDER_START)
+ ->setJobOrder($jo)
+ ->setRider($rider);
+ $this->em->persist($event);
+
+ $this->em->flush();
+
+ return $data;
+ }
+
+ public function completeJobOrder(Request $req)
+ {
+ $required_params = ['jo_id'];
+ $data = $this->checkActiveJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Job Order Completion';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ // set customer signature
+ $jo_extra = $jo->getJOExtra();
+ $sig_file = $this->handleFileUpload($req, 'signature');
+ if ($sig_file)
+ $jo_extra->setCustomerSignature($sig_file);
+
+ // set email
+ $cust_email = $req->request->get('customer_email', '');
+ $jo->addMeta('customer_email', $cust_email);
+
+
+ /*
+ // set jo status to fulfilled
+ $jo->setStatus(JOStatus::FULFILLED);
+ */
+ //$jo->fulfill();
+ $jo->setStatus(JOStatus::FULFILLED)
+ ->setDateFulfill(new DateTime());
+
+ // add event log
+ $rider = $this->session->getRider();
+ $event = new JOEvent();
+ $event->setDateHappen(new DateTime())
+ ->setTypeID(CMBJOEventType::FULFILL)
+ ->setJobOrder($jo)
+ ->setRider($rider);
+ $this->em->persist($event);
+
+ $rider_event = new JOEvent();
+ $rider_event->setDateHappen(new DateTime())
+ ->setTypeID(CMBJOEventType::RIDER_FINISH)
+ ->setJobOrder($jo)
+ ->setRider($rider);
+ $this->em->persist($rider_event);
+
+ // save to customer vehicle battery record
+ $this->jo_handler->updateVehicleBattery($jo);
+
+ $this->em->flush();
+
+ // create warranty
+ if($this->jo_handler->checkIfNewBattery($jo))
+ {
+ $serial = null;
+ $warranty_class = $jo->getWarrantyClass();
+ $first_name = $jo->getCustomer()->getFirstName();
+ $last_name = $jo->getCustomer()->getLastName();
+ $mobile_number = $jo->getCustomer()->getPhoneMobile();
+
+ // check if date fulfilled is null
+ //if ($jo->getDateFulfill() == null)
+ // $date_purchase = $jo->getDateCreate();
+ //else
+ // $date_purchase = $jo->getDateFulfill();
+
+ // use date_schedule for warranty expiration computation
+ $date_purchase = $jo->getDateSchedule();
+
+ $plate_number = $this->wh->cleanPlateNumber($jo->getCustomerVehicle()->getPlateNumber());
+
+ $batt_list = array();
+ $invoice = $jo->getInvoice();
+ if (!empty($invoice))
+ {
+ // get battery
+ $invoice_items = $invoice->getItems();
+ foreach ($invoice_items as $item)
+ {
+ $battery = $item->getBattery();
+ if ($battery != null)
+ {
+ $batt_list[] = $item->getBattery();
+ }
+ }
+ }
+
+ $this->wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
+ }
+
+ // TODO: Need to verify if needed.
+ // send mqtt event (fulfilled)
+ $image_url = $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/assets/images/user.gif';
+ if ($rider->getImageFile() != null)
+ $image_url = $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/uploads/' . $rider->getImageFile();
+
+ $payload = [
+ 'event' => 'fulfilled',
+ 'jo_id' => $jo->getID(),
+ 'driver_image' => $image_url,
+ 'driver_name' => $rider->getFullName(),
+ 'driver_id' => $rider->getID(),
+ ];
+ $this->mclient->sendEvent($jo, $payload);
+
+ return $data;
+ }
+
+ public function setActiveJobOrder(Request $req)
+ {
+ $required_params = [
+ 'jo_id'
+ ];
+ $data = $this->checkJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Set Active Job Order';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $rider = $this->session->getRider();
+ $rider->setActiveJobOrder($jo);
+
+ $this->em->persist($rider);
+ $this->em->flush();
+
+ return $data;
+ }
+
+ public function setOdometer(Request $req)
+ {
+ $required_params = [
+ 'jo_id',
+ 'odometer'
+ ];
+ $data = $this->checkActiveJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Set Odometer';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $odometer_reading = $req->request->get('odometer');
+
+ if ($odometer_reading > 999999)
+ {
+ $data = [
+ 'title' => 'Failed Set Odometer',
+ 'error' => 'Odometer cannot be more than 6 figures.',
+ ];
+ return $data;
+ }
+
+ $jo->addMeta('odometer', $odometer_reading);
+
+ $this->em->flush();
+
+ return $data;
+ }
+
+ protected function handleFileUpload($req, $name)
+ {
+ $file = $req->files->get($name);
+ if (empty($file))
+ return false;
+
+ $orig_filename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
+
+ // generate prefix to assure uniqueness
+ $prefix = uniqid() . '_';
+ $new_filename = $prefix . $orig_filename . '.' . $file->guessClientExtension();
+
+ // move to our upload dir
+ $dest = $this->upload_dir;
+ try
+ {
+ $file->move($dest, $new_filename);
+ }
+ catch (FileException $e)
+ {
+ return false;
+ }
+
+ return $new_filename;
+ }
+
+ public function uploadFinishPhotos(Request $req)
+ {
+ $required_params = [
+ 'jo_id',
+ ];
+ $data = $this->checkActiveJO($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Upload Finish Photos';
+ return $data;
+ }
+
+ $rider = $this->session->getRider();
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $dest = $this->upload_dir;
+
+ $img_1_file = $req->files->get('img_1');
+ $img_2_file = $req->files->get('img_2');
+ $img_3_file = $req->files->get('img_3');
+ $img_4_file = $req->files->get('img_4');
+ $other_img_files[]= $req->files->get('other_images');
+
+ if ((empty($img_1_file)) &&
+ (empty($img_2_file)) &&
+ (empty($img_3_file)) &&
+ (empty($img_4_file)))
+ {
+ $data = [
+ 'title' => 'Failed Upload Arrive Photos',
+ 'error' => 'No image files received.'
+ ];
+ return $data;
+ }
+ else
+ {
+ $new_img1_filename = '';
+ $new_img2_filename = '';
+ $new_img3_filename = '';
+ $new_img4_filename = '';
+ $other_filenames = [];
+
+ if (!empty($img_1_file))
+ {
+ $orig_img1_filename = pathinfo($img_1_file->getClientOriginalName(), PATHINFO_FILENAME);
+ $new_img1_filename = uniqid() . '-'. $orig_img1_filename . '.' . $img_1_file->guessClientExtension();
+
+ try
+ {
+ $img_1_file->move($dest, $new_img1_filename);
+ }
+ catch (FileException $e)
+ {
+ $data = [
+ 'error' => 'Error saving image files.'
+ ];
+ return $data;
+ }
+ }
+ if (!empty($img_2_file))
+ {
+ $orig_img2_filename = pathinfo($img_2_file->getClientOriginalName(), PATHINFO_FILENAME);
+ $new_img2_filename = uniqid() . '-' . $orig_img2_filename . '.' . $img_2_file->guessClientExtension();
+
+ try
+ {
+ $img_2_file->move($dest, $new_img2_filename);
+ }
+ catch (FileException $e)
+ {
+ $data = [
+ 'error' => 'Error saving image files.'
+ ];
+ return $data;
+ }
+ }
+ if (!empty($img_3_file))
+ {
+ $orig_img3_filename = pathinfo($img_3_file->getClientOriginalName(), PATHINFO_FILENAME);
+ $new_img3_filename = uniqid() . '-' . $orig_img3_filename . '.' . $img_3_file->guessClientExtension();
+
+ try
+ {
+ $img_3_file->move($dest, $new_img3_filename);
+ }
+ catch (FileException $e)
+ {
+ $data = [
+ 'error' => 'Error saving image files.'
+ ];
+ return $data;
+ }
+ }
+
+ if (!empty($img_4_file))
+ {
+ $orig_img4_filename = pathinfo($img_4_file->getClientOriginalName(), PATHINFO_FILENAME);
+ $new_img4_filename = uniqid() . '-' . $orig_img4_filename . '.' . $img_4_file->guessClientExtension();
+
+ try
+ {
+ $img_4_file->move($dest, $new_img4_filename);
+ }
+ catch (FileException $e)
+ {
+ $data = [
+ 'error' => 'Error saving image files.'
+ ];
+ return $data;
+ }
+ }
+
+ foreach ($other_img_files as $other_img_file)
+ {
+ if (!(empty($other_img_file)))
+ {
+ foreach($other_img_file as $other_img)
+ {
+ $orig_other_filename = pathinfo($other_img->getClientOriginalName(), PATHINFO_FILENAME);
+ $new_other_filename = uniqid() . '-'. $orig_other_filename . '.' . $other_img->guessClientExtension();
+
+ $other_filenames[] = $new_other_filename;
+
+ try
+ {
+ $other_img->move($dest, $new_other_filename);
+ }
+ catch (FileException $e)
+ {
+ $data = [
+ 'error' => 'Error saving image files.'
+ ];
+ return $data;
+ }
+ }
+ }
+ }
+
+ $jo_extra = $jo->getJOExtra();
+ if ($jo_extra == null)
+ {
+ // create JOExtra entity
+ $jo_extra = new JOExtra();
+
+ $jo_extra->setImage1Filename($new_img1_filename);
+ $jo_extra->setImage2Filename($new_img2_filename);
+ $jo_extra->setImage3Filename($new_img3_filename);
+ $jo_extra->setImage4Filename($new_img4_filename);
+
+ if (empty($other_filenames))
+ {
+ $jo_extra->clearOtherImages();
+ }
+ else
+ {
+ $jo_extra->setOtherImages($other_filenames);
+ }
+
+ $jo->setJOExtra($jo_extra);
+
+ $this->em->persist($jo_extra);
+ }
+ else
+ {
+ $jo_extra->setImage1Filename($new_img1_filename);
+ $jo_extra->setImage2Filename($new_img2_filename);
+ $jo_extra->setImage3Filename($new_img3_filename);
+ $jo_extra->setImage4Filename($new_img4_filename);
+
+ if (empty($other_filenames))
+ {
+ $jo_extra->clearOtherImages();
+ }
+ else
+ {
+ $jo_extra->setOtherImages($other_filenames);
+ }
+ }
+
+ // create event for upload photo
+ $rider_event = new JOEvent();
+ $rider_event->setDateHappen(new DateTime())
+ ->setTypeID(CMBJOEventType::RIDER_UPLOAD_PHOTO)
+ ->setJobOrder($jo)
+ ->setRider($rider);
+ $this->em->persist($rider_event);
+
+ $this->em->flush();
+ }
+
+ return $data;
+ }
+
+ public function getStatus(Request $req)
+ {
+ $required_params = [];
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Status';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $rider = $this->session->getRider();
+
+ $rider_status = $rider->isAvailable();
+
+ $status = 'Offline';
+ if ($rider_status)
+ $status = 'Online';
+
+ $data = [
+ 'status' => $status,
+ ];
+
+ return $data;
+ }
+
+ public function getOngoingJobOrder(Request $req)
+ {
+ $required_params = [];
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Ongoing Job Order';
+ $data['job_order'] = [];
+ return $data;
+ }
+
+ // are we logged in?
+ if (!$this->session->hasRider())
+ {
+ $data = [
+ 'title' => 'Failed Get Ongoing Job Order',
+ 'error' => 'No logged in rider.',
+ 'job_order' => null,
+ ];
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $rider = $this->session->getRider();
+
+ // check if we have an active JO
+ $jo = $rider->getRiderActiveJobOrder();
+ if ($jo == null)
+ {
+ $data = [
+ //'title' => 'Failed Get Ongoing Job Order',
+ //'error' => 'No active job order.',
+ 'job_order' => [],
+ ];
+ return $data;
+ }
+
+ $jo_data = [];
+ // check if JO status is in_progress, in_transit, performed, paid
+ switch($jo->getStatus())
+ {
+ case JOStatus::IN_TRANSIT:
+ case JOStatus::IN_PROGRESS:
+ case JOStatus::PERFORMED:
+ case JOStatus::PAID:
+ $jo_data = $this->formatJobOrderData($req, $jo);
+ break;
+ }
+
+ $data = [
+ 'job_order' => $jo_data
+ ];
+
+ return $data;
+ }
+
+ public function getPaymentMethods(Request $req)
+ {
+ $required_params = [];
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Payment Methods';
+ return $data;
+ }
+
+ $data = [
+ 'payment_methods' => CMBModeOfPayment::getCollection(),
+ ];
+
+ return $data;
+
+ }
+
+ public function getCancelReasons(Request $req)
+ {
+ $required_params = [];
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Get Cancel Reasons';
+ return $data;
+ }
+
+ $data = [
+ 'cancel_reasons' => CMBCancelReason::getCollection(),
+ ];
+
+ return $data;
+
+ }
+
+ public function verifyJobOrder(Request $req)
+ {
+ $required_params = [
+ 'jo_id'
+ ];
+ $data = $this->checkJOForVerify($req, $required_params, $jo);
+ if (isset($data['error']))
+ {
+ $data['title'] = 'Failed Verify Job Order';
+ return $data;
+ }
+
+ // get longitude and latitude
+ // TODO: right now, we're just checking if we get the coordinates
+ $this->getHeaderCoordinates($req);
+
+ $rider = $this->session->getRider();
+ // check if rider is assigned to JO
+ if ($jo->getRider() != null)
+ {
+ if ($rider->getID() != $jo->getRider()->getID())
+ {
+ $data = [
+ 'assigned' => false,
+ 'available' => false,
+ ];
+ return $data;
+ }
+ }
+
+ // check if JO status is not fulfilled and not cancelled
+ if (($jo->getStatus() == JOStatus::FULFILLED) ||
+ ($jo->getStatus() == JOStatus::CANCELLED))
+ {
+ $data = [
+ 'assigned' => true,
+ 'available' => false
+ ];
+ return $data;
+ }
+
+ $data = [
+ 'assigned' => true,
+ 'available' => true,
+ ];
+
+ return $data;
+ }
+
protected function checkMissingParameters(Request $req, $params = [])
{
$missing = [];
@@ -880,7 +2284,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
return $session;
}
- protected function checkJO(Request $req, $required_params, &$jo = null)
+ protected function checkActiveJO(Request $req, $required_params, &$jo = null)
{
// set jo status to in transit
$data = $this->checkParamsAndKey($req, $required_params);
@@ -899,7 +2303,7 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
$rider = $this->session->getRider();
// check if we have an active JO
- $jo = $rider->getActiveJobOrder();
+ $jo = $rider->getRiderActiveJobOrder();
if ($jo == null)
{
$data = [
@@ -907,9 +2311,14 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
];
return $data;
}
+ $jo_id = '';
+ if ($req->getMethod() == 'GET')
+ $jo_id = $req->query->get('jo_id');
+ else
+ $jo_id = $req->request->get('jo_id');
// check if the jo_id sent is the same as our active jo
- if ($req->request->get('jo_id') != $jo->getID())
+ if ($jo_id != $jo->getID())
{
$data = [
'error' => 'Job order selected is not active job order.'
@@ -920,9 +2329,142 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface
return $data;
}
+ protected function checkJO(Request $req, $required_params, &$jo = null)
+ {
+ // set jo status to in transit
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ return $data;
+
+ // are we logged in?
+ if (!$this->session->hasRider())
+ {
+ $data = [
+ 'error' => 'No logged in rider.'
+ ];
+ return $data;
+ }
+
+ $rider = $this->session->getRider();
+
+ // get jo
+ $jo_id = '';
+ if ($req->getMethod() == 'GET')
+ $jo_id = $req->query->get('jo_id');
+ else
+ $jo_id = $req->request->get('jo_id');
+
+ $jo = $this->em->getRepository(JobOrder::class)->find($jo_id);
+ if ($jo == null)
+ {
+ $data = [
+ 'error' => 'No job order found.'
+ ];
+ return $data;
+
+ }
+
+ // check if rider assigned to jo is our rider
+ if ($jo->getRider() == null)
+ {
+ $data = [
+ 'error' => 'Job order selected has no rider assigned.'
+ ];
+ return $data;
+ }
+
+ // check if rider is assigned to JO
+ if ($rider->getID() != $jo->getRider()->getID())
+ {
+ $data = [
+ 'error' => 'Job order selected is not assigned to rider'
+ ];
+ return $data;
+ }
+
+ return $data;
+ }
+
+ protected function checkJOForVerify(Request $req, $required_params, &$jo = null)
+ {
+ // set jo status to in transit
+ $data = $this->checkParamsAndKey($req, $required_params);
+ if (isset($data['error']))
+ return $data;
+
+ // are we logged in?
+ if (!$this->session->hasRider())
+ {
+ $data = [
+ 'error' => 'No logged in rider.'
+ ];
+ return $data;
+ }
+
+ $rider = $this->session->getRider();
+
+ // get jo
+ $jo_id = '';
+ if ($req->getMethod() == 'GET')
+ $jo_id = $req->query->get('jo_id');
+ else
+ $jo_id = $req->request->get('jo_id');
+
+ $jo = $this->em->getRepository(JobOrder::class)->find($jo_id);
+ if ($jo == null)
+ {
+ $data = [
+ 'error' => 'No job order found.'
+ ];
+ return $data;
+
+ }
+
+ // check if rider assigned to jo is our rider
+ if ($jo->getRider() == null)
+ {
+ $data = [
+ 'error' => 'Job order selected has no rider assigned.'
+ ];
+ return $data;
+ }
+
+ /*
+ // check if rider is assigned to JO
+ if ($rider->getID() != $jo->getRider()->getID())
+ {
+ $data = [
+ 'error' => 'Job order selected is not assigned to rider'
+ ];
+ return $data;
+ } */
+
+ return $data;
+ }
+
protected function debugRequest(Request $req)
{
$all = $req->request->all();
error_log(print_r($all, true));
}
+
+ protected function getURLExtraImage(Request $req, $filename)
+ {
+ // return null if blank filename
+ if (strlen(trim($filename)) <= 0)
+ return null;
+
+ return $req->getScheme() . '://' . $req->getHttpHost() . $req->getBasePath() . '/uploads/jo_extra/' . $filename;
+ }
+
+ protected function getHeaderCoordinates(Request $req)
+ {
+ $x_lng = $req->headers->get('x-longitude');
+ $x_lat = $req->headers->get('x-latitude');
+
+ // TODO: this is still unfinished
+
+ //error_log('rider_longitude ' . $x_lng);
+ //error_log('rider_latitude ' . $x_lat);
+ }
}
diff --git a/src/Service/RiderCache.php b/src/Service/RiderCache.php
index cdd18864..af5ca716 100644
--- a/src/Service/RiderCache.php
+++ b/src/Service/RiderCache.php
@@ -2,6 +2,8 @@
namespace App\Service;
+use Doctrine\ORM\EntityManagerInterface;
+
use App\Service\RedisClientProvider;
use App\Entity\Rider;
@@ -10,12 +12,14 @@ class RiderCache
protected $redis;
protected $loc_key;
protected $status_key;
+ protected $em;
- public function __construct(RedisClientProvider $redis_prov, $loc_key, $status_key)
+ public function __construct(EntityManagerInterface $em, RedisClientProvider $redis_prov, $loc_key, $status_key)
{
$this->redis = $redis_prov->getRedisClient();
$this->loc_key = $loc_key;
$this->status_key = $status_key;
+ $this->em = $em;
}
public function addActiveRider($id, $lat, $lng)
@@ -46,10 +50,15 @@ class RiderCache
$lng = $data[1][0];
$lat = $data[1][1];
- $locs[$id] = [
- 'longitude' => $lng,
- 'latitude' => $lat,
- ];
+ // get rider details so we can check for availability
+ $rider = $this->getRiderDetails($id);
+ if ($rider != null)
+ {
+ $locs[$id] = [
+ 'longitude' => $lng,
+ 'latitude' => $lat,
+ ];
+ }
}
// error_log(print_r($all_riders, true));
@@ -73,4 +82,17 @@ class RiderCache
{
$this->redis->hincrby($this->status_key, $id, -1);
}
+
+ protected function getRiderDetails($id)
+ {
+ $rider = $this->em->getRepository(Rider::class)->find($id);
+ if ($rider == null)
+ return null;
+
+ // return only if available
+ if ($rider->isAvailable())
+ return $rider;
+
+ return null;
+ }
}
diff --git a/templates/base.html.twig b/templates/base.html.twig
index 5e5abfc2..37fcfc4d 100644
--- a/templates/base.html.twig
+++ b/templates/base.html.twig
@@ -448,217 +448,50 @@
+
+
@@ -770,6 +605,26 @@
+
+
+
+
{% block scripts %}{% endblock %}