Added feature to optimize routes, also cleaned up code (#15193)
This commit is contained in:
parent
a2288ff8e7
commit
2175b90b8d
@ -14,6 +14,7 @@
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -46,6 +47,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -78,6 +80,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -108,6 +111,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -139,6 +143,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -169,6 +174,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -201,6 +207,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -231,6 +238,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -262,6 +270,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -292,12 +301,13 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "estimated_arrival",
|
||||
"fieldtype": "Time",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@ -323,6 +333,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -354,6 +365,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -384,6 +396,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -416,6 +429,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -446,6 +460,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -477,6 +492,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -507,6 +523,7 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -547,7 +564,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-02-22 16:43:55.257470",
|
||||
"modified": "2018-08-21 22:25:53.276548",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Stop",
|
||||
@ -561,5 +578,6 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
22
erpnext/stock/doctype/delivery_trip/delivery_trip.js
Normal file → Executable file
22
erpnext/stock/doctype/delivery_trip/delivery_trip.js
Normal file → Executable file
@ -66,17 +66,27 @@ frappe.ui.form.on('Delivery Trip', {
|
||||
|
||||
calculate_arrival_time: function (frm) {
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.doctype.delivery_trip.delivery_trip.calculate_time_matrix',
|
||||
method: 'erpnext.stock.doctype.delivery_trip.delivery_trip.get_arrival_times',
|
||||
freeze: true,
|
||||
freeze_message: __("Updating estimated arrival times."),
|
||||
args: {
|
||||
name: frm.doc.name
|
||||
name: frm.doc.name,
|
||||
},
|
||||
callback: function (r) {
|
||||
frm.reload_doc();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
optimize_route: function (frm) {
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.doctype.delivery_trip.delivery_trip.optimize_route',
|
||||
freeze: true,
|
||||
freeze_message: __("Optimizing routes."),
|
||||
args: {
|
||||
name: frm.doc.name,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (r.message.error) {
|
||||
frappe.throw(__("Malformatted address for {0}, please fix to continue.",
|
||||
[r.message.error.destination.address]));
|
||||
}
|
||||
frm.reload_doc();
|
||||
}
|
||||
});
|
||||
|
@ -467,6 +467,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "optimize_route",
|
||||
"fieldtype": "Button",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Optimize Route",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -575,7 +608,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 14:44:36.993178",
|
||||
"modified": "2018-08-29 14:44:36.993178",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Trip",
|
||||
|
@ -5,15 +5,16 @@
|
||||
from __future__ import unicode_literals
|
||||
import datetime
|
||||
import frappe
|
||||
import googlemaps
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.user import get_user_fullname
|
||||
from frappe.utils import getdate, cstr
|
||||
from frappe.utils import getdate, cstr, get_datetime
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
|
||||
class DeliveryTrip(Document):
|
||||
pass
|
||||
|
||||
|
||||
def get_default_contact(out, name):
|
||||
contact_persons = frappe.db.sql(
|
||||
"""
|
||||
@ -80,61 +81,59 @@ def get_contact_display(contact):
|
||||
}
|
||||
return contact_info.html
|
||||
|
||||
@frappe.whitelist()
|
||||
def calculate_time_matrix(name):
|
||||
"""Calucation and round in closest 15 minutes, delivery stops"""
|
||||
|
||||
gmaps = frappe.db.get_value('Google Maps', None,
|
||||
['client_key', 'enabled', 'home_address'], as_dict=1)
|
||||
def process_route(name, optimize):
|
||||
doc = frappe.get_doc("Delivery Trip", name)
|
||||
settings = frappe.get_single("Google Maps Settings")
|
||||
gmaps_client = settings.get_client()
|
||||
|
||||
if not gmaps.enabled:
|
||||
if not settings.enabled:
|
||||
frappe.throw(_("Google Maps integration is not enabled"))
|
||||
|
||||
home_address = get_address_display(frappe.get_doc("Address", settings.home_address).as_dict())
|
||||
address_list = []
|
||||
|
||||
for stop in doc.delivery_stops:
|
||||
address_list.append(stop.customer_address)
|
||||
|
||||
# Cannot add datetime.date to datetime.timedelta
|
||||
departure_datetime = get_datetime(doc.date) + doc.departure_time
|
||||
|
||||
try:
|
||||
gmaps_client = googlemaps.Client(key=gmaps.client_key)
|
||||
directions = gmaps_client.directions(origin=home_address,
|
||||
destination=home_address, waypoints=address_list,
|
||||
optimize_waypoints=optimize, departure_time=departure_datetime)
|
||||
except Exception as e:
|
||||
frappe.throw(e.message)
|
||||
frappe.throw((e.message))
|
||||
|
||||
secs_15min = 900
|
||||
doc = frappe.get_doc('Delivery Trip', name)
|
||||
departure_time = doc.departure_time
|
||||
matrix_duration = []
|
||||
if not directions:
|
||||
return
|
||||
|
||||
for i, stop in enumerate(doc.delivery_stops):
|
||||
if i == 0:
|
||||
# The first row is the starting pointing
|
||||
origin = gmaps.home_address
|
||||
destination = format_address(doc.delivery_stops[i].address)
|
||||
distance_calc = gmaps_client.distance_matrix(origin, destination)
|
||||
matrix_duration.append(distance_calc)
|
||||
directions = directions[0]
|
||||
duration = 0
|
||||
|
||||
try:
|
||||
distance_secs = distance_calc['rows'][0]['elements'][0]['duration']['value']
|
||||
except Exception as e:
|
||||
frappe.throw(_("Error '{0}' occured. Arguments {1}.").format(e.message, e.args))
|
||||
# Google Maps returns the optimized order of the waypoints that were sent
|
||||
for idx, order in enumerate(directions.get("waypoint_order")):
|
||||
# We accordingly rearrange the rows
|
||||
doc.delivery_stops[order].idx = idx + 1
|
||||
# Google Maps returns the "legs" in the optimized order, so we loop through it
|
||||
duration += directions.get("legs")[idx].get("duration").get("value")
|
||||
arrival_datetime = departure_datetime + datetime.timedelta(seconds=duration)
|
||||
doc.delivery_stops[order].estimated_arrival = arrival_datetime
|
||||
|
||||
stop.estimated_arrival = round_timedelta(
|
||||
departure_time + datetime.timedelta(0, distance_secs + secs_15min),
|
||||
datetime.timedelta(minutes=15))
|
||||
else:
|
||||
# Calculation based on previous
|
||||
origin = format_address(doc.delivery_stops[i - 1].address)
|
||||
destination = format_address(doc.delivery_stops[i].address)
|
||||
distance_calc = gmaps_client.distance_matrix(origin, destination)
|
||||
matrix_duration.append(distance_calc)
|
||||
doc.save()
|
||||
frappe.db.commit()
|
||||
|
||||
try:
|
||||
distance_secs = distance_calc['rows'][0]['elements'][0]['duration']['value']
|
||||
except Exception as e:
|
||||
frappe.throw(_("Error '{0}' occured. Arguments {1}.").format(e.message, e.args))
|
||||
|
||||
stop.estimated_arrival = round_timedelta(
|
||||
doc.delivery_stops[i - 1].estimated_arrival +
|
||||
datetime.timedelta(0, distance_secs + secs_15min), datetime.timedelta(minutes=15))
|
||||
stop.save()
|
||||
frappe.db.commit()
|
||||
@frappe.whitelist()
|
||||
def optimize_route(name):
|
||||
process_route(name, optimize=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_arrival_times(name):
|
||||
process_route(name, optimize=False)
|
||||
|
||||
return matrix_duration
|
||||
|
||||
@frappe.whitelist()
|
||||
def notify_customers(docname, date, driver, vehicle, sender_email, delivery_notification):
|
||||
|
Loading…
x
Reference in New Issue
Block a user