diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 497f6742aed0..76c19a0a6b6b 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -124,13 +124,14 @@ "per_returned", "transporter_info", "transporter", - "driver", "lr_no", - "vehicle_no", + "delivery_trip", + "driver", "col_break34", "transporter_name", - "driver_name", "lr_date", + "vehicle_no", + "driver_name", "customer_po_details", "po_no", "column_break_17", @@ -1391,13 +1392,20 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" + }, + { + "fieldname": "delivery_trip", + "fieldtype": "Link", + "label": "Delivery Trip", + "options": "Delivery Trip", + "print_hide": 1 } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2024-03-27 13:06:49.519676", + "modified": "2024-03-29 19:03:55.374173", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 9561e4758354..b915c09e4ab2 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -64,6 +64,7 @@ class DeliveryNote(SellingController): customer_address: DF.Link | None customer_group: DF.Link | None customer_name: DF.Data | None + delivery_trip: DF.Link | None disable_rounded_total: DF.Check discount_amount: DF.Currency dispatch_address: DF.TextEditor | None @@ -76,7 +77,7 @@ class DeliveryNote(SellingController): ignore_pricing_rule: DF.Check in_words: DF.Data | None incoterm: DF.Link | None - installation_status: DF.Literal[None] + installation_status: DF.LiteralNone instructions: DF.Text | None inter_company_reference: DF.Link | None is_internal_customer: DF.Check @@ -1068,7 +1069,6 @@ def make_delivery_trip(source_name, target_doc=None): { "Delivery Note": { "doctype": "Delivery Stop", - "validation": {"docstatus": ["=", 1]}, "on_parent": target_doc, "field_map": { "name": "delivery_note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index c6b98c4134c6..8e4f970e4690 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -30,12 +30,6 @@ frappe.listview_settings["Delivery Note"] = { const docnames = doclist.get_checked_items(true); if (selected_docs.length > 0) { - for (let doc of selected_docs) { - if (!doc.docstatus) { - frappe.throw(__("Cannot create a Delivery Trip from Draft documents.")); - } - } - frappe.new_doc("Delivery Trip").then(() => { // Empty out the child table before inserting new ones cur_frm.set_value("delivery_stops", []); diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 499a4a653e29..e8ec2982f4a4 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -22,6 +22,7 @@ make_delivery_trip, make_sales_invoice, ) +from erpnext.stock.doctype.delivery_trip.test_delivery_trip import create_driver from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import ( @@ -1064,6 +1065,21 @@ def test_delivery_trip(self): dn = create_delivery_note() dt = make_delivery_trip(dn.name) self.assertEqual(dn.name, dt.delivery_stops[0].delivery_note) + dt.delivery_stops[0].customer_address = "fake string" + dt.flags.ignore_mandatory = True + dt.save() + dn.reload() + self.assertEqual(dn.delivery_trip, dt.name) + + dn = create_delivery_note(do_not_submit=True) + dt = make_delivery_trip(dn.name) + self.assertEqual(dn.name, dt.delivery_stops[0].delivery_note) + dt.driver = create_driver() + self.assertRaisesRegex( + frappe.exceptions.ValidationError, + r"^Delivery Notes should not be in draft state when submitting a Delivery Trip.*", + dt.submit, + ) def test_delivery_note_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js index 4f8649c0bfa2..e0c20cf1351b 100755 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js @@ -60,7 +60,6 @@ frappe.ui.form.on("Delivery Trip", { company: frm.doc.company, }, get_query_filters: { - docstatus: 1, company: frm.doc.company, }, }); diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index 58f393df5d04..24910a5f5887 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -54,11 +54,18 @@ def validate(self): if self._action == "submit" and not self.driver: frappe.throw(_("A driver must be set to submit.")) + if self._action == "submit": + self.validate_delivery_note_not_draft() self.validate_stop_addresses() + def on_update(self): + self.update_delivery_notes() + + def on_trash(self): + self.update_delivery_notes(delete=True) + def on_submit(self): self.update_status() - self.update_delivery_notes() def on_update_after_submit(self): self.update_status() @@ -72,6 +79,20 @@ def validate_stop_addresses(self): if not stop.customer_address: stop.customer_address = get_address_display(frappe.get_doc("Address", stop.address).as_dict()) + def validate_delivery_note_not_draft(self): + delivery_notes = list(set(stop.delivery_note for stop in self.delivery_stops if stop.delivery_note)) + draft_delivery_notes = frappe.get_all( + "Delivery Note", + {"docstatus": 0, "name": ["in", delivery_notes]}, + pluck="name", + ) + if draft_delivery_notes: + frappe.throw( + _( + "Delivery Notes should not be in draft state when submitting a Delivery Trip. The following Delivery Notes are still in draft state: {0}. Please submit them first." + ).format(", ".join(draft_delivery_notes)) + ) + def update_status(self): status = {0: "Draft", 1: "Scheduled", 2: "Cancelled"}[self.docstatus] @@ -100,22 +121,29 @@ def update_delivery_notes(self, delete=False): "driver": self.driver, "driver_name": self.driver_name, "vehicle_no": self.vehicle, + "delivery_trip": self.name, "lr_no": self.name, "lr_date": self.departure_time, } + delivery_notes_updated = set() + for delivery_note in delivery_notes: note_doc = frappe.get_doc("Delivery Note", delivery_note) for field, value in update_fields.items(): + prev_value = getattr(note_doc, field) value = None if delete else value + if prev_value != value: + delivery_notes_updated.add(delivery_note) setattr(note_doc, field, value) - note_doc.flags.ignore_validate_update_after_submit = True - note_doc.save() + if delivery_note in delivery_notes_updated: + note_doc.flags.ignore_validate_update_after_submit = True + note_doc.save() - delivery_notes = [get_link_to_form("Delivery Note", note) for note in delivery_notes] - frappe.msgprint(_("Delivery Notes {0} updated").format(", ".join(delivery_notes))) + delivery_notes_updated = [get_link_to_form("Delivery Note", note) for note in delivery_notes_updated] + frappe.msgprint(_("Delivery Notes {0} updated").format(", ".join(delivery_notes_updated))) @frappe.whitelist() def process_route(self, optimize):