Merge branch '270-final-cmb-fixes' of gitlab.com:jankstudio/resq into 311-cmb-refactor-rapicontroller-into-a-service
This commit is contained in:
commit
74c239cfd6
44 changed files with 1512 additions and 375 deletions
2
README.md
Normal file
2
README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
MQTT format for location updates:
|
||||||
|
`<latitude>:<longitude>`
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
"predis/predis": "^1.1",
|
"predis/predis": "^1.1",
|
||||||
"sensio/framework-extra-bundle": "^5.1",
|
"sensio/framework-extra-bundle": "^5.1",
|
||||||
"setasign/fpdf": "^1.8",
|
"setasign/fpdf": "^1.8",
|
||||||
|
"symfony/asset": "^4.0",
|
||||||
"symfony/console": "^4.0",
|
"symfony/console": "^4.0",
|
||||||
"symfony/debug": "^4.0",
|
"symfony/debug": "^4.0",
|
||||||
"symfony/filesystem": "^4.0",
|
"symfony/filesystem": "^4.0",
|
||||||
|
|
|
||||||
62
composer.lock
generated
62
composer.lock
generated
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"_readme": [
|
"_readme": [
|
||||||
"This file locks the dependencies of your project to a known state",
|
"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"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "4873ae3fd18db755bc9bf395bbbfb141",
|
"content-hash": "b101ecfbc1f6f2270f0e8ad326035b7e",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "catalyst/auth-bundle",
|
"name": "catalyst/auth-bundle",
|
||||||
|
|
@ -2411,6 +2411,62 @@
|
||||||
],
|
],
|
||||||
"time": "2016-01-01T17:47:15+00:00"
|
"time": "2016-01-01T17:47:15+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/asset",
|
||||||
|
"version": "v4.4.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/asset.git",
|
||||||
|
"reference": "2c67c89d064bfb689ea6bc41217c87100bb94c17"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/asset/zipball/2c67c89d064bfb689ea6bc41217c87100bb94c17",
|
||||||
|
"reference": "2c67c89d064bfb689ea6bc41217c87100bb94c17",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/http-foundation": "^3.4|^4.0|^5.0",
|
||||||
|
"symfony/http-kernel": "^3.4|^4.0|^5.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/http-foundation": ""
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "4.4-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Component\\Asset\\": ""
|
||||||
|
},
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony Asset Component",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"time": "2020-01-04T13:00:46+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/cache",
|
"name": "symfony/cache",
|
||||||
"version": "v4.3.1",
|
"version": "v4.3.1",
|
||||||
|
|
@ -5344,6 +5400,7 @@
|
||||||
"code",
|
"code",
|
||||||
"zf2"
|
"zf2"
|
||||||
],
|
],
|
||||||
|
"abandoned": "laminas/laminas-code",
|
||||||
"time": "2018-08-13T20:36:59+00:00"
|
"time": "2018-08-13T20:36:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -5398,6 +5455,7 @@
|
||||||
"events",
|
"events",
|
||||||
"zf2"
|
"zf2"
|
||||||
],
|
],
|
||||||
|
"abandoned": "laminas/laminas-eventmanager",
|
||||||
"time": "2018-04-25T15:33:34+00:00"
|
"time": "2018-04-25T15:33:34+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@ security:
|
||||||
methods: [GET]
|
methods: [GET]
|
||||||
security: false
|
security: false
|
||||||
|
|
||||||
|
tracker:
|
||||||
|
pattern: ^\/track\/
|
||||||
|
methods: [GET]
|
||||||
|
security: false
|
||||||
|
|
||||||
api:
|
api:
|
||||||
pattern: ^\/api\/
|
pattern: ^\/api\/
|
||||||
security: false
|
security: false
|
||||||
|
|
|
||||||
|
|
@ -201,3 +201,8 @@ jo_ajax_popup:
|
||||||
controller: App\Controller\JobOrderController::popupInfo
|
controller: App\Controller\JobOrderController::popupInfo
|
||||||
methods: [GET]
|
methods: [GET]
|
||||||
|
|
||||||
|
jo_tracker:
|
||||||
|
path: /track/{id}
|
||||||
|
controller: App\Controller\JobOrderController::tracker
|
||||||
|
methods: [GET]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ services:
|
||||||
App\Service\MQTTClient:
|
App\Service\MQTTClient:
|
||||||
arguments:
|
arguments:
|
||||||
$redis_client: "@App\\Service\\RedisClientProvider"
|
$redis_client: "@App\\Service\\RedisClientProvider"
|
||||||
|
$key: "mqtt_events"
|
||||||
|
|
||||||
App\Service\APNSClient:
|
App\Service\APNSClient:
|
||||||
arguments:
|
arguments:
|
||||||
|
|
@ -87,7 +88,6 @@ services:
|
||||||
$host: "%env(REDIS_CLIENT_HOST)%"
|
$host: "%env(REDIS_CLIENT_HOST)%"
|
||||||
$port: "%env(REDIS_CLIENT_PORT)%"
|
$port: "%env(REDIS_CLIENT_PORT)%"
|
||||||
$password: "%env(REDIS_CLIENT_PASSWORD)%"
|
$password: "%env(REDIS_CLIENT_PASSWORD)%"
|
||||||
$env_flag: "dev"
|
|
||||||
|
|
||||||
App\Service\GeofenceTracker:
|
App\Service\GeofenceTracker:
|
||||||
arguments:
|
arguments:
|
||||||
|
|
@ -205,3 +205,28 @@ services:
|
||||||
App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet"
|
App\Service\GISManagerInterface: "@App\\Service\\GISManager\\OpenStreet"
|
||||||
#App\Service\GISManagerInterface: "@App\\Service\\GISManager\\Google"
|
#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)%"
|
||||||
|
|
|
||||||
|
|
@ -331,4 +331,31 @@ span.has-danger,
|
||||||
.map-div-icon i.awesome {
|
.map-div-icon i.awesome {
|
||||||
margin: 12px auto;
|
margin: 12px auto;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-info {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 9999;
|
||||||
|
padding: 1.5em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-info > .m-portlet {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-info .m-portlet__body {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-info .rider-image {
|
||||||
|
width: 4.8rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-info .m-badge {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
171
public/assets/js/dashboard_map.js
Normal file
171
public/assets/js/dashboard_map.js
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
class DashboardMap {
|
||||||
|
constructor(options, rider_markers, cust_markers) {
|
||||||
|
this.options = options;
|
||||||
|
this.rider_markers = rider_markers;
|
||||||
|
this.cust_markers = cust_markers;
|
||||||
|
|
||||||
|
// layer groups
|
||||||
|
this.layer_groups = {
|
||||||
|
'rider_available': L.layerGroup(),
|
||||||
|
'rider_active_jo': L.layerGroup(),
|
||||||
|
'customer': L.layerGroup()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
// main map
|
||||||
|
this.map = L.map(this.options.div_id).setView(
|
||||||
|
[this.options.center_lat, this.options.center_lng],
|
||||||
|
this.options.zoom
|
||||||
|
);
|
||||||
|
|
||||||
|
// add tile layer
|
||||||
|
var streets = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
|
||||||
|
attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|
||||||
|
maxZoom: 18,
|
||||||
|
id: 'mapbox/streets-v11',
|
||||||
|
accessToken: this.options.access_token
|
||||||
|
}).addTo(this.map);
|
||||||
|
|
||||||
|
// layer groups
|
||||||
|
this.layer_groups.rider_available.addTo(this.map);
|
||||||
|
this.layer_groups.rider_active_jo.addTo(this.map);
|
||||||
|
this.layer_groups.customer.addTo(this.map);
|
||||||
|
|
||||||
|
// base layer
|
||||||
|
var baseMaps = {
|
||||||
|
'Streets': streets
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.options.display_overlay) {
|
||||||
|
// overlay layer
|
||||||
|
var overlayMaps = {
|
||||||
|
'Available Riders' : this.layer_groups.rider_available,
|
||||||
|
'JO Riders' : this.layer_groups.rider_active_jo,
|
||||||
|
'Customers' : this.layer_groups.customer
|
||||||
|
}
|
||||||
|
|
||||||
|
L.control.layers(baseMaps, overlayMaps).addTo(this.map);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.map;
|
||||||
|
}
|
||||||
|
|
||||||
|
putMarker(id, lat, lng, markers, icon, layer_group, popup_url) {
|
||||||
|
var my = this;
|
||||||
|
// existing marker
|
||||||
|
if (markers.hasOwnProperty(id)) {
|
||||||
|
markers[id].setLatLng(L.latLng(lat, lng));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// new marker
|
||||||
|
markers[id] = L.marker(
|
||||||
|
[lat, lng],
|
||||||
|
{ icon: icon }
|
||||||
|
).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,
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
|
this.cust_markers,
|
||||||
|
this.options.icons.customer,
|
||||||
|
this.layer_groups.customer,
|
||||||
|
this.options.cust_popup_url
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCustomerMarker(id) {
|
||||||
|
console.log('removing customer marker for ' + id);
|
||||||
|
var layer_group = this.layer_groups.customer;
|
||||||
|
var markers = this.cust_markers;
|
||||||
|
|
||||||
|
// no customer marker with that id
|
||||||
|
if (!markers.hasOwnProperty(id)) {
|
||||||
|
console.log('no such marker to remove');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer_group.removeLayer(markers[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLocations(location_url) {
|
||||||
|
console.log(this.rider_markers);
|
||||||
|
var my = this;
|
||||||
|
$.ajax({
|
||||||
|
url: location_url,
|
||||||
|
}).done(function(response) {
|
||||||
|
// clear all markers
|
||||||
|
my.layer_groups.rider_available.clearLayers();
|
||||||
|
my.layer_groups.rider_active_jo.clearLayers();
|
||||||
|
my.layer_groups.customer.clearLayers();
|
||||||
|
|
||||||
|
// get riders and job orders
|
||||||
|
var riders = response.riders;
|
||||||
|
var jos = response.jos;
|
||||||
|
|
||||||
|
// job orders
|
||||||
|
$.each(jos, function(id, data) {
|
||||||
|
var lat = data.latitude;
|
||||||
|
var lng = data.longitude;
|
||||||
|
|
||||||
|
my.putCustomerMarker(id, lat, lng);
|
||||||
|
});
|
||||||
|
|
||||||
|
// riders
|
||||||
|
$.each(riders, function(id, data) {
|
||||||
|
var lat = data.latitude;
|
||||||
|
var lng = data.longitude;
|
||||||
|
|
||||||
|
if (data.has_jo)
|
||||||
|
my.putRiderActiveJOMarker(id, lat, lng);
|
||||||
|
else
|
||||||
|
my.putRiderAvailableMarker(id, lat, lng);
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log(rider_markers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
114
public/assets/js/map_mqtt.js
Normal file
114
public/assets/js/map_mqtt.js
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
class MapEventHandler {
|
||||||
|
constructor(options, dashmap) {
|
||||||
|
this.options = options;
|
||||||
|
this.dashmap = dashmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(user_id, host, port) {
|
||||||
|
var d = new Date();
|
||||||
|
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);
|
||||||
|
var options = {
|
||||||
|
// useSSL: true,
|
||||||
|
timeout: 3,
|
||||||
|
invocationContext: this,
|
||||||
|
onSuccess: this.onConnect.bind(this),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.mqtt.onMessageArrived = this.onMessage.bind(this);
|
||||||
|
|
||||||
|
console.log('connecting to mqtt server...');
|
||||||
|
this.mqtt.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
onConnect(icontext) {
|
||||||
|
console.log('mqtt connected!');
|
||||||
|
var my = icontext.invocationContext;
|
||||||
|
|
||||||
|
// subscribe to rider locations
|
||||||
|
if (my.options.track_rider) {
|
||||||
|
console.log('subscribing to ' + my.options.channels.rider_location);
|
||||||
|
my.mqtt.subscribe(my.options.channels.rider_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscribe to jo locations
|
||||||
|
if (my.options.track_jo) {
|
||||||
|
console.log('subscribing to ' + my.options.channels.jo_location);
|
||||||
|
my.mqtt.subscribe(my.options.channels.jo_location);
|
||||||
|
my.mqtt.subscribe(my.options.channels.jo_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage(msg) {
|
||||||
|
// console.log(msg);
|
||||||
|
console.log('received message');
|
||||||
|
|
||||||
|
var channel = msg.destinationName;
|
||||||
|
var chan_split = channel.split('/');
|
||||||
|
var payload = msg.payloadString;
|
||||||
|
|
||||||
|
// handle different channels
|
||||||
|
switch (chan_split[0]) {
|
||||||
|
case "rider":
|
||||||
|
this.handleRider(chan_split, payload);
|
||||||
|
break;
|
||||||
|
case "jo":
|
||||||
|
this.handleJobOrder(chan_split, payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRider(chan_split, payload) {
|
||||||
|
console.log("rider message");
|
||||||
|
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);
|
||||||
|
|
||||||
|
// check for correct format
|
||||||
|
if (pl_split.length != 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var lat = parseFloat(pl_split[0]);
|
||||||
|
var lng = parseFloat(pl_split[1]);
|
||||||
|
|
||||||
|
this.dashmap.putRiderAvailableMarker(chan_split[1], lat, lng);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleJobOrder(chan_split, payload) {
|
||||||
|
console.log("jo message");
|
||||||
|
var id = chan_split[1];
|
||||||
|
switch (chan_split[2]) {
|
||||||
|
case "location":
|
||||||
|
// var my = this;
|
||||||
|
console.log("got location for jo " + id + " - " + payload);
|
||||||
|
var pl_split = payload.split(':');
|
||||||
|
|
||||||
|
// check for correct format
|
||||||
|
if (pl_split.length != 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var lat = parseFloat(pl_split[0]);
|
||||||
|
var lng = parseFloat(pl_split[1]);
|
||||||
|
|
||||||
|
// move marker
|
||||||
|
console.log(lat + ' - ' + lng);
|
||||||
|
|
||||||
|
this.dashmap.putCustomerMarker(id, lat, lng);
|
||||||
|
break;
|
||||||
|
case "status":
|
||||||
|
switch (payload) {
|
||||||
|
case 'cancel':
|
||||||
|
case 'fulfill':
|
||||||
|
case 'delete':
|
||||||
|
this.dashmap.removeCustomerMarker(id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -313,17 +313,14 @@ class CreateCustomerFromWarrantyCommand extends Command
|
||||||
|
|
||||||
protected function loadCustomers()
|
protected function loadCustomers()
|
||||||
{
|
{
|
||||||
error_log('starting query...');
|
|
||||||
// get all customers
|
// get all customers
|
||||||
$customers = $this->em->getRepository(Customer::class)->findAll();
|
$customers = $this->em->getRepository(Customer::class)->findAll();
|
||||||
$cust_q = $this->em->createQuery('select c from App\Entity\Customer c');
|
$cust_q = $this->em->createQuery('select c from App\Entity\Customer c');
|
||||||
$cust_iter = $q->iterate();
|
$cust_iter = $q->iterate();
|
||||||
|
|
||||||
error_log('looping through query...');
|
|
||||||
$this->cust_index = [];
|
$this->cust_index = [];
|
||||||
foreach ($cust_iter as $customer)
|
foreach ($cust_iter as $customer)
|
||||||
{
|
{
|
||||||
error_log('here');
|
|
||||||
$mobile = trim($customer->getPhoneMobile());
|
$mobile = trim($customer->getPhoneMobile());
|
||||||
if (!empty($mobile))
|
if (!empty($mobile))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
68
src/Command/RefreshJobOrderCacheCommand.php
Normal file
68
src/Command/RefreshJobOrderCacheCommand.php
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Command;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Doctrine\Common\Persistence\ObjectManager;
|
||||||
|
use App\Service\JobOrderCache;
|
||||||
|
use App\Entity\JobOrder;
|
||||||
|
use App\Ramcar\JOStatus;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
|
||||||
|
class RefreshJobOrderCacheCommand extends Command
|
||||||
|
{
|
||||||
|
protected $em;
|
||||||
|
protected $jo_cache;
|
||||||
|
|
||||||
|
public function __construct(ObjectManager $om, JobOrderCache $jo_cache)
|
||||||
|
{
|
||||||
|
$this->em = $om;
|
||||||
|
$this->jo_cache = $jo_cache;
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName('joborder:refresh_cache')
|
||||||
|
->setDescription('Refresh active job order cache from database.')
|
||||||
|
->setHelp('Refresh active job order cache from database.');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$date = new DateTime();
|
||||||
|
$date->modify('-3 day');
|
||||||
|
|
||||||
|
$status_list = [
|
||||||
|
JOStatus::PENDING,
|
||||||
|
JOStatus::RIDER_ASSIGN,
|
||||||
|
JOStatus::ASSIGNED,
|
||||||
|
JOStatus::IN_TRANSIT,
|
||||||
|
JOStatus::IN_PROGRESS,
|
||||||
|
];
|
||||||
|
|
||||||
|
$qb = $this->em->getRepository(JobOrder::class)
|
||||||
|
->createQueryBuilder('jo');
|
||||||
|
$res = $qb->select('jo')
|
||||||
|
->where('jo.status IN (:statuses)')
|
||||||
|
->andWhere('jo.date_schedule >= :date')
|
||||||
|
->setParameter('statuses', $status_list, Connection::PARAM_STR_ARRAY)
|
||||||
|
->setParameter('date', $date)
|
||||||
|
->getQuery()
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// fulfill each
|
||||||
|
foreach ($res as $jo)
|
||||||
|
{
|
||||||
|
$this->jo_cache->addActiveJobOrder($jo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,8 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
use App\Service\RiderTracker;
|
use App\Service\RiderTracker;
|
||||||
use App\Service\GISManagerInterface;
|
use App\Service\GISManagerInterface;
|
||||||
|
use App\Service\JobOrderCache;
|
||||||
|
use App\Service\RiderCache;
|
||||||
|
|
||||||
use App\Entity\Rider;
|
use App\Entity\Rider;
|
||||||
|
|
||||||
|
|
@ -18,8 +20,11 @@ class HomeController extends Controller
|
||||||
/**
|
/**
|
||||||
* @Menu(selected="home")
|
* @Menu(selected="home")
|
||||||
*/
|
*/
|
||||||
public function index(EntityManagerInterface $em, RiderTracker $rider_tracker,
|
public function index(
|
||||||
GISManagerInterface $gis_manager)
|
EntityManagerInterface $em,
|
||||||
|
RiderTracker $rider_tracker,
|
||||||
|
GISManagerInterface $gis_manager
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// get map
|
// get map
|
||||||
$params['map_js_file'] = $gis_manager->getJSInitFile();
|
$params['map_js_file'] = $gis_manager->getJSInitFile();
|
||||||
|
|
@ -27,11 +32,40 @@ class HomeController extends Controller
|
||||||
return $this->render('home.html.twig', $params);
|
return $this->render('home.html.twig', $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRiderLocations(EntityManagerInterface $em, RiderTracker $rider_tracker)
|
public function getMapLocations(JobOrderCache $jo_cache)
|
||||||
{
|
{
|
||||||
// TODO: get active riders from cache
|
$active_jos = $jo_cache->getAllActiveJobOrders();
|
||||||
// TODO: get active JOs from cache
|
|
||||||
|
// get active JOs from cache
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRiderLocations(JobOrderCache $jo_cache, RiderCache $rider_cache, EntityManagerInterface $em, RiderTracker $rider_tracker)
|
||||||
|
{
|
||||||
|
// get active JOs from cache
|
||||||
|
$active_jos = $jo_cache->getAllActiveJobOrders();
|
||||||
|
$riders = $rider_cache->getAllActiveRiders();
|
||||||
|
|
||||||
|
// TODO: optimize this
|
||||||
|
// get all riders and figure out if they have active jos
|
||||||
|
foreach ($riders as $rider_id => $rider_data)
|
||||||
|
{
|
||||||
|
$rider = $em->getRepository(Rider::class)->find($rider_id);
|
||||||
|
if ($rider == null)
|
||||||
|
{
|
||||||
|
unset($riders[$rider_id]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$jo = $rider->getActiveJobOrder();
|
||||||
|
if ($jo == null)
|
||||||
|
$riders[$rider_id]['has_jo'] = false;
|
||||||
|
else
|
||||||
|
$riders[$rider_id]['has_jo'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get active riders from cache
|
||||||
// get all riders
|
// get all riders
|
||||||
|
/*
|
||||||
$riders = $em->getRepository(Rider::class)->findAll();
|
$riders = $em->getRepository(Rider::class)->findAll();
|
||||||
|
|
||||||
$locations = [];
|
$locations = [];
|
||||||
|
|
@ -81,9 +115,11 @@ class HomeController extends Controller
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return $this->json([
|
return $this->json([
|
||||||
'riders' => $locations,
|
'jos' => $active_jos,
|
||||||
|
'riders' => $riders,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ class HubController extends Controller
|
||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted('hub.add', null, 'No access.');
|
$this->denyAccessUnlessGranted('hub.add', null, 'No access.');
|
||||||
|
|
||||||
error_log($req->request->get('time_open'));
|
//error_log($req->request->get('time_open'));
|
||||||
|
|
||||||
// create new object
|
// create new object
|
||||||
$em = $this->getDoctrine()->getManager();
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
@ -314,6 +314,7 @@ class HubController extends Controller
|
||||||
'name' => $hub->getName(),
|
'name' => $hub->getName(),
|
||||||
'branch' => $hub->getBranch(),
|
'branch' => $hub->getBranch(),
|
||||||
'cnum' => $hub->getContactNumbers(),
|
'cnum' => $hub->getContactNumbers(),
|
||||||
|
'distance' => $hub_res['distance'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Controller;
|
||||||
|
|
||||||
use App\Ramcar\JOStatus;
|
use App\Ramcar\JOStatus;
|
||||||
use App\Ramcar\InvoiceCriteria;
|
use App\Ramcar\InvoiceCriteria;
|
||||||
|
use App\Ramcar\CMBServiceType;
|
||||||
|
|
||||||
use App\Entity\CustomerVehicle;
|
use App\Entity\CustomerVehicle;
|
||||||
use App\Entity\Promo;
|
use App\Entity\Promo;
|
||||||
|
|
@ -23,6 +24,9 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
|
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use App\Service\RiderTracker;
|
||||||
|
|
||||||
use Catalyst\MenuBundle\Annotation\Menu;
|
use Catalyst\MenuBundle\Annotation\Menu;
|
||||||
|
|
||||||
class JobOrderController extends Controller
|
class JobOrderController extends Controller
|
||||||
|
|
@ -922,4 +926,29 @@ class JobOrderController extends Controller
|
||||||
|
|
||||||
return $this->render('job-order/popup.html.twig', [ 'jo' => $jo ]);
|
return $this->render('job-order/popup.html.twig', [ 'jo' => $jo ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ParamConverter("jo", class="App\Entity\JobOrder")
|
||||||
|
*/
|
||||||
|
public function tracker(
|
||||||
|
EntityManagerInterface $em,
|
||||||
|
RiderTracker $rider_tracker,
|
||||||
|
GISManagerInterface $gis_manager,
|
||||||
|
JobOrder $jo
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ($jo === null)
|
||||||
|
return new Response('No job order data');
|
||||||
|
|
||||||
|
$rider = $jo->getRider();
|
||||||
|
|
||||||
|
// get map
|
||||||
|
$params['jo'] = $jo;
|
||||||
|
$params['rider'] = $rider;
|
||||||
|
$params['rider_pos'] = $rider_tracker->getRiderLocation($rider->getID());
|
||||||
|
$params['service_type'] = CMBServiceType::getName($jo->getServiceType());
|
||||||
|
$params['map_js_file'] = $gis_manager->getJSInitFile();
|
||||||
|
|
||||||
|
return $this->render('job-order/tracker.html.twig', $params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ use App\Service\InvoiceGeneratorInterface;
|
||||||
use App\Service\MQTTClient;
|
use App\Service\MQTTClient;
|
||||||
use App\Service\WarrantyHandler;
|
use App\Service\WarrantyHandler;
|
||||||
use App\Service\RedisClientProvider;
|
use App\Service\RedisClientProvider;
|
||||||
|
use App\Service\RiderCache;
|
||||||
|
|
||||||
use App\Entity\RiderSession;
|
use App\Entity\RiderSession;
|
||||||
use App\Entity\Customer;
|
use App\Entity\Customer;
|
||||||
|
|
@ -213,7 +214,7 @@ class RAPIController extends Controller
|
||||||
return $res->getReturnResponse();
|
return $res->getReturnResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(Request $req, EncoderFactoryInterface $ef, RedisClientProvider $redis)
|
public function login(Request $req, EncoderFactoryInterface $ef, RedisClientProvider $redis, RiderCache $rcache)
|
||||||
{
|
{
|
||||||
$required_params = [
|
$required_params = [
|
||||||
'user',
|
'user',
|
||||||
|
|
@ -255,6 +256,11 @@ class RAPIController extends Controller
|
||||||
|
|
||||||
$rider->setAvailable(true);
|
$rider->setAvailable(true);
|
||||||
|
|
||||||
|
$rider_id = $rider->getID();
|
||||||
|
// cache rider location (default to hub)
|
||||||
|
// TODO: figure out longitude / latitude default
|
||||||
|
$rcache->addActiveRider($rider_id, 0, 0);
|
||||||
|
|
||||||
// TODO: log rider logging in
|
// TODO: log rider logging in
|
||||||
|
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
@ -293,7 +299,7 @@ class RAPIController extends Controller
|
||||||
return $res->getReturnResponse();
|
return $res->getReturnResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function logout(Request $req)
|
public function logout(Request $req, RiderCache $rcache)
|
||||||
{
|
{
|
||||||
$required_params = [];
|
$required_params = [];
|
||||||
$em = $this->getDoctrine()->getManager();
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
@ -305,6 +311,9 @@ class RAPIController extends Controller
|
||||||
$rider = $this->session->getRider();
|
$rider = $this->session->getRider();
|
||||||
$rider->setAvailable(false);
|
$rider->setAvailable(false);
|
||||||
|
|
||||||
|
// remove from cache
|
||||||
|
$rcache->removeActiveRider($rider->getID());
|
||||||
|
|
||||||
// remove rider from session
|
// remove rider from session
|
||||||
$this->session->setRider(null);
|
$this->session->setRider(null);
|
||||||
|
|
||||||
|
|
@ -936,7 +945,6 @@ class RAPIController extends Controller
|
||||||
// check if new battery
|
// check if new battery
|
||||||
if (($jo->getServiceType() != ServiceType::BATTERY_REPLACEMENT_NEW) ||
|
if (($jo->getServiceType() != ServiceType::BATTERY_REPLACEMENT_NEW) ||
|
||||||
($jo->getServiceType() != CMBServiceType::BATTERY_REPLACEMENT_NEW))
|
($jo->getServiceType() != CMBServiceType::BATTERY_REPLACEMENT_NEW))
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// customer vehicle
|
// customer vehicle
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,6 @@ class StaticContentController extends Controller
|
||||||
$result = $em->getRepository(StaticContent::class)->find($id);
|
$result = $em->getRepository(StaticContent::class)->find($id);
|
||||||
if ($result != null)
|
if ($result != null)
|
||||||
{
|
{
|
||||||
error_log($id);
|
|
||||||
$error_array['id'] = 'Duplicate ID exists.';
|
$error_array['id'] = 'Duplicate ID exists.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,7 +225,6 @@ class StaticContentController extends Controller
|
||||||
$result = $em->getRepository(StaticContent::class)->find($id);
|
$result = $em->getRepository(StaticContent::class)->find($id);
|
||||||
if ($result != null)
|
if ($result != null)
|
||||||
{
|
{
|
||||||
error_log($id);
|
|
||||||
$error_array['id'] = 'Duplicate ID exists.';
|
$error_array['id'] = 'Duplicate ID exists.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,9 @@ class VehicleController extends Controller
|
||||||
if (empty($row))
|
if (empty($row))
|
||||||
throw $this->createNotFoundException('The item does not exist');
|
throw $this->createNotFoundException('The item does not exist');
|
||||||
|
|
||||||
|
// get current batteries of vehicle to be updated
|
||||||
|
$current_batteries = $row->getBatteries();
|
||||||
|
|
||||||
// set and save values
|
// set and save values
|
||||||
$row->setMake($req->request->get('make'))
|
$row->setMake($req->request->get('make'))
|
||||||
->setModelYearFrom($req->request->get('model_year_from'))
|
->setModelYearFrom($req->request->get('model_year_from'))
|
||||||
|
|
@ -251,7 +254,6 @@ class VehicleController extends Controller
|
||||||
else
|
else
|
||||||
$row->setDisplayMobile(false);
|
$row->setDisplayMobile(false);
|
||||||
|
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
$errors = $validator->validate($row);
|
$errors = $validator->validate($row);
|
||||||
|
|
||||||
|
|
@ -272,6 +274,50 @@ class VehicleController extends Controller
|
||||||
else
|
else
|
||||||
$row->setManufacturer($manufacturer);
|
$row->setManufacturer($manufacturer);
|
||||||
|
|
||||||
|
// custom validation for batteries
|
||||||
|
$batteries = $req->request->get('batteries');
|
||||||
|
if (!empty($batteries))
|
||||||
|
{
|
||||||
|
// need to check if a battery has been removed
|
||||||
|
if (count($current_batteries) > count($batteries))
|
||||||
|
{
|
||||||
|
// battery/batteries have been removed
|
||||||
|
foreach ($current_batteries as $cbatt)
|
||||||
|
{
|
||||||
|
$cbatt_id = $cbatt->getID();
|
||||||
|
if (in_array($cbatt_id, $batteries))
|
||||||
|
{
|
||||||
|
// do nothing, move to next element
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// cbatt_id has been deleted
|
||||||
|
$battery = $em->getRepository(Battery::class)->find($cbatt_id);
|
||||||
|
|
||||||
|
if (!empty($battery))
|
||||||
|
{
|
||||||
|
$battery->removeVehicle($row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no more battery compatible with vehicle
|
||||||
|
foreach ($current_batteries as $c_battery)
|
||||||
|
{
|
||||||
|
$cbatt_id = $c_battery->getID();
|
||||||
|
$battery = $em->getRepository(Battery::class)->find($cbatt_id);
|
||||||
|
|
||||||
|
if (!empty($battery))
|
||||||
|
{
|
||||||
|
$battery->removeVehicle($row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if any errors were found
|
// check if any errors were found
|
||||||
if (!empty($error_array)) {
|
if (!empty($error_array)) {
|
||||||
// return validation failure response
|
// return validation failure response
|
||||||
|
|
|
||||||
|
|
@ -567,7 +567,7 @@ class WarrantyController extends Controller
|
||||||
{
|
{
|
||||||
// call service to check if warranty details is incomplete and then update warranty
|
// call service to check if warranty details is incomplete and then update warranty
|
||||||
// using details from csv file
|
// using details from csv file
|
||||||
error_log('Updating warranty for ' . $warr->getID());
|
// error_log('Updating warranty for ' . $warr->getID());
|
||||||
$wh->updateWarranty($warr, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase);
|
$wh->updateWarranty($warr, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -582,7 +582,7 @@ class WarrantyController extends Controller
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_log('Creating warranty for serial ' . $serial . ' and plate number ' . $plate_number);
|
// error_log('Creating warranty for serial ' . $serial . ' and plate number ' . $plate_number);
|
||||||
|
|
||||||
$wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
|
$wh->createWarranty($serial, $plate_number, $first_name, $last_name, $mobile_number, $batt_list, $date_purchase, $warranty_class);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
105
src/EventListener/JobOrderActiveCacheListener.php
Normal file
105
src/EventListener/JobOrderActiveCacheListener.php
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\EventListener;
|
||||||
|
|
||||||
|
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
||||||
|
|
||||||
|
use App\Service\JobOrderCache;
|
||||||
|
use App\Ramcar\JOStatus;
|
||||||
|
use App\Entity\JobOrder;
|
||||||
|
|
||||||
|
class JobOrderActiveCacheListener
|
||||||
|
{
|
||||||
|
protected $key;
|
||||||
|
protected $mqtt;
|
||||||
|
|
||||||
|
public function __construct(JobOrderCache $jo_cache, $mqtt)
|
||||||
|
{
|
||||||
|
$this->jo_cache = $jo_cache;
|
||||||
|
$this->mqtt = $mqtt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when a new job order comes in
|
||||||
|
public function postPersist(JobOrder $jo, LifecycleEventArgs $args)
|
||||||
|
{
|
||||||
|
$status = $jo->getStatus();
|
||||||
|
|
||||||
|
switch ($status)
|
||||||
|
{
|
||||||
|
// active
|
||||||
|
case JOStatus::PENDING:
|
||||||
|
case JOStatus::RIDER_ASSIGN:
|
||||||
|
case JOStatus::ASSIGNED:
|
||||||
|
case JOStatus::IN_TRANSIT:
|
||||||
|
case JOStatus::IN_PROGRESS:
|
||||||
|
$this->processActiveJO($jo);
|
||||||
|
break;
|
||||||
|
// inactive
|
||||||
|
case JOStatus::CANCELLED:
|
||||||
|
$this->processInactiveJO($jo, 'cancel');
|
||||||
|
break;
|
||||||
|
case JOStatus::FULFILLED:
|
||||||
|
$this->processInactiveJO($jo, 'fulfill');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when a job order is updated
|
||||||
|
public function postUpdate(JobOrder $jo, LifecycleEventArgs $args)
|
||||||
|
{
|
||||||
|
$status = $jo->getStatus();
|
||||||
|
|
||||||
|
switch ($status)
|
||||||
|
{
|
||||||
|
// active
|
||||||
|
case JOStatus::PENDING:
|
||||||
|
case JOStatus::RIDER_ASSIGN:
|
||||||
|
case JOStatus::ASSIGNED:
|
||||||
|
case JOStatus::IN_TRANSIT:
|
||||||
|
case JOStatus::IN_PROGRESS:
|
||||||
|
$this->processActiveJO($jo);
|
||||||
|
break;
|
||||||
|
// inactive
|
||||||
|
case JOStatus::CANCELLED:
|
||||||
|
$this->processInactiveJO($jo, 'cancel');
|
||||||
|
break;
|
||||||
|
case JOStatus::FULFILLED:
|
||||||
|
$this->processInactiveJO($jo, 'fulfill');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// when a job order is deleted
|
||||||
|
public function postRemove(JobOrder $jo, LifecycleEventArgs $args)
|
||||||
|
{
|
||||||
|
$this->processInactiveJO($jo, 'delete');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processActiveJO($jo)
|
||||||
|
{
|
||||||
|
// save in cache
|
||||||
|
$this->jo_cache->addActiveJobOrder($jo);
|
||||||
|
|
||||||
|
// publish to mqtt
|
||||||
|
$coords = $jo->getCoordinates();
|
||||||
|
|
||||||
|
// TODO: do we put the key in config?
|
||||||
|
$this->mqtt->publish(
|
||||||
|
'jo/' . $jo->getID() . '/location',
|
||||||
|
$coords->getLatitude() . ':' . $coords->getLongitude()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processInactiveJO($jo, $status = 'cancel')
|
||||||
|
{
|
||||||
|
// remove from redis cache
|
||||||
|
$this->jo_cache->removeActiveJobOrder($jo);
|
||||||
|
|
||||||
|
// TODO: publich to mqtt
|
||||||
|
$this->mqtt->publish(
|
||||||
|
'jo/' . $jo->getID() . '/status',
|
||||||
|
$status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -594,7 +594,7 @@ class ResqInvoiceGenerator implements InvoiceGeneratorInterface
|
||||||
$diesel_price = self::REFUEL_FEE_DIESEL;
|
$diesel_price = self::REFUEL_FEE_DIESEL;
|
||||||
|
|
||||||
$fuel = new InvoiceItem();
|
$fuel = new InvoiceItem();
|
||||||
error_log('fuel type - ' . $ftype);
|
//error_log('fuel type - ' . $ftype);
|
||||||
switch ($ftype)
|
switch ($ftype)
|
||||||
{
|
{
|
||||||
case FuelType::GAS:
|
case FuelType::GAS:
|
||||||
|
|
@ -631,4 +631,77 @@ class ResqInvoiceGenerator implements InvoiceGeneratorInterface
|
||||||
$total['vat'] = $vat;
|
$total['vat'] = $vat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function processCriteria(InvoiceCriteria $criteria)
|
||||||
|
{
|
||||||
|
// initialize
|
||||||
|
$invoice = new Invoice();
|
||||||
|
$total = [
|
||||||
|
'sell_price' => 0.0,
|
||||||
|
'vat' => 0.0,
|
||||||
|
'vat_ex_price' => 0.0,
|
||||||
|
'ti_rate' => 0.0,
|
||||||
|
'total_price' => 0.0,
|
||||||
|
'discount' => 0.0,
|
||||||
|
];
|
||||||
|
|
||||||
|
$stype = $criteria->getServiceType();
|
||||||
|
$cv = $criteria->getCustomerVehicle();
|
||||||
|
$has_coolant = $criteria->hasCoolant();
|
||||||
|
// error_log($stype);
|
||||||
|
switch ($stype)
|
||||||
|
{
|
||||||
|
case ServiceType::JUMPSTART_TROUBLESHOOT:
|
||||||
|
$this->processJumpstart($total, $invoice);
|
||||||
|
break;
|
||||||
|
case ServiceType::JUMPSTART_WARRANTY:
|
||||||
|
$this->processJumpstartWarranty($total, $invoice);
|
||||||
|
|
||||||
|
case ServiceType::BATTERY_REPLACEMENT_NEW:
|
||||||
|
$this->processEntries($total, $criteria, $invoice);
|
||||||
|
/*
|
||||||
|
$this->processBatteries($total, $criteria, $invoice);
|
||||||
|
$this->processTradeIns($total, $criteria, $invoice);
|
||||||
|
*/
|
||||||
|
$this->processDiscount($total, $criteria, $invoice);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ServiceType::BATTERY_REPLACEMENT_WARRANTY:
|
||||||
|
$this->processWarranty($total, $criteria, $invoice);
|
||||||
|
break;
|
||||||
|
case ServiceType::POST_RECHARGED:
|
||||||
|
$this->processRecharge($total, $invoice);
|
||||||
|
break;
|
||||||
|
case ServiceType::POST_REPLACEMENT:
|
||||||
|
$this->processReplacement($total, $invoice);
|
||||||
|
break;
|
||||||
|
case ServiceType::TIRE_REPAIR:
|
||||||
|
$this->processTireRepair($total, $invoice, $cv);
|
||||||
|
// $this->processOtherServices($total, $invoice, $stype);
|
||||||
|
break;
|
||||||
|
case ServiceType::OVERHEAT_ASSISTANCE:
|
||||||
|
$this->processOverheat($total, $invoice, $cv, $has_coolant);
|
||||||
|
break;
|
||||||
|
case ServiceType::EMERGENCY_REFUEL:
|
||||||
|
//error_log('processing refuel');
|
||||||
|
$ftype = $criteria->getCustomerVehicle()->getFuelType();
|
||||||
|
$this->processRefuel($total, $invoice, $cv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check if any promo is applied
|
||||||
|
// apply discounts
|
||||||
|
$promos = $criteria->getPromos();
|
||||||
|
|
||||||
|
$invoice->setTotalPrice($total['total_price'])
|
||||||
|
->setVATExclusivePrice($total['vat_ex_price'])
|
||||||
|
->setVAT($total['vat'])
|
||||||
|
->setDiscount($total['discount'])
|
||||||
|
->setTradeIn($total['ti_rate']);
|
||||||
|
|
||||||
|
|
||||||
|
// dump
|
||||||
|
//Debug::dump($invoice, 1);
|
||||||
|
|
||||||
|
return $invoice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
66
src/Service/JobOrderCache.php
Normal file
66
src/Service/JobOrderCache.php
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Service\RedisClientProvider;
|
||||||
|
use App\Entity\JobOrder;
|
||||||
|
|
||||||
|
class JobOrderCache
|
||||||
|
{
|
||||||
|
protected $redis;
|
||||||
|
protected $active_jo_key;
|
||||||
|
|
||||||
|
public function __construct(RedisClientProvider $redis_prov, $active_jo_key)
|
||||||
|
{
|
||||||
|
$this->redis = $redis_prov->getRedisClient();
|
||||||
|
$this->active_jo_key = $active_jo_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addActiveJobOrder(JobOrder $jo)
|
||||||
|
{
|
||||||
|
$coords = $jo->getCoordinates();
|
||||||
|
|
||||||
|
$this->redis->geoadd(
|
||||||
|
$this->active_jo_key,
|
||||||
|
$coords->getLongitude(),
|
||||||
|
$coords->getLatitude(),
|
||||||
|
$jo->getID()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllActiveJobOrders()
|
||||||
|
{
|
||||||
|
$all_jo = $this->redis->georadius(
|
||||||
|
$this->active_jo_key,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
41000,
|
||||||
|
'km',
|
||||||
|
['WITHCOORD' => true]
|
||||||
|
);
|
||||||
|
|
||||||
|
$jo_locs = [];
|
||||||
|
foreach ($all_jo as $jo_data)
|
||||||
|
{
|
||||||
|
$id = $jo_data[0];
|
||||||
|
$lng = $jo_data[1][0];
|
||||||
|
$lat = $jo_data[1][1];
|
||||||
|
|
||||||
|
$jo_locs[$id] = [
|
||||||
|
'longitude' => $lng,
|
||||||
|
'latitude' => $lat,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// error_log(print_r($all_jo, true));
|
||||||
|
return $jo_locs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeActiveJobOrder(JobOrder $jo)
|
||||||
|
{
|
||||||
|
$this->redis->zrem(
|
||||||
|
$this->active_jo_key,
|
||||||
|
$jo->getID()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -164,7 +164,7 @@ class CMBJobOrderHandler implements JobOrderHandlerInterface
|
||||||
$row['delivery_address'] = $orow->getDeliveryAddress();
|
$row['delivery_address'] = $orow->getDeliveryAddress();
|
||||||
$row['date_schedule'] = $orow->getDateSchedule()->format("d M Y g:i A");
|
$row['date_schedule'] = $orow->getDateSchedule()->format("d M Y g:i A");
|
||||||
$row['type'] = $orow->isAdvanceOrder() ? 'Advanced Order' : 'Immediate';
|
$row['type'] = $orow->isAdvanceOrder() ? 'Advanced Order' : 'Immediate';
|
||||||
$row['service_type'] = $service_types[$orow->getServiceType()];
|
$row['service_type'] = $service_types[$orow->getServiceType()] ?? 'Unknown';
|
||||||
$row['status'] = $statuses[$orow->getStatus()];
|
$row['status'] = $statuses[$orow->getStatus()];
|
||||||
$row['flag_advance'] = $orow->isAdvanceOrder();
|
$row['flag_advance'] = $orow->isAdvanceOrder();
|
||||||
$row['plate_number'] = $orow->getCustomerVehicle()->getPlateNumber();
|
$row['plate_number'] = $orow->getCustomerVehicle()->getPlateNumber();
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,15 @@ class MQTTClient
|
||||||
{
|
{
|
||||||
const PREFIX = 'motolite.control.';
|
const PREFIX = 'motolite.control.';
|
||||||
const RIDER_PREFIX = 'motorider_';
|
const RIDER_PREFIX = 'motorider_';
|
||||||
const REDIS_KEY = 'events';
|
|
||||||
|
|
||||||
// protected $mclient;
|
// protected $mclient;
|
||||||
protected $redis;
|
protected $redis;
|
||||||
|
protected $key;
|
||||||
|
|
||||||
public function __construct(RedisClientProvider $redis_client)
|
public function __construct(RedisClientProvider $redis_client, $key)
|
||||||
{
|
{
|
||||||
$this->redis = $redis_client->getRedisClient();
|
$this->redis = $redis_client->getRedisClient();
|
||||||
|
$this->key = $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __destruct()
|
public function __destruct()
|
||||||
|
|
@ -29,7 +30,7 @@ class MQTTClient
|
||||||
// $this->mclient->publish($channel, $message);
|
// $this->mclient->publish($channel, $message);
|
||||||
|
|
||||||
$data = $channel . '|' . $message;
|
$data = $channel . '|' . $message;
|
||||||
$this->redis->lpush(self::REDIS_KEY, $data);
|
$this->redis->lpush($this->key, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendEvent(JobOrder $job_order, $payload)
|
public function sendEvent(JobOrder $job_order, $payload)
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,23 @@ class MapTools
|
||||||
{
|
{
|
||||||
//error_log($row[0]->getName() . ' - ' . $row['dist']);
|
//error_log($row[0]->getName() . ' - ' . $row['dist']);
|
||||||
$hubs[] = $row[0];
|
$hubs[] = $row[0];
|
||||||
|
|
||||||
|
// get coordinates of hub
|
||||||
|
$hub_coordinates = $row[0]->getCoordinates();
|
||||||
|
|
||||||
|
$cust_lat = $point->getLatitude();
|
||||||
|
$cust_lng = $point->getLongitude();
|
||||||
|
|
||||||
|
$hub_lat = $hub_coordinates->getLatitude();
|
||||||
|
$hub_lng = $hub_coordinates->getLongitude();
|
||||||
|
|
||||||
|
// get distance in kilometers from customer point to hub point
|
||||||
|
$dist = $this->distance($cust_lat, $cust_lng, $hub_lat, $hub_lng);
|
||||||
|
|
||||||
$final_data[] = [
|
$final_data[] = [
|
||||||
'hub' => $row[0],
|
'hub' => $row[0],
|
||||||
'db_distance' => $row['dist'],
|
'db_distance' => $row['dist'],
|
||||||
'distance' => 0,
|
'distance' => $dist,
|
||||||
'duration' => 0,
|
'duration' => 0,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -135,4 +148,18 @@ class MapTools
|
||||||
return $final_data;
|
return $final_data;
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function distance($lat1, $lon1, $lat2, $lon2)
|
||||||
|
{
|
||||||
|
if (($lat1 == $lat2) && ($lon1 == $lon2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
$theta = $lon1 - $lon2;
|
||||||
|
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
|
||||||
|
$dist = acos($dist);
|
||||||
|
$dist = rad2deg($dist);
|
||||||
|
$miles = $dist * 60 * 1.1515;
|
||||||
|
|
||||||
|
return round(($miles * 1.609344), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,34 +11,46 @@ class RedisClientProvider
|
||||||
protected $host;
|
protected $host;
|
||||||
protected $port;
|
protected $port;
|
||||||
protected $password;
|
protected $password;
|
||||||
protected $env_flag;
|
|
||||||
|
|
||||||
public function __construct($scheme, $host, $port, $password, $env_flag)
|
public function __construct($scheme, $host, $port, $password)
|
||||||
{
|
{
|
||||||
$this->scheme = $scheme;
|
$this->scheme = $scheme;
|
||||||
$this->host = $host;
|
$this->host = $host;
|
||||||
$this->port = $port;
|
$this->port = $port;
|
||||||
$this->password = $password;
|
$this->password = $password;
|
||||||
$this->env_flag = $env_flag;
|
$this->redis = null;
|
||||||
|
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function connect()
|
||||||
|
{
|
||||||
|
// already connected
|
||||||
|
if ($this->redis != null)
|
||||||
|
return $this->redis;
|
||||||
|
|
||||||
|
// if password is specified attempt connection
|
||||||
|
if (strlen($this->password) > 0)
|
||||||
|
{
|
||||||
|
$this->redis = new PredisClient([
|
||||||
|
"scheme" => $this->scheme,
|
||||||
|
"host" => $this->host,
|
||||||
|
"port" => $this->port,
|
||||||
|
"password" => $this->password]);
|
||||||
|
|
||||||
|
return $this->redis;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redis = new PredisClient([
|
||||||
|
"scheme" => $this->scheme,
|
||||||
|
"host" => $this->host,
|
||||||
|
"port" => $this->port]);
|
||||||
|
|
||||||
|
return $this->redis;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRedisClient()
|
public function getRedisClient()
|
||||||
{
|
{
|
||||||
if ($this->env_flag == 'dev')
|
|
||||||
{
|
|
||||||
$this->redis = new PredisClient([
|
|
||||||
"scheme"=>$this->scheme,
|
|
||||||
"host"=>$this->host,
|
|
||||||
"port"=>$this->port]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->redis = new PredisClient([
|
|
||||||
"scheme"=>$this->scheme,
|
|
||||||
"host"=>$this->host,
|
|
||||||
"port"=>$this->port,
|
|
||||||
"password"=>$this->password]);
|
|
||||||
}
|
|
||||||
return $this->redis;
|
return $this->redis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
76
src/Service/RiderCache.php
Normal file
76
src/Service/RiderCache.php
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Service\RedisClientProvider;
|
||||||
|
use App\Entity\Rider;
|
||||||
|
|
||||||
|
class RiderCache
|
||||||
|
{
|
||||||
|
protected $redis;
|
||||||
|
protected $loc_key;
|
||||||
|
protected $status_key;
|
||||||
|
|
||||||
|
public function __construct(RedisClientProvider $redis_prov, $loc_key, $status_key)
|
||||||
|
{
|
||||||
|
$this->redis = $redis_prov->getRedisClient();
|
||||||
|
$this->loc_key = $loc_key;
|
||||||
|
$this->status_key = $status_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addActiveRider($id, $lat, $lng)
|
||||||
|
{
|
||||||
|
$this->redis->geoadd(
|
||||||
|
$this->loc_key,
|
||||||
|
$lng,
|
||||||
|
$lat,
|
||||||
|
$id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllActiveRiders()
|
||||||
|
{
|
||||||
|
$all_riders = $this->redis->georadius(
|
||||||
|
$this->loc_key,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
41000,
|
||||||
|
'km',
|
||||||
|
['WITHCOORD' => true]
|
||||||
|
);
|
||||||
|
|
||||||
|
$locs = [];
|
||||||
|
foreach ($all_riders as $data)
|
||||||
|
{
|
||||||
|
$id = $data[0];
|
||||||
|
$lng = $data[1][0];
|
||||||
|
$lat = $data[1][1];
|
||||||
|
|
||||||
|
$locs[$id] = [
|
||||||
|
'longitude' => $lng,
|
||||||
|
'latitude' => $lat,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// error_log(print_r($all_riders, true));
|
||||||
|
return $locs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeActiveRider($id)
|
||||||
|
{
|
||||||
|
$this->redis->zrem(
|
||||||
|
$this->loc_key,
|
||||||
|
$id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function incJobOrderCount($id, $status)
|
||||||
|
{
|
||||||
|
$this->redis->hincrby($this->status_key, $id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decJobOrderCount($id, $status)
|
||||||
|
{
|
||||||
|
$this->redis->hincrby($this->status_key, $id, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -93,7 +93,7 @@ class WarrantyHandler
|
||||||
public function updateCustomerVehicle($serial, $batteries, $plate_number, $date_expire)
|
public function updateCustomerVehicle($serial, $batteries, $plate_number, $date_expire)
|
||||||
{
|
{
|
||||||
// find customer vehicle using plate number
|
// find customer vehicle using plate number
|
||||||
error_log('Finding customer vehicle with plate number ' . $plate_number);
|
// error_log('Finding customer vehicle with plate number ' . $plate_number);
|
||||||
$cv_q = $this->em->createQuery('select count(cv) from App\Entity\CustomerVehicle cv where cv.plate_number = :plate_number')
|
$cv_q = $this->em->createQuery('select count(cv) from App\Entity\CustomerVehicle cv where cv.plate_number = :plate_number')
|
||||||
->setParameter('plate_number', $plate_number);
|
->setParameter('plate_number', $plate_number);
|
||||||
$cv_result = $cv_q->getSingleScalarResult();
|
$cv_result = $cv_q->getSingleScalarResult();
|
||||||
|
|
@ -288,7 +288,7 @@ class WarrantyHandler
|
||||||
|
|
||||||
if (empty($warranty_class))
|
if (empty($warranty_class))
|
||||||
{
|
{
|
||||||
error_log('Warranty class is empty for warranty id ' . $warr->getID());
|
//error_log('Warranty class is empty for warranty id ' . $warr->getID());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,12 +301,12 @@ class WarrantyHandler
|
||||||
{
|
{
|
||||||
if ($batt_model == null)
|
if ($batt_model == null)
|
||||||
{
|
{
|
||||||
error_log('Battery model is null for warranty id ' . $warr->getID());
|
//error_log('Battery model is null for warranty id ' . $warr->getID());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ($batt_size == null)
|
if ($batt_size == null)
|
||||||
{
|
{
|
||||||
error_log('Battery size is null for warranty id ' . $warr->getID());
|
//error_log('Battery size is null for warranty id ' . $warr->getID());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,9 @@
|
||||||
"setasign/fpdf": {
|
"setasign/fpdf": {
|
||||||
"version": "1.8.1"
|
"version": "1.8.1"
|
||||||
},
|
},
|
||||||
|
"symfony/asset": {
|
||||||
|
"version": "v4.4.3"
|
||||||
|
},
|
||||||
"symfony/cache": {
|
"symfony/cache": {
|
||||||
"version": "v4.0.2"
|
"version": "v4.0.2"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
70
templates/base_minimal.html.twig
Normal file
70
templates/base_minimal.html.twig
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
{% import 'menu.html.twig' as menu %}
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<!-- begin::Head -->
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{% block title %}{% trans %}block_title{% endtrans %}{% endblock %}</title>
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">
|
||||||
|
|
||||||
|
<!--begin::Web font -->
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.16/webfont.js"></script>
|
||||||
|
<script>
|
||||||
|
WebFont.load({
|
||||||
|
google: {"families":["Poppins:300,400,500,600,700","Roboto:300,400,500,600,700"]},
|
||||||
|
active: function() {
|
||||||
|
sessionStorage.fonts = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<!--end::Web font -->
|
||||||
|
|
||||||
|
<!--begin::Base Styles -->
|
||||||
|
<!--begin::Page Vendors -->
|
||||||
|
<link href="/assets/vendors/custom/fullcalendar/fullcalendar.bundle.css" rel="stylesheet" type="text/css" />
|
||||||
|
<!--end::Page Vendors -->
|
||||||
|
<link href="/assets/vendors/base/vendors.bundle.css" rel="stylesheet" type="text/css" />
|
||||||
|
<link href="/assets/demo/default/base/style.bundle.css" rel="stylesheet" type="text/css" />
|
||||||
|
<link href="/assets/css/style.css" rel="stylesheet" type="text/css" />
|
||||||
|
<!--end::Base Styles -->
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/images/favicon/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="{% trans %}icon_base_32x32{% endtrans %}">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="{% trans %}icon_base_16x16{% endtrans %}">
|
||||||
|
<link rel="manifest" href="/assets/images/favicon/manifest.json">
|
||||||
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
|
<!--begin::Extra Styles -->
|
||||||
|
{% block stylesheets %}
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
|
||||||
|
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
|
||||||
|
crossorigin=""/>
|
||||||
|
{% endblock %}
|
||||||
|
<!--end::Extra Styles -->
|
||||||
|
</head>
|
||||||
|
<!-- end::Head -->
|
||||||
|
<!-- end::Body -->
|
||||||
|
<body class="m-page--fluid m--skin- m-content--skin-light2 m-aside-left--enabled m-aside-left--skin-dark m-aside-left--offcanvas m-aside--offcanvas-default">
|
||||||
|
<!-- begin:: Page -->
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
<!-- end:: Page -->
|
||||||
|
|
||||||
|
<!--begin::Base Scripts -->
|
||||||
|
<script src="/assets/vendors/base/vendors.bundle.js" type="text/javascript"></script>
|
||||||
|
<script src="/assets/demo/default/base/scripts.bundle.js" type="text/javascript"></script>
|
||||||
|
<!--end::Base Scripts -->
|
||||||
|
<!--begin::Page Vendors -->
|
||||||
|
<script src="/assets/vendors/custom/fullcalendar/fullcalendar.bundle.js" type="text/javascript"></script>
|
||||||
|
<!--end::Page Vendors -->
|
||||||
|
<!--begin::Page Snippets -->
|
||||||
|
<script src="/assets/app/js/dashboard.js" type="text/javascript"></script>
|
||||||
|
<script src="/assets/js/common.js" type="text/javascript"></script>
|
||||||
|
<!--end::Page Snippets -->
|
||||||
|
|
||||||
|
<!--begin::Extra Scripts -->
|
||||||
|
{% block scripts %}{% endblock %}
|
||||||
|
<!--end::Extra Scripts -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -13,111 +13,78 @@
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
|
||||||
|
<script src="{{ asset('assets/js/dashboard_map.js') }}"></script>
|
||||||
|
<script src="{{ asset('assets/js/map_mqtt.js') }}"></script>
|
||||||
|
{{ include('map/' ~ map_js_file) }}
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
var map;
|
function initMap(r_markers, c_markers, icons) {
|
||||||
|
|
||||||
function initMap() {
|
|
||||||
var default_lat = {% trans %}default_lat{% endtrans %};
|
var default_lat = {% trans %}default_lat{% endtrans %};
|
||||||
var default_lng = {% trans %}default_long{% endtrans %};
|
var default_lng = {% trans %}default_long{% endtrans %};
|
||||||
|
|
||||||
var rider_popup_url = '/riders/[id]/popup';
|
|
||||||
var cust_popup_url = '/job-order/[id]/popup';
|
|
||||||
map = mapCreate('dashboard_map', default_lat, default_lng, 'road', 13, rider_popup_url, cust_popup_url);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{ include('map/' ~ map_js_file) }}
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// TODO: put this in .env
|
|
||||||
var mqtt;
|
|
||||||
var timeout = 2000;
|
|
||||||
var host = '{{ mqtt_host }}';
|
|
||||||
var port = {{ mqtt_port }};
|
|
||||||
|
|
||||||
var icon_rider_available = L.divIcon({
|
|
||||||
className: 'map-div-icon',
|
|
||||||
html: "<div style='background-color:#00FF00;' class='marker-pin'></div><i class='fa fa-bolt awesome'>",
|
|
||||||
iconSize: [39, 42],
|
|
||||||
iconAnchor: [15, 42]
|
|
||||||
});
|
|
||||||
|
|
||||||
function onConnect() {
|
|
||||||
console.log('connected!');
|
|
||||||
|
|
||||||
mqtt.subscribe('rider/+/location');
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMessage(msg) {
|
|
||||||
// console.log(msg);
|
|
||||||
console.log('received message');
|
|
||||||
|
|
||||||
var channel = msg.destinationName;
|
|
||||||
var chan_split = channel.split('/');
|
|
||||||
var payload = msg.payloadString;
|
|
||||||
|
|
||||||
// handle different channels
|
|
||||||
switch (chan_split[0]) {
|
|
||||||
case "rider":
|
|
||||||
handleRider(chan_split, payload);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function handleRider(chan_split, payload) {
|
|
||||||
console.log("rider message");
|
|
||||||
switch (chan_split[2]) {
|
|
||||||
case "location":
|
|
||||||
console.log("got location for rider " + chan_split[1] + " - " + payload);
|
|
||||||
pl_split = payload.split(':');
|
|
||||||
console.log(pl_split);
|
|
||||||
|
|
||||||
// check for correct format
|
|
||||||
if (pl_split.length != 2)
|
|
||||||
break;
|
|
||||||
|
|
||||||
var lat = parseFloat(pl_split[0]);
|
|
||||||
var lng = parseFloat(pl_split[1]);
|
|
||||||
|
|
||||||
// move marker
|
|
||||||
console.log(rider_markers[chan_split[1]]);
|
|
||||||
|
|
||||||
// check if marker exists
|
|
||||||
if (rider_markers.hasOwnProperty(chan_split[1])) {
|
|
||||||
// marker's there, move it
|
|
||||||
rider_markers[chan_split[1]].setLatLng(L.latLng(lat, lng));
|
|
||||||
} else {
|
|
||||||
// no marker, make one
|
|
||||||
console.log('creating marker');
|
|
||||||
// TODO: make it add to the correct map layer
|
|
||||||
rider_markers[chan_split[1]]= L.marker([lat, lng], { icon: icon_rider_available }).addTo(map);;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mqttConnect() {
|
|
||||||
var d = new Date();
|
|
||||||
var client_id = "dash-{{ app.user.getID }}-" + d.getMonth() + "-" + d.getDate() + "-" + d.getHours() + "-" + d.getMinutes() + "-" + d.getSeconds() + "-" + d.getMilliseconds();
|
|
||||||
console.log(client_id);
|
|
||||||
|
|
||||||
mqtt = new Paho.MQTT.Client(host, port, client_id);
|
|
||||||
var options = {
|
var options = {
|
||||||
useSSL: true,
|
'display_overlay': true,
|
||||||
timeout: 3,
|
'enable_popup': true,
|
||||||
onSuccess: onConnect,
|
'access_token': 'pk.eyJ1Ijoia2NvcmRlcm8iLCJhIjoiY2szbzA3ZHdsMDZxdTNsbGl4ZGNnN2VxaSJ9.LRzAe3RlV8sIP1N1x0chdw',
|
||||||
|
'div_id': 'dashboard_map',
|
||||||
|
'center_lat': default_lat,
|
||||||
|
'center_lng': default_lng,
|
||||||
|
'map_type': 'road',
|
||||||
|
'zoom': 13,
|
||||||
|
'rider_popup_url': '/riders/[id]/popup',
|
||||||
|
'cust_popup_url': '/job-order/[id]/popup',
|
||||||
|
'icons': icons
|
||||||
};
|
};
|
||||||
|
|
||||||
mqtt.onMessageArrived = onMessage;
|
var dashmap = new DashboardMap(options, r_markers, c_markers);
|
||||||
|
dashmap.initialize();
|
||||||
|
dashmap.loadLocations('{{ path('rider_locations') }}');
|
||||||
|
|
||||||
console.log('connecting to mqtt server...');
|
return dashmap;
|
||||||
mqtt.connect(options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mqttConnect();
|
function initEventHandler(dashmap) {
|
||||||
|
var options = {
|
||||||
|
'track_jo': true,
|
||||||
|
'track_rider': true,
|
||||||
|
'channels': {
|
||||||
|
'rider_location': 'rider/+/location',
|
||||||
|
'jo_location': 'jo/+/location',
|
||||||
|
'jo_status': 'jo/+/status'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var event_handler = new MapEventHandler(options, dashmap);
|
||||||
|
event_handler.connect('{{ app.user.getID }}', '{{ mqtt_host }}', {{ mqtt_port }});
|
||||||
|
}
|
||||||
|
|
||||||
|
// create icons
|
||||||
|
var icons = {
|
||||||
|
'rider_active_jo': L.divIcon({
|
||||||
|
className: 'map-div-icon',
|
||||||
|
html: "<div style='background-color:#FF0000;' class='marker-pin'></div><i class='fa fa-bolt awesome'>",
|
||||||
|
iconSize: [39, 42],
|
||||||
|
iconAnchor: [15, 42]
|
||||||
|
}),
|
||||||
|
'rider_available': L.divIcon({
|
||||||
|
className: 'map-div-icon',
|
||||||
|
html: "<div style='background-color:#00FF00;' class='marker-pin'></div><i class='fa fa-bolt awesome'>",
|
||||||
|
iconSize: [39, 42],
|
||||||
|
iconAnchor: [15, 42]
|
||||||
|
}),
|
||||||
|
'customer': L.divIcon({
|
||||||
|
className: 'map-div-icon',
|
||||||
|
html: "<div style='background-color:#0055FF;' class='marker-pin'></div><i class='fa fa-user awesome'>",
|
||||||
|
iconSize: [39, 42],
|
||||||
|
iconAnchor: [15, 42]
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
var r_markers = {};
|
||||||
|
var c_markers = {};
|
||||||
|
|
||||||
|
var dashmap = initMap(r_markers, c_markers, icons);
|
||||||
|
initEventHandler(dashmap, icons);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -381,18 +381,21 @@
|
||||||
<th>Hub</th>
|
<th>Hub</th>
|
||||||
<th>Branch</th>
|
<th>Branch</th>
|
||||||
<th>Contact Numbers</th>
|
<th>Contact Numbers</th>
|
||||||
|
<th>Distance in KM</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="nearest_hubs">
|
<tbody id="nearest_hubs">
|
||||||
{% if mode in ['onestep-edit'] %}
|
<!-- {% if mode in ['onestep-edit'] %}
|
||||||
{% for hub in hubs %}
|
{% for hub in hubs %}
|
||||||
<tr data-id="{{ hub.hub.getID }}"{{ obj.getHub and obj.getHub.getID == hub.hub.getID ? ' class="m-table__row--primary"' }}>
|
<tr data-id="{{ hub.hub.getID }}"{{ obj.getHub and obj.getHub.getID == hub.hub.getID ? ' class="m-table__row--primary"' }}>
|
||||||
<td>{{ hub.hub.getName }}</td>
|
<td>{{ hub.hub.getName }}</td>
|
||||||
<td>{{ hub.hub.getBranch }}</td>
|
<td>{{ hub.hub.getBranch }}</td>
|
||||||
<td>{{ hub.hub.getContactNumbers }}</td>
|
<td>{{ hub.hub.getContactNumbers }}</td>
|
||||||
|
<td></span></td>
|
||||||
|
<td></td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %} -->
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -582,6 +585,9 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<button type="submit" class="btn btn-success">Submit</button>
|
<button type="submit" class="btn btn-success">Submit</button>
|
||||||
|
{% if ftags.set_map_coordinate and is_granted('joborder.cancel') and not obj.isCancelled %}
|
||||||
|
<a href="{{ url('jo_cancel', {'id': obj.getID}) }}" class="btn btn-danger btn-cancel-job-order">Cancel Job Order</a>
|
||||||
|
{% endif %}
|
||||||
<a href="{{ return_url }}" class="btn btn-secondary">Back</a>
|
<a href="{{ return_url }}" class="btn btn-secondary">Back</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -626,7 +632,7 @@ $(function() {
|
||||||
});
|
});
|
||||||
var icon_hub = L.divIcon({
|
var icon_hub = L.divIcon({
|
||||||
className: 'map-div-icon',
|
className: 'map-div-icon',
|
||||||
html: "<div style='background-color:#00FF00;' class='marker-pin'></div><i class='fa fa-home awesome'>",
|
html: "<div style='background-color:#FFFF00;' class='marker-pin'></div><i class='fa fa-home awesome'>",
|
||||||
iconSize: [39, 42],
|
iconSize: [39, 42],
|
||||||
iconAnchor: [15, 42]
|
iconAnchor: [15, 42]
|
||||||
});
|
});
|
||||||
|
|
@ -659,6 +665,7 @@ $(function() {
|
||||||
hub_table += '<td>' + hub['name'] + '</td>';
|
hub_table += '<td>' + hub['name'] + '</td>';
|
||||||
hub_table += '<td>' + hub['branch'] + '</td>';
|
hub_table += '<td>' + hub['branch'] + '</td>';
|
||||||
hub_table += '<td>' + hub['cnum'] + '</td>';
|
hub_table += '<td>' + hub['cnum'] + '</td>';
|
||||||
|
hub_table += '<td>' + hub['distance'] + '</td>';
|
||||||
hub_table += '<td></td>';
|
hub_table += '<td></td>';
|
||||||
hub_table += '</tr>';
|
hub_table += '</tr>';
|
||||||
}
|
}
|
||||||
|
|
@ -670,13 +677,30 @@ $(function() {
|
||||||
|
|
||||||
{% if mode in ['onestep-edit'] %}
|
{% if mode in ['onestep-edit'] %}
|
||||||
// get nearest hubs ajax
|
// get nearest hubs ajax
|
||||||
|
var hub_table = '';
|
||||||
$.getJSON("{{ url('hub_nearest') }}?lat=" + lat + "&long=" + lng, function(data) {
|
$.getJSON("{{ url('hub_nearest') }}?lat=" + lat + "&long=" + lng, function(data) {
|
||||||
var hubs = data['hubs'];
|
var hubs = data['hubs'];
|
||||||
for (i in hubs) {
|
for (i in hubs) {
|
||||||
var hub = hubs[i];
|
var hub = hubs[i];
|
||||||
var hub_marker = L.marker([hub['lat'], hub['long']], { icon: icon_hub });
|
var hub_marker = L.marker([hub['lat'], hub['long']], { icon: icon_hub });
|
||||||
hubLayerGroup.addLayer(hub_marker);
|
hubLayerGroup.addLayer(hub_marker);
|
||||||
|
|
||||||
|
if (selected_hub == hub['id']) {
|
||||||
|
hub_table += '<tr data-id=' + hub['id'] + ' class="m-table__row--primary"' + '>';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hub_table += '<tr data-id=' + hub['id'] + '>';
|
||||||
|
}
|
||||||
|
hub_table += '<td>' + hub['name'] + '</td>';
|
||||||
|
hub_table += '<td>' + hub['branch'] + '</td>';
|
||||||
|
hub_table += '<td>' + hub['cnum'] + '</td>';
|
||||||
|
hub_table += '<td>' + hub['distance'] + '</td>';
|
||||||
|
hub_table += '<td></td>';
|
||||||
|
hub_table += '</tr>';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#nearest_hubs').html(hub_table);
|
||||||
});
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
@ -743,14 +767,6 @@ $(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
{% if mode in ['onestep-edit'] %}
|
|
||||||
selected_hub = '{{ obj.getHub ? obj.getHub.getID: "" }}';
|
|
||||||
$('#hub-field').val(selected_hub);
|
|
||||||
{% endif %}
|
|
||||||
{% if mode in ['onestep'] %}
|
|
||||||
selected_hub = '';
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
$('#hubs-table').on('click', 'tr', function() {
|
$('#hubs-table').on('click', 'tr', function() {
|
||||||
var id = $(this).data('id');
|
var id = $(this).data('id');
|
||||||
|
|
||||||
|
|
@ -806,13 +822,6 @@ $(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
{% if mode in ['onestep-edit'] %}
|
|
||||||
selected_rider = '{{ obj.getRider ? obj.getRider.getID: "" }}';
|
|
||||||
$('#rider-field').val(selected_rider);
|
|
||||||
{% endif %}
|
|
||||||
{% if mode in ['onestep'] %}
|
|
||||||
selected_rider = '';
|
|
||||||
{% endif %}
|
|
||||||
$('#rider-table').on('click', 'tr', function() {
|
$('#rider-table').on('click', 'tr', function() {
|
||||||
var id = $(this).data('id');
|
var id = $(this).data('id');
|
||||||
|
|
||||||
|
|
@ -830,12 +839,30 @@ $(function() {
|
||||||
{% if mode in ['onestep-edit'] %}
|
{% if mode in ['onestep-edit'] %}
|
||||||
var lat = {{ obj.getCoordinates.getLatitude }};
|
var lat = {{ obj.getCoordinates.getLatitude }};
|
||||||
var lng = {{ obj.getCoordinates.getLongitude }};
|
var lng = {{ obj.getCoordinates.getLongitude }};
|
||||||
var hub = {{ obj.getHub.getID }};
|
|
||||||
var rider = {{ obj.getRider.getID }};
|
selected_hub = '{{ obj.getHub ? obj.getHub.getID: "" }}';
|
||||||
|
$('#hub-field').val(selected_hub);
|
||||||
|
|
||||||
|
selected_rider = '{{ obj.getRider ? obj.getRider.getID: "" }}';
|
||||||
|
$('#rider-field').val(selected_rider);
|
||||||
|
|
||||||
selectPoint(lat, lng);
|
selectPoint(lat, lng);
|
||||||
|
|
||||||
// TODO: find a way to highlight the set hub
|
// need to put selected rider on map. selected_hub is already on map because of selectPoint
|
||||||
|
riderLayerGroup.clearLayers();
|
||||||
|
|
||||||
|
$.getJSON("{{ url('hub_riders') }}?id=" + selected_hub, function(data) {
|
||||||
|
var riders = data['riders'];
|
||||||
|
for (i in riders) {
|
||||||
|
var rider = riders[i];
|
||||||
|
if (selected_rider == rider['id']) {
|
||||||
|
var rider_lat = rider['location'][0];
|
||||||
|
var rider_lng = rider['location'][1];
|
||||||
|
var rider_marker = L.marker([rider_lat, rider_lng], { icon: icon_rider_available });
|
||||||
|
riderLayerGroup.addLayer(rider_marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if mode in ['update-processing', 'update-reassign-hub'] %}
|
{% if mode in ['update-processing', 'update-reassign-hub'] %}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@
|
||||||
<div class="m-form__section m-form__section--first">
|
<div class="m-form__section m-form__section--first">
|
||||||
<div class="form-group m-form__group row">
|
<div class="form-group m-form__group row">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<label data-field="customer_vehicle">Select a vehicle:</label>
|
<label data-field="customer_vehicle">Select a vehicle:
|
||||||
|
<span style="color:red"> *</span>
|
||||||
|
</label>
|
||||||
<select class="form-control m-select2" id="customer-vehicle" name="customer_vehicle"></select>
|
<select class="form-control m-select2" id="customer-vehicle" name="customer_vehicle"></select>
|
||||||
<div class="form-control-feedback hide" data-field="customer_vehicle"></div>
|
<div class="form-control-feedback hide" data-field="customer_vehicle"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -326,7 +328,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group m-form__group row">
|
<div class="form-group m-form__group row">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<label data-field="delivery_address">Delivery Address</label>
|
<label data-field="delivery_address">Delivery Address
|
||||||
|
<span style="color:red"> *</span>
|
||||||
|
</label>
|
||||||
<textarea name="delivery_address" class="form-control m-input" rows="4">{{ obj.getDeliveryAddress }}</textarea>
|
<textarea name="delivery_address" class="form-control m-input" rows="4">{{ obj.getDeliveryAddress }}</textarea>
|
||||||
<div class="form-control-feedback hide" data-field="delivery_address"></div>
|
<div class="form-control-feedback hide" data-field="delivery_address"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -338,7 +342,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group m-form__group row">
|
<div class="form-group m-form__group row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<label name="coordinates" data-field="coordinates">Coordinates</label>
|
<label name="coordinates" data-field="coordinates">Coordinates
|
||||||
|
<span style="color:red"> *</span>
|
||||||
|
</label>
|
||||||
<div class="form-control-feedback hide" data-field="coordinates"></div>
|
<div class="form-control-feedback hide" data-field="coordinates"></div>
|
||||||
<input type="hidden" id="map_lat" name="coord_lat" value="">
|
<input type="hidden" id="map_lat" name="coord_lat" value="">
|
||||||
<input type="hidden" id="map_lng" name="coord_lng" value="">
|
<input type="hidden" id="map_lng" name="coord_lng" value="">
|
||||||
|
|
@ -363,7 +369,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group m-form__group row">
|
<div class="form-group m-form__group row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<label name="hub" data-field="hub">Click on a row to select a hub</label>
|
<label name="hub" data-field="hub">Click on a row to select a hub
|
||||||
|
<span style="color:red"> *</span>
|
||||||
|
</label>
|
||||||
<div class="form-control-feedback hide" data-field="hub"></div>
|
<div class="form-control-feedback hide" data-field="hub"></div>
|
||||||
<input type="hidden" id="hub-field" name="hub_id" value="">
|
<input type="hidden" id="hub-field" name="hub_id" value="">
|
||||||
<div class="table-frame" data-name="hub">
|
<div class="table-frame" data-name="hub">
|
||||||
|
|
@ -372,25 +380,22 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th>Hub</th>
|
<th>Hub</th>
|
||||||
<th>Branch</th>
|
<th>Branch</th>
|
||||||
<!--
|
|
||||||
<th class="text-right">Distance</th>
|
|
||||||
<th class="text-right">Travel Time</th>
|
|
||||||
<th class="text-right">Available Riders</th>
|
|
||||||
<th class="text-right">Jobs For Assignment</th>
|
|
||||||
-->
|
|
||||||
<th>Contact Numbers</th>
|
<th>Contact Numbers</th>
|
||||||
|
<th>Distance in KM</th>
|
||||||
<th>Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="nearest_hubs">
|
<tbody id="nearest_hubs">
|
||||||
{% if mode in ['onestep-edit'] %}
|
<!-- {% if mode in ['onestep-edit'] %}
|
||||||
{% for hub in hubs %}
|
{% for hub in hubs %}
|
||||||
<tr data-id="{{ hub.hub.getID }}"{{ obj.getHub and obj.getHub.getID == hub.hub.getID ? ' class="m-table__row--primary"' }}>
|
<tr data-id="{{ hub.hub.getID }}"{{ obj.getHub and obj.getHub.getID == hub.hub.getID ? ' class="m-table__row--primary"' }}>
|
||||||
<td>{{ hub.hub.getName }}</td>
|
<td>{{ hub.hub.getName }}</td>
|
||||||
<td>{{ hub.hub.getBranch }}</td>
|
<td>{{ hub.hub.getBranch }}</td>
|
||||||
<td>{{ hub.hub.getContactNumbers }}</td>
|
<td>{{ hub.hub.getContactNumbers }}</td>
|
||||||
|
<td></span></td>
|
||||||
|
<td></td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %} -->
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -405,7 +410,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group m-form__group row">
|
<div class="form-group m-form__group row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<label name="rider" data-field="rider">Click on a row to select a rider</label>
|
<label name="rider" data-field="rider">Click on a row to select a rider
|
||||||
|
<span style="color:red"> *</span>
|
||||||
|
</label>
|
||||||
<div class="form-control-feedback hide" data-field="rider"></div>
|
<div class="form-control-feedback hide" data-field="rider"></div>
|
||||||
<input type="hidden" id="rider-field" name="rider_id" value="">
|
<input type="hidden" id="rider-field" name="rider_id" value="">
|
||||||
<div class="table-frame" data-name="rider">
|
<div class="table-frame" data-name="rider">
|
||||||
|
|
@ -578,6 +585,9 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<button type="submit" class="btn btn-success">Submit</button>
|
<button type="submit" class="btn btn-success">Submit</button>
|
||||||
|
{% if ftags.set_map_coordinate and is_granted('joborder.cancel') and not obj.isCancelled %}
|
||||||
|
<a href="{{ url('jo_cancel', {'id': obj.getID}) }}" class="btn btn-danger btn-cancel-job-order">Cancel Job Order</a>
|
||||||
|
{% endif %}
|
||||||
<a href="{{ return_url }}" class="btn btn-secondary">Back</a>
|
<a href="{{ return_url }}" class="btn btn-secondary">Back</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -622,7 +632,7 @@ $(function() {
|
||||||
});
|
});
|
||||||
var icon_hub = L.divIcon({
|
var icon_hub = L.divIcon({
|
||||||
className: 'map-div-icon',
|
className: 'map-div-icon',
|
||||||
html: "<div style='background-color:#00FF00;' class='marker-pin'></div><i class='fa fa-home awesome'>",
|
html: "<div style='background-color:#FFFF00;' class='marker-pin'></div><i class='fa fa-home awesome'>",
|
||||||
iconSize: [39, 42],
|
iconSize: [39, 42],
|
||||||
iconAnchor: [15, 42]
|
iconAnchor: [15, 42]
|
||||||
});
|
});
|
||||||
|
|
@ -655,24 +665,42 @@ $(function() {
|
||||||
hub_table += '<td>' + hub['name'] + '</td>';
|
hub_table += '<td>' + hub['name'] + '</td>';
|
||||||
hub_table += '<td>' + hub['branch'] + '</td>';
|
hub_table += '<td>' + hub['branch'] + '</td>';
|
||||||
hub_table += '<td>' + hub['cnum'] + '</td>';
|
hub_table += '<td>' + hub['cnum'] + '</td>';
|
||||||
|
hub_table += '<td>' + hub['distance'] + '</td>';
|
||||||
hub_table += '<td></td>';
|
hub_table += '<td></td>';
|
||||||
hub_table += '</tr>';
|
hub_table += '</tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#nearest_hubs').html(hub_table);
|
$('#nearest_hubs').html(hub_table);
|
||||||
|
|
||||||
});
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if mode in ['onestep-edit'] %}
|
{% if mode in ['onestep-edit'] %}
|
||||||
// get nearest hubs ajax
|
// get nearest hubs ajax
|
||||||
|
var hub_table = '';
|
||||||
$.getJSON("{{ url('hub_nearest') }}?lat=" + lat + "&long=" + lng, function(data) {
|
$.getJSON("{{ url('hub_nearest') }}?lat=" + lat + "&long=" + lng, function(data) {
|
||||||
var hubs = data['hubs'];
|
var hubs = data['hubs'];
|
||||||
for (i in hubs) {
|
for (i in hubs) {
|
||||||
var hub = hubs[i];
|
var hub = hubs[i];
|
||||||
var hub_marker = L.marker([hub['lat'], hub['long']], { icon: icon_hub });
|
var hub_marker = L.marker([hub['lat'], hub['long']], { icon: icon_hub });
|
||||||
hubLayerGroup.addLayer(hub_marker);
|
hubLayerGroup.addLayer(hub_marker);
|
||||||
|
|
||||||
|
if (selected_hub == hub['id']) {
|
||||||
|
hub_table += '<tr data-id=' + hub['id'] + ' class="m-table__row--primary"' + '>';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hub_table += '<tr data-id=' + hub['id'] + '>';
|
||||||
|
}
|
||||||
|
hub_table += '<td>' + hub['name'] + '</td>';
|
||||||
|
hub_table += '<td>' + hub['branch'] + '</td>';
|
||||||
|
hub_table += '<td>' + hub['cnum'] + '</td>';
|
||||||
|
hub_table += '<td>' + hub['distance'] + '</td>';
|
||||||
|
hub_table += '<td></td>';
|
||||||
|
hub_table += '</tr>';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#nearest_hubs').html(hub_table);
|
||||||
});
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
@ -739,13 +767,6 @@ $(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
{% if mode in ['onestep-edit'] %}
|
|
||||||
selected_hub = '{{ obj.getHub ? obj.getHub.getID: "" }}';
|
|
||||||
$('#hub-field').val(selected_hub);
|
|
||||||
{% endif %}
|
|
||||||
{% if mode in ['onestep'] %}
|
|
||||||
selected_hub = '';
|
|
||||||
{% endif %}
|
|
||||||
$('#hubs-table').on('click', 'tr', function() {
|
$('#hubs-table').on('click', 'tr', function() {
|
||||||
var id = $(this).data('id');
|
var id = $(this).data('id');
|
||||||
|
|
||||||
|
|
@ -801,13 +822,6 @@ $(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
{% if mode in ['onestep-edit'] %}
|
|
||||||
selected_rider = '{{ obj.getRider ? obj.getRider.getID: "" }}';
|
|
||||||
$('#rider-field').val(selected_rider);
|
|
||||||
{% endif %}
|
|
||||||
{% if mode in ['onestep'] %}
|
|
||||||
selected_rider = '';
|
|
||||||
{% endif %}
|
|
||||||
$('#rider-table').on('click', 'tr', function() {
|
$('#rider-table').on('click', 'tr', function() {
|
||||||
var id = $(this).data('id');
|
var id = $(this).data('id');
|
||||||
|
|
||||||
|
|
@ -826,9 +840,29 @@ $(function() {
|
||||||
var lat = {{ obj.getCoordinates.getLatitude }};
|
var lat = {{ obj.getCoordinates.getLatitude }};
|
||||||
var lng = {{ obj.getCoordinates.getLongitude }};
|
var lng = {{ obj.getCoordinates.getLongitude }};
|
||||||
|
|
||||||
|
selected_hub = '{{ obj.getHub ? obj.getHub.getID: "" }}';
|
||||||
|
$('#hub-field').val(selected_hub);
|
||||||
|
|
||||||
|
selected_rider = '{{ obj.getRider ? obj.getRider.getID: "" }}';
|
||||||
|
$('#rider-field').val(selected_rider);
|
||||||
|
|
||||||
selectPoint(lat, lng);
|
selectPoint(lat, lng);
|
||||||
|
|
||||||
// TODO: find a way to highlight the set hub
|
// need to put selected rider on map. selected_hub is already on map because of selectPoint
|
||||||
|
riderLayerGroup.clearLayers();
|
||||||
|
|
||||||
|
$.getJSON("{{ url('hub_riders') }}?id=" + selected_hub, function(data) {
|
||||||
|
var riders = data['riders'];
|
||||||
|
for (i in riders) {
|
||||||
|
var rider = riders[i];
|
||||||
|
if (selected_rider == rider['id']) {
|
||||||
|
var rider_lat = rider['location'][0];
|
||||||
|
var rider_lng = rider['location'][1];
|
||||||
|
var rider_marker = L.marker([rider_lat, rider_lng], { icon: icon_rider_available });
|
||||||
|
riderLayerGroup.addLayer(rider_marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if mode in ['update-processing', 'update-reassign-hub'] %}
|
{% if mode in ['update-processing', 'update-reassign-hub'] %}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,12 @@
|
||||||
{% set cv = jo.getCustomerVehicle %}
|
{% set cv = jo.getCustomerVehicle %}
|
||||||
<strong>{{ cust.getNameDisplay }}</strong><br>
|
<strong>{{ cust.getNameDisplay }}</strong><br>
|
||||||
{{ cv.getPlateNumber }}<br>
|
{{ cv.getPlateNumber }}<br>
|
||||||
<a href="">Job Order #{{ jo.getID }}</a><br>
|
<a href="{{ url('jo_onestep_edit_form', {'id': jo.getID}) }}">Job Order #{{ jo.getID }}</a><br>
|
||||||
{{ jo.getServiceTypeName }}<br>
|
{{ jo.getServiceTypeName }}<br>
|
||||||
{{ jo.getStatusText }}
|
{{ jo.getStatusText }}
|
||||||
|
{% if jo.getRider != null %}
|
||||||
|
<br><br>
|
||||||
|
{% set rider = jo.getRider %}
|
||||||
|
{{ rider.getFullName }}<br>
|
||||||
|
{{ rider.getPlateNumber }}
|
||||||
|
{% endif %}
|
||||||
|
|
|
||||||
118
templates/job-order/tracker.html.twig
Normal file
118
templates/job-order/tracker.html.twig
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
{% extends 'base_minimal.html.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div id="tracker_map" style="height:100%;"></div>
|
||||||
|
<div class="map-info">
|
||||||
|
<div class="m-portlet m-portlet--mobile">
|
||||||
|
<div class="m-portlet__body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 d-flex flex-row justify-content-start">
|
||||||
|
<img class="mr-2 rider-image" src="{{ asset(rider.getImageFile ? "uploads/" ~ rider.getImageFile : "assets/images/user.gif") }}" alt="">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div><strong>Order #{{ jo.getID }}</strong></div>
|
||||||
|
<div>{{ rider.getFullName }}</div>
|
||||||
|
<div class="m-badge m-badge--brand m-badge--wide">{{ service_type }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
|
||||||
|
<script src="{{ asset('assets/js/dashboard_map.js') }}"></script>
|
||||||
|
<script src="{{ asset('assets/js/map_mqtt.js') }}"></script>
|
||||||
|
{{ include('map/' ~ map_js_file) }}
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function initMap(r_markers, c_markers, icons) {
|
||||||
|
var default_lat = {% trans %}default_lat{% endtrans %};
|
||||||
|
var default_lng = {% trans %}default_long{% endtrans %};
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
'display_overlay': false,
|
||||||
|
'enable_popup': false,
|
||||||
|
'access_token': 'pk.eyJ1Ijoia2NvcmRlcm8iLCJhIjoiY2szbzA3ZHdsMDZxdTNsbGl4ZGNnN2VxaSJ9.LRzAe3RlV8sIP1N1x0chdw',
|
||||||
|
'div_id': 'tracker_map',
|
||||||
|
'center_lat': default_lat,
|
||||||
|
'center_lng': default_lng,
|
||||||
|
'map_type': 'road',
|
||||||
|
'zoom': 13,
|
||||||
|
'rider_popup_url': '/riders/[id]/popup',
|
||||||
|
'cust_popup_url': '/job-order/[id]/popup',
|
||||||
|
'icons': icons
|
||||||
|
};
|
||||||
|
|
||||||
|
var dashmap = new DashboardMap(options, r_markers, c_markers);
|
||||||
|
dashmap.initialize();
|
||||||
|
// dashmap.loadLocations('{{ path('rider_locations') }}');
|
||||||
|
|
||||||
|
return dashmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initEventHandler(dashmap) {
|
||||||
|
var options = {
|
||||||
|
'track_jo': false,
|
||||||
|
'track_rider': true,
|
||||||
|
'channels': {
|
||||||
|
'rider_location': 'rider/{{ rider.getID }}/location',
|
||||||
|
'jo_location': 'none',
|
||||||
|
'jo_status': 'none'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var event_handler = new MapEventHandler(options, dashmap);
|
||||||
|
event_handler.connect('tracker', '{{ mqtt_host }}', {{ mqtt_port }});
|
||||||
|
}
|
||||||
|
|
||||||
|
// create icons
|
||||||
|
var icons = {
|
||||||
|
'rider_active_jo': L.divIcon({
|
||||||
|
className: 'map-div-icon',
|
||||||
|
html: "<div style='background-color:#FF0000;' class='marker-pin'></div><i class='fa fa-bolt awesome'>",
|
||||||
|
iconSize: [39, 42],
|
||||||
|
iconAnchor: [15, 42]
|
||||||
|
}),
|
||||||
|
'rider_available': L.divIcon({
|
||||||
|
className: 'map-div-icon',
|
||||||
|
html: "<div style='background-color:#00FF00;' class='marker-pin'></div><i class='fa fa-bolt awesome'>",
|
||||||
|
iconSize: [39, 42],
|
||||||
|
iconAnchor: [15, 42]
|
||||||
|
}),
|
||||||
|
'customer': L.divIcon({
|
||||||
|
className: 'map-div-icon',
|
||||||
|
html: "<div style='background-color:#0055FF;' class='marker-pin'></div><i class='fa fa-user awesome'>",
|
||||||
|
iconSize: [39, 42],
|
||||||
|
iconAnchor: [15, 42]
|
||||||
|
}),
|
||||||
|
'hub': L.divIcon({
|
||||||
|
className: 'map-div-icon',
|
||||||
|
html: "<div style='background-color:#0055FF;' class='marker-pin'></div><i class='fa fa-home awesome'>",
|
||||||
|
iconSize: [39, 42],
|
||||||
|
iconAnchor: [15, 42]
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
var r_markers = {};
|
||||||
|
var c_markers = {};
|
||||||
|
var h_markers = {};
|
||||||
|
|
||||||
|
{% set hub = jo.getHub %}
|
||||||
|
var dashmap = initMap(r_markers, c_markers, icons);
|
||||||
|
dashmap.putCustomerMarker({{ jo.getID }}, {{ jo.getCoordinates.getLatitude }}, {{ jo.getCoordinates.getLongitude }});
|
||||||
|
dashmap.putRiderActiveJOMarker({{ rider.getID }}, {{ rider_pos.getLatitude }}, {{ rider_pos.getLongitude }});
|
||||||
|
dashmap.putMarker(
|
||||||
|
{{ hub.getID }},
|
||||||
|
{{ hub.getCoordinates.getLatitude }},
|
||||||
|
{{ hub.getCoordinates.getLongitude }},
|
||||||
|
h_markers,
|
||||||
|
icons['hub'],
|
||||||
|
dashmap.map,
|
||||||
|
'/'
|
||||||
|
);
|
||||||
|
initEventHandler(dashmap, icons);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -2,142 +2,3 @@
|
||||||
integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
|
integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
|
||||||
crossorigin="">
|
crossorigin="">
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
var rider_markers = {};
|
|
||||||
var cust_markers = {};
|
|
||||||
|
|
||||||
function mapCreate(div_id, center_lat, center_lng, map_type, zoom, rider_popup_url, cust_popup_url) {
|
|
||||||
var map = L.map(div_id).setView(
|
|
||||||
[center_lat, center_lng],
|
|
||||||
zoom
|
|
||||||
);
|
|
||||||
|
|
||||||
// add tile layer
|
|
||||||
// TODO: put access token in .env
|
|
||||||
var streets = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
|
|
||||||
attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|
|
||||||
maxZoom: 18,
|
|
||||||
id: 'mapbox/streets-v11',
|
|
||||||
accessToken: 'pk.eyJ1Ijoia2NvcmRlcm8iLCJhIjoiY2szbzA3ZHdsMDZxdTNsbGl4ZGNnN2VxaSJ9.LRzAe3RlV8sIP1N1x0chdw'
|
|
||||||
}).addTo(map);
|
|
||||||
|
|
||||||
// layer groups
|
|
||||||
// .addTo(map) --> this will display your riders by default
|
|
||||||
var lg_avail_rider = L.layerGroup().addTo(map);
|
|
||||||
var lg_jo_rider = L.layerGroup().addTo(map);
|
|
||||||
var lg_cust = L.layerGroup().addTo(map);
|
|
||||||
|
|
||||||
// this little snippet will not display your riders by default.
|
|
||||||
// Instead, a toggle button will display in the map, with a checkbox with text Riders.
|
|
||||||
// Check that to display the riders
|
|
||||||
//var ridersLayerGroup = L.layerGroup();
|
|
||||||
|
|
||||||
// create icons
|
|
||||||
var icon_rider_active_jo = L.divIcon({
|
|
||||||
className: 'map-div-icon',
|
|
||||||
html: "<div style='background-color:#FF0000;' class='marker-pin'></div><i class='fa fa-bolt awesome'>",
|
|
||||||
iconSize: [39, 42],
|
|
||||||
iconAnchor: [15, 42]
|
|
||||||
});
|
|
||||||
var icon_rider_available = L.divIcon({
|
|
||||||
className: 'map-div-icon',
|
|
||||||
html: "<div style='background-color:#00FF00;' class='marker-pin'></div><i class='fa fa-bolt awesome'>",
|
|
||||||
iconSize: [39, 42],
|
|
||||||
iconAnchor: [15, 42]
|
|
||||||
});
|
|
||||||
var icon_customer = L.divIcon({
|
|
||||||
className: 'map-div-icon',
|
|
||||||
html: "<div style='background-color:#0055FF;' class='marker-pin'></div><i class='fa fa-user awesome'>",
|
|
||||||
iconSize: [39, 42],
|
|
||||||
iconAnchor: [15, 42]
|
|
||||||
});
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '{{ path('rider_locations') }}',
|
|
||||||
}).done(function(response) {
|
|
||||||
// clear all markers
|
|
||||||
lg_avail_rider.clearLayers();
|
|
||||||
lg_jo_rider.clearLayers();
|
|
||||||
lg_cust.clearLayers();
|
|
||||||
|
|
||||||
// get riders and mark
|
|
||||||
var riders = response.riders;
|
|
||||||
|
|
||||||
$.each(riders, function(rider_id, rider_data) {
|
|
||||||
// rider location
|
|
||||||
var point = rider_data['loc'];
|
|
||||||
var lat = point[0];
|
|
||||||
var long = point[1];
|
|
||||||
|
|
||||||
// customer location
|
|
||||||
var cloc = rider_data['cust_loc'];
|
|
||||||
var clat = cloc[0];
|
|
||||||
var clong = cloc[1];
|
|
||||||
|
|
||||||
// rider popup content
|
|
||||||
rider_popup = '<strong>' + rider_data['label'] + '</strong>';
|
|
||||||
|
|
||||||
// create rider markers
|
|
||||||
if (rider_data['has_jo']) {
|
|
||||||
var jo_data = rider_data['jo'];
|
|
||||||
|
|
||||||
// rider_markers[rider_id] = L.marker([lat, long], { icon: icon_rider_active_jo }).bindPopup(rider_popup);
|
|
||||||
rider_markers[rider_id] = L.marker([lat, long], { icon: icon_rider_active_jo }).bindPopup('Loading...');
|
|
||||||
// var cust_marker = L.marker([clat, clong], { icon: icon_customer }).bindPopup('Loading...');
|
|
||||||
cust_markers[jo_data['id']] = L.marker([clat, clong], { icon: icon_customer }).bindPopup('Loading...');
|
|
||||||
lg_cust.addLayer(cust_markers[jo_data['id']]);
|
|
||||||
lg_jo_rider.addLayer(rider_markers[rider_id]);
|
|
||||||
|
|
||||||
// customer popup ajax
|
|
||||||
cust_markers[jo_data['id']].on('click', function(e) {
|
|
||||||
var popup = e.target.getPopup();
|
|
||||||
var url = cust_popup_url.replace('[id]', jo_data['id']);
|
|
||||||
console.log(url);
|
|
||||||
$.get(url).done(function(data) {
|
|
||||||
popup.setContent(data);
|
|
||||||
popup.update();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// rider_markers[rider_id]= L.marker([lat, long], { icon: icon_rider_available }).bindPopup(rider_popup);
|
|
||||||
rider_markers[rider_id]= L.marker([lat, long], { icon: icon_rider_available }).bindPopup('Loading...');
|
|
||||||
lg_avail_rider.addLayer(rider_markers[rider_id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ajax loading of rider popup
|
|
||||||
rider_markers[rider_id].on('click', function(e) {
|
|
||||||
var popup = e.target.getPopup();
|
|
||||||
var url = rider_popup_url.replace('[id]', rider_id);
|
|
||||||
console.log(url);
|
|
||||||
$.get(url).done(function(data) {
|
|
||||||
popup.setContent(data);
|
|
||||||
popup.update();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// console.log(rider_markers);
|
|
||||||
});
|
|
||||||
|
|
||||||
// base layer
|
|
||||||
var baseMaps = {
|
|
||||||
'Streets': streets
|
|
||||||
};
|
|
||||||
|
|
||||||
// overlay layer
|
|
||||||
var overlayMaps = {
|
|
||||||
'Available Riders' : lg_avail_rider,
|
|
||||||
'JO Riders' : lg_jo_rider,
|
|
||||||
'Customers' : lg_cust
|
|
||||||
}
|
|
||||||
|
|
||||||
L.control.layers(baseMaps, overlayMaps).addTo(map);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
initMap();
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
{% set cust = jo.getCustomer %}
|
{% set cust = jo.getCustomer %}
|
||||||
{% set cv = jo.getCustomerVehicle %}
|
{% set cv = jo.getCustomerVehicle %}
|
||||||
<br>
|
<br>
|
||||||
<a href="">Job Order #{{ jo.getID }}</a><br>
|
<a href="{{ url('jo_onestep_edit_form', {'id': jo.getID}) }}">Job Order #{{ jo.getID }}</a><br>
|
||||||
{{ jo.getServiceTypeName }}<br>
|
{{ jo.getServiceTypeName }}<br>
|
||||||
{{ jo.getStatusText }}<br><br>
|
{{ jo.getStatusText }}<br><br>
|
||||||
{{ cust.getNameDisplay }}<br>
|
{{ cust.getNameDisplay }}<br>
|
||||||
|
|
|
||||||
|
|
@ -135,13 +135,23 @@
|
||||||
$(function() {
|
$(function() {
|
||||||
$("#row-form").submit(function(e) {
|
$("#row-form").submit(function(e) {
|
||||||
var form = $(this);
|
var form = $(this);
|
||||||
|
var formdata = form.serialize();
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
// add battery data
|
||||||
|
bdata = '';
|
||||||
|
$.each(battRows, function(index, battery) {
|
||||||
|
bdata += "&batteries%5B%5D=" + battery.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// append to form data
|
||||||
|
formdata += bdata;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: form.prop('action'),
|
url: form.prop('action'),
|
||||||
data: form.serialize()
|
data: formdata
|
||||||
}).done(function(response) {
|
}).done(function(response) {
|
||||||
// remove all error classes
|
// remove all error classes
|
||||||
removeErrors();
|
removeErrors();
|
||||||
|
|
@ -192,6 +202,7 @@ $(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
var battRows = [];
|
var battRows = [];
|
||||||
|
var batteryIds = [];
|
||||||
|
|
||||||
{% for batt in obj.getBatteries %}
|
{% for batt in obj.getBatteries %}
|
||||||
trow = {
|
trow = {
|
||||||
|
|
@ -202,8 +213,30 @@ $(function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
battRows.push(trow);
|
battRows.push(trow);
|
||||||
|
batteryIds.push({{ batt.getID }});
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
// remove battery from table
|
||||||
|
$(document).on('click', '.btn-delete', function(e) {
|
||||||
|
var btn = $(this);
|
||||||
|
var id = $(this).data('id');
|
||||||
|
|
||||||
|
$.each(battRows, function(index, battery) {
|
||||||
|
if (battery.id == id) {
|
||||||
|
battRows.splice(index, 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove from battery ids
|
||||||
|
batteryIds.splice(batteryIds.indexOf(id), 1);
|
||||||
|
|
||||||
|
// reload table
|
||||||
|
battTable.row(btn.parents('tr')).remove();
|
||||||
|
battTable.originalDataSet = battRows;
|
||||||
|
battTable.reload();
|
||||||
|
});
|
||||||
|
|
||||||
// battery data table
|
// battery data table
|
||||||
var battOptions = {
|
var battOptions = {
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -235,7 +268,17 @@ $(function() {
|
||||||
{
|
{
|
||||||
field: 'sell_price',
|
field: 'sell_price',
|
||||||
title: 'Price'
|
title: 'Price'
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
field: 'Actions',
|
||||||
|
width: 70,
|
||||||
|
title: 'Actions',
|
||||||
|
sortable: false,
|
||||||
|
overflow: 'visible',
|
||||||
|
template: function (row, index, datatable) {
|
||||||
|
return '<button data-id="' + row.id + '" type="button" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-delete" title="Delete"><i class="la la-trash"></i></button>';
|
||||||
|
},
|
||||||
|
}
|
||||||
],
|
],
|
||||||
pagination: false
|
pagination: false
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,4 @@ default_lat: 14.6091
|
||||||
default_long: 121.0223
|
default_long: 121.0223
|
||||||
#default_lat: 3.084216
|
#default_lat: 3.084216
|
||||||
#default_long: 101.6129996
|
#default_long: 101.6129996
|
||||||
default_region: my
|
default_region: ph
|
||||||
|
|
|
||||||
41
utils/mqtt_rider/rider_location_cache.py
Normal file
41
utils/mqtt_rider/rider_location_cache.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import ssl
|
||||||
|
import redis
|
||||||
|
import time
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
class RiderLocationCache(object):
|
||||||
|
|
||||||
|
def run(self, client):
|
||||||
|
print "running loop..."
|
||||||
|
client.loop_forever()
|
||||||
|
|
||||||
|
# TODO: fix this and put these guys back under the class
|
||||||
|
def init_subscriptions(client):
|
||||||
|
print "subscribing to rider/+/location"
|
||||||
|
client.subscribe('rider/+/location')
|
||||||
|
|
||||||
|
def on_connect(client, userdata, flags, rc):
|
||||||
|
init_subscriptions(client)
|
||||||
|
#print("Connected with result code "+str(rc))
|
||||||
|
# client.subscribe("$SYS/#")
|
||||||
|
|
||||||
|
def on_publish(client, userdata, mid):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_message(client, userdata, message):
|
||||||
|
redis_conn = userdata['redis']
|
||||||
|
|
||||||
|
topic_split = message.topic.split('/')
|
||||||
|
if topic_split[0] != 'rider':
|
||||||
|
return;
|
||||||
|
payload_split = message.payload.split(':')
|
||||||
|
|
||||||
|
rider_long = str(payload_split[1])
|
||||||
|
rider_lat = str(payload_split[0])
|
||||||
|
|
||||||
|
# set the location
|
||||||
|
redis_conn.geoadd('loc_rider_active', rider_long, rider_lat, topic_split[1])
|
||||||
BIN
utils/mqtt_rider/rider_location_cache.pyc
Normal file
BIN
utils/mqtt_rider/rider_location_cache.pyc
Normal file
Binary file not shown.
26
utils/mqtt_rider/riderloc.py
Normal file
26
utils/mqtt_rider/riderloc.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import rider_location_cache as rlc
|
||||||
|
import ssl
|
||||||
|
import redis
|
||||||
|
import logging
|
||||||
|
|
||||||
|
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
|
||||||
|
userdata = {'redis': redis_client}
|
||||||
|
|
||||||
|
client = mqtt.Client("", True, userdata)
|
||||||
|
client.on_connect = rlc.on_connect
|
||||||
|
# client.on_publish = on_publish
|
||||||
|
client.on_message = rlc.on_message
|
||||||
|
|
||||||
|
#client.tls_set(
|
||||||
|
# "/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE,
|
||||||
|
# tls_version=ssl.PROTOCOL_TLSv1)
|
||||||
|
#client.tls_set(
|
||||||
|
# "/root/aws_ssl_keys/fullchain.pem", cert_reqs=ssl.CERT_NONE,
|
||||||
|
# tls_version=ssl.PROTOCOL_TLSv1)
|
||||||
|
#client.connect("resqaws.jankstudio.com", 8883, 60)
|
||||||
|
client.connect("localhost", 1883, 60)
|
||||||
|
|
||||||
|
|
||||||
|
rider_location = rlc.RiderLocationCache()
|
||||||
|
rider_location.run(client)
|
||||||
12
utils/mqtt_rider/riderloc.service
Normal file
12
utils/mqtt_rider/riderloc.service
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Rider Location Cache Service
|
||||||
|
After=mosquitto.service redis.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/python /root/www/resq/utils/rider_location_cache/riderloc.py
|
||||||
|
StandardInput=tty-force
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import paho.mqtt.client as mqtt
|
import paho.mqtt.client as mqtt
|
||||||
|
import yaml
|
||||||
import ssl
|
import ssl
|
||||||
from threading import Thread
|
|
||||||
from daemonize import Daemonize
|
|
||||||
import redis
|
import redis
|
||||||
import time
|
import time
|
||||||
import signal
|
import signal
|
||||||
|
|
@ -10,29 +9,23 @@ import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: yaml configuration file for redis and mqtt settings
|
||||||
def sigint_handler(signal, frame):
|
|
||||||
#logging.warning('Interrupted')
|
|
||||||
sys.exit(0)
|
|
||||||
os._exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
def on_connect(client, userdata, flags, rc):
|
def on_connect(client, userdata, flags, rc):
|
||||||
#logging.info("Connected with result code "+str(rc))
|
logging.info("Connected with result code "+str(rc))
|
||||||
client.subscribe("$SYS/#")
|
#client.subscribe("$SYS/#")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_publish(client, userdata, mid):
|
def on_publish(client, userdata, mid):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def getRedis(i, client, logger):
|
def redis_listen(client, logger):
|
||||||
logger.info("Listening in redis events")
|
logger.info("Listening in redis events")
|
||||||
r = redis.StrictRedis(host='localhost', port=6379, db=0)
|
r = redis.StrictRedis(host='localhost', port=6379, db=0)
|
||||||
while 1:
|
while 1:
|
||||||
time.sleep(0)
|
time.sleep(0)
|
||||||
data = r.brpop("events", 10)
|
data = r.brpop("mqtt_events", 10)
|
||||||
if data:
|
if data:
|
||||||
info = data[1].split('|')
|
info = data[1].split('|')
|
||||||
logger.info("Channel: " + info[0] + " message: " + info[1])
|
logger.info("Channel: " + info[0] + " message: " + info[1])
|
||||||
|
|
@ -40,9 +33,6 @@ def getRedis(i, client, logger):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def sigint_handler(signal, frame):
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def get_logger():
|
def get_logger():
|
||||||
logger = logging.getLogger("mqtt_logger")
|
logger = logging.getLogger("mqtt_logger")
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
@ -64,25 +54,14 @@ def main():
|
||||||
client.on_connect = on_connect
|
client.on_connect = on_connect
|
||||||
client.on_publish = on_publish
|
client.on_publish = on_publish
|
||||||
|
|
||||||
client.tls_set(
|
# configure mqtt broker to accept localhost
|
||||||
"/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE,
|
client.connect("localhost", 1883, 60)
|
||||||
tls_version=ssl.PROTOCOL_TLSv1)
|
|
||||||
|
|
||||||
client.connect("resqaws.jankstudio.com", 8883, 60)
|
client.loop_start()
|
||||||
|
redis_listen(client, logger)
|
||||||
|
client.loop_end()
|
||||||
|
|
||||||
logger.info("Starting redis thread")
|
#client.loop_forever()
|
||||||
t = Thread(target=getRedis, args=(1, client, logger))
|
|
||||||
|
|
||||||
t.start()
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, sigint_handler)
|
|
||||||
client.loop_forever()
|
|
||||||
|
|
||||||
|
|
||||||
#logging.basicConfig(filename='/tmp/mqtt_sender.log', level=logging.INFO)
|
|
||||||
#logging.info('Started mqtt_sender')
|
|
||||||
|
|
||||||
#pid = "/tmp/mqtt_sender.pid"
|
|
||||||
#daemon = Daemonize(app="mqtt_sender", pid=pid, action=main)
|
|
||||||
#daemon.start()
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ client.on_message = rlc.on_message
|
||||||
#client.tls_set(
|
#client.tls_set(
|
||||||
# "/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE,
|
# "/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE,
|
||||||
# tls_version=ssl.PROTOCOL_TLSv1)
|
# tls_version=ssl.PROTOCOL_TLSv1)
|
||||||
client.tls_set(
|
#client.tls_set(
|
||||||
"/root/aws_ssl_keys/fullchain.pem", cert_reqs=ssl.CERT_NONE,
|
# "/root/aws_ssl_keys/fullchain.pem", cert_reqs=ssl.CERT_NONE,
|
||||||
tls_version=ssl.PROTOCOL_TLSv1)
|
# tls_version=ssl.PROTOCOL_TLSv1)
|
||||||
#client.connect("resqaws.jankstudio.com", 8883, 60)
|
#client.connect("resqaws.jankstudio.com", 8883, 60)
|
||||||
client.connect("localhost", 8883, 60)
|
client.connect("localhost", 8883, 60)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue