Update Delivery Trip status based on visited stops (#15723)

* feat(delivery_trip_status): Update Delivery Trip status based on visited stops

* feat(delivery_trip_status): Fix tests

* feat(delivery_trip_status): Fix allow on submit for status

* feat(delivery_trip_status): Change status mapping

* feat(delivery_trip_status): Fix patch

* Update update_delivery_trip_status.py
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
old mode 100644
new mode 100755
index 19857d2..1f231e6
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -576,3 +576,4 @@
 erpnext.patches.v11_0.drop_column_max_days_allowed
 erpnext.patches.v11_0.change_healthcare_desktop_icons
 erpnext.patches.v10_0.update_user_image_in_employee
+erpnext.patches.v11_0.update_delivery_trip_status
\ No newline at end of file
diff --git a/erpnext/patches/v11_0/update_delivery_trip_status.py b/erpnext/patches/v11_0/update_delivery_trip_status.py
new file mode 100755
index 0000000..64b3063
--- /dev/null
+++ b/erpnext/patches/v11_0/update_delivery_trip_status.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2017, Frappe and Contributors

+# License: GNU General Public License v3. See license.txt

+

+from __future__ import unicode_literals

+import frappe

+

+def execute():

+	frappe.reload_doc('stock', 'doctype', 'delivery_trip')

+	frappe.reload_doc('stock', 'doctype', 'delivery_stop', force=True)

+

+	for trip in frappe.get_all("Delivery Trip"):

+		trip_doc = frappe.get_doc("Delivery Trip", trip.name)

+

+		status = {

+			0: "Draft",

+			1: "Scheduled",

+			2: "Cancelled"

+		}[trip_doc.docstatus]

+

+		if trip_doc.docstatus == 1:

+			visited_stops = [stop.visited for stop in trip_doc.delivery_stops]

+			if all(visited_stops):

+				status = "Completed"

+			elif any(visited_stops):

+				status = "In Transit"

+

+		frappe.db.set_value("Delivery Trip", trip.name, "status", status, update_modified=False)

diff --git a/erpnext/stock/doctype/delivery_stop/delivery_stop.json b/erpnext/stock/doctype/delivery_stop/delivery_stop.json
index 7bce72d..5610a81 100644
--- a/erpnext/stock/doctype/delivery_stop/delivery_stop.json
+++ b/erpnext/stock/doctype/delivery_stop/delivery_stop.json
@@ -1,5 +1,6 @@
 {
  "allow_copy": 0, 
+ "allow_events_in_timeline": 0, 
  "allow_guest_to_view": 0, 
  "allow_import": 0, 
  "allow_rename": 0, 
@@ -176,6 +177,39 @@
   {
    "allow_bulk_edit": 0, 
    "allow_in_quick_entry": 0, 
+   "allow_on_submit": 1, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "depends_on": "eval:doc.docstatus==1", 
+   "fieldname": "visited", 
+   "fieldtype": "Check", 
+   "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": "Visited", 
+   "length": 0, 
+   "no_copy": 1, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "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, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -762,7 +796,7 @@
  "issingle": 0, 
  "istable": 1, 
  "max_attachments": 0, 
- "modified": "2018-10-11 22:32:27.450906", 
+ "modified": "2018-10-16 05:23:25.661542", 
  "modified_by": "Administrator", 
  "module": "Stock", 
  "name": "Delivery Stop", 
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
index a38c6d7..a9e2f88 100755
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js
@@ -3,6 +3,8 @@
 
 frappe.ui.form.on('Delivery Trip', {
 	setup: function (frm) {
+		frm.set_indicator_formatter('customer', (stop) => (stop.visited) ? "green" : "orange");
+
 		frm.set_query("driver", function () {
 			return {
 				filters: {
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.json b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
index a9236e8..1d32ecd 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.json
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
@@ -1,5 +1,6 @@
 {
  "allow_copy": 0, 
+ "allow_events_in_timeline": 0, 
  "allow_guest_to_view": 0, 
  "allow_import": 0, 
  "allow_rename": 0, 
@@ -540,7 +541,7 @@
   {
    "allow_bulk_edit": 0, 
    "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
+   "allow_on_submit": 1, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
@@ -575,6 +576,70 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fieldname": "status", 
+   "fieldtype": "Select", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 1, 
+   "label": "Status", 
+   "length": 0, 
+   "no_copy": 1, 
+   "options": "Draft\nScheduled\nIn Transit\nCompleted\nCancelled", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 1, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 1, 
+   "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, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "cb_more_info", 
+   "fieldtype": "Column Break", 
+   "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, 
+   "length": 0, 
+   "no_copy": 0, 
+   "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, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
    "fieldname": "amended_from", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -611,7 +676,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-10-11 22:32:04.355068", 
+ "modified": "2018-10-22 08:25:42.323147", 
  "modified_by": "Administrator", 
  "module": "Stock", 
  "name": "Delivery Trip", 
@@ -663,6 +728,7 @@
  "show_name_in_global_search": 0, 
  "sort_field": "modified", 
  "sort_order": "DESC", 
+ "title_field": "driver_name", 
  "track_changes": 0, 
  "track_seen": 0, 
  "track_views": 0
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
index 431eb6c..01b4734 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py
@@ -27,9 +27,14 @@
 		self.validate_stop_addresses()
 
 	def on_submit(self):
+		self.update_status()
 		self.update_delivery_notes()
 
+	def on_update_after_submit(self):
+		self.update_status()
+
 	def on_cancel(self):
+		self.update_status()
 		self.update_delivery_notes(delete=True)
 
 	def validate_stop_addresses(self):
@@ -37,6 +42,22 @@
 			if not stop.customer_address:
 				stop.customer_address = get_address_display(frappe.get_doc("Address", stop.address).as_dict())
 
+	def update_status(self):
+		status = {
+			0: "Draft",
+			1: "Scheduled",
+			2: "Cancelled"
+		}[self.docstatus]
+
+		if self.docstatus == 1:
+			visited_stops = [stop.visited for stop in self.delivery_stops]
+			if all(visited_stops):
+				status = "Completed"
+			elif any(visited_stops):
+				status = "In Transit"
+
+		self.db_set("status", status)
+
 	def update_delivery_notes(self, delete=False):
 		"""
 		Update all connected Delivery Notes with Delivery Trip details
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js
new file mode 100644
index 0000000..1d198b7
--- /dev/null
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js
@@ -0,0 +1,12 @@
+frappe.listview_settings['Delivery Trip'] = {
+	add_fields: ["status"],
+	get_indicator: function (doc) {
+		if (in_list(["Cancelled", "Draft"], doc.status)) {
+			return [__(doc.status), "red", "status,=," + doc.status];
+		} else if (in_list(["In Transit", "Scheduled"], doc.status)) {
+			return [__(doc.status), "orange", "status,=," + doc.status];
+		} else if (doc.status === "Completed") {
+			return [__(doc.status), "green", "status,=," + doc.status];
+		}
+	}
+};
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index b0a3d31..76b6fcd 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -9,7 +9,7 @@
 import frappe
 from erpnext.stock.doctype.delivery_trip.delivery_trip import get_contact_and_address, notify_customers
 from erpnext.tests.utils import create_test_contact_and_address
-from frappe.utils import add_days, now_datetime
+from frappe.utils import add_days, flt, now_datetime, nowdate
 
 
 class TestDeliveryTrip(unittest.TestCase):
@@ -72,6 +72,33 @@
 		self.assertEqual(len(route_list[0]), 2)  # [home_address, locked_stop]
 		self.assertEqual(len(route_list[1]), 3)  # [locked_stop, second_stop, home_address]
 
+	def test_delivery_trip_status_draft(self):
+		self.assertEqual(self.delivery_trip.status, "Draft")
+
+	def test_delivery_trip_status_scheduled(self):
+		self.delivery_trip.submit()
+		self.assertEqual(self.delivery_trip.status, "Scheduled")
+
+	def test_delivery_trip_status_cancelled(self):
+		self.delivery_trip.submit()
+		self.delivery_trip.cancel()
+		self.assertEqual(self.delivery_trip.status, "Cancelled")
+
+	def test_delivery_trip_status_in_transit(self):
+		self.delivery_trip.submit()
+		self.delivery_trip.delivery_stops[0].visited = 1
+		self.delivery_trip.save()
+		self.assertEqual(self.delivery_trip.status, "In Transit")
+
+	def test_delivery_trip_status_completed(self):
+		self.delivery_trip.submit()
+
+		for stop in self.delivery_trip.delivery_stops:
+			stop.visited = 1
+
+		self.delivery_trip.save()
+		self.assertEqual(self.delivery_trip.status, "Completed")
+
 
 def create_driver():
 	if not frappe.db.exists("Driver", "Newton Scmander"):
@@ -108,11 +135,11 @@
 			"make": "Maruti",
 			"model": "PCM",
 			"last_odometer": 5000,
-			"acquisition_date": frappe.utils.nowdate(),
+			"acquisition_date": nowdate(),
 			"location": "Mumbai",
 			"chassis_no": "1234ABCD",
 			"uom": "Litre",
-			"vehicle_value": frappe.utils.flt(500000)
+			"vehicle_value": flt(500000)
 		})
 		vehicle.insert()