diff --git a/vsd_fleet_ms/hooks.py b/vsd_fleet_ms/hooks.py index a27f2e8..84929f9 100644 --- a/vsd_fleet_ms/hooks.py +++ b/vsd_fleet_ms/hooks.py @@ -142,6 +142,11 @@ # "vsd_fleet_ms.tasks.monthly" # ], # } +scheduler_events = { + "daily": [ + "vsd_fleet_ms.utils.document_expiry.notify_expiring_documents", + ], +} # Testing # ------- diff --git a/vsd_fleet_ms/utils/document_expiry.py b/vsd_fleet_ms/utils/document_expiry.py new file mode 100644 index 0000000..8d13c06 --- /dev/null +++ b/vsd_fleet_ms/utils/document_expiry.py @@ -0,0 +1,110 @@ +import frappe +from frappe.utils import getdate, nowdate + + +DEFAULT_ALERT_DAYS = {7, 3, 1, 0} + + +def _get_recipients_by_roles(roles): + if not roles: + return [] + + users = frappe.get_all( + "Has Role", + filters={"role": ["in", roles]}, + fields=["parent"], + distinct=True, + ) + user_ids = [u.parent for u in users] + if not user_ids: + return [] + + return [ + u.email + for u in frappe.get_all( + "User", + filters={"name": ["in", user_ids], "enabled": 1}, + fields=["email"], + ) + if u.email + ] + + +def _already_notified(subject): + return frappe.db.exists( + "Notification Log", + {"subject": subject, "creation": (">=", nowdate())}, + ) + + +def notify_expiring_documents(): + """Notify on expiring Truck/Trailer documents (Document Attachments).""" + today = getdate(nowdate()) + roles = ["Fleet Manager", "Logistic Master", "System Manager"] + recipients = _get_recipients_by_roles(roles) + + rows = frappe.get_all( + "Document Attachments", + filters={"parenttype": ["in", ["Truck", "Trailers"]]}, + fields=["name1", "reference_number", "expire_date", "parenttype", "parent"], + ) + + for row in rows: + if not row.expire_date: + continue + exp_date = getdate(row.expire_date) + days_left = (exp_date - today).days + + is_expired = days_left < 0 + if not is_expired and days_left not in DEFAULT_ALERT_DAYS: + continue + + if is_expired: + subject = ( + f"Document expiry Reminder: {row.parenttype} {row.parent} " + f"Document {row.name1 or row.reference_number} expired" + ) + else: + subject = ( + f"Document expiry Reminder: {row.parenttype} {row.parent} " + f"Document {row.name1 or row.reference_number} expires in {days_left} day(s)" + ) + + if _already_notified(subject): + continue + + if is_expired: + days_ago = abs(days_left) + message = ( + f"{row.parenttype} {row.parent} document " + f"{row.name1 or row.reference_number} expired on {exp_date} " + f"({days_ago} day(s) ago). " + "Please update the document validation." + ) + else: + message = ( + f"{row.parenttype} {row.parent} document " + f"{row.name1 or row.reference_number} expires on {exp_date} " + f"({days_left} day(s) left). " + "Please update the document validation." + ) + + frappe.get_doc( + { + "doctype": "Notification Log", + "subject": subject, + "type": "Alert", + "document_type": row.parenttype, + "document_name": row.parent, + "email_content": message, + "for_user": "Administrator", + } + ).insert(ignore_permissions=True) + + if recipients: + frappe.sendmail( + recipients=recipients, + subject=subject, + message=message, + now=True, + ) diff --git a/vsd_fleet_ms/vsd_fleet_ms/doctype/trips/trips.js b/vsd_fleet_ms/vsd_fleet_ms/doctype/trips/trips.js index d58c16e..aa84646 100644 --- a/vsd_fleet_ms/vsd_fleet_ms/doctype/trips/trips.js +++ b/vsd_fleet_ms/vsd_fleet_ms/doctype/trips/trips.js @@ -498,5 +498,60 @@ function fuel_amount() { '
Total Fuel Rejected: ' + rejected_fuel.toLocaleString() + "
"; + + if (cur_frm.doc.transporter_type !== "In House" || !cur_frm.doc.truck_number) { + cur_frm.get_field("html4").wrapper.innerHTML = content; + if (cur_frm.fields_dict.custom_truck_tank_fuel_balance) { + cur_frm.get_field("custom_truck_tank_fuel_balance").wrapper.innerHTML = ""; + } + return; + } + cur_frm.get_field("html4").wrapper.innerHTML = content; + + frappe.db + .get_value("Truck", cur_frm.doc.truck_number, "trans_ms_fuel_warehouse") + .then((r) => { + var warehouse = null; + if (r && r.message) { + warehouse = r.message.trans_ms_fuel_warehouse; + } + if (!warehouse) { + return; + } + + frappe.db.get_single_value("Transport Settings", "fuel_item").then((fuel_item) => { + if (!fuel_item) { + return; + } + + frappe.db + .get_value("Bin", { item_code: fuel_item, warehouse: warehouse }, "actual_qty") + .then((b) => { + var balance = 0; + if (b && b.message) { + balance = b.message.actual_qty || 0; + } + if (cur_frm.fields_dict.custom_truck_tank_fuel_balance) { + cur_frm.get_field("custom_truck_tank_fuel_balance").wrapper.innerHTML = + 'Trip Tank Fuel Balance: ' + + balance.toLocaleString() + + "
"; + } + }); + }); + }); } + + +frappe.ui.form.on("Trips", { + stock_out_entry: function () { + fuel_amount(); + }, + fuel_request_history: function () { + fuel_amount(); + }, + total_fuel: function () { + fuel_amount(); + }, +}); diff --git a/vsd_fleet_ms/vsd_fleet_ms/doctype/trips/trips.json b/vsd_fleet_ms/vsd_fleet_ms/doctype/trips/trips.json index 1475029..959a785 100644 --- a/vsd_fleet_ms/vsd_fleet_ms/doctype/trips/trips.json +++ b/vsd_fleet_ms/vsd_fleet_ms/doctype/trips/trips.json @@ -70,6 +70,7 @@ "fuel_stock_out", "reduce_stock", "html4", + "custom_truck_tank_fuel_balance", "column_break_c2lwo", "stock_out_entry", "section_break_vefjo", @@ -548,6 +549,11 @@ "fieldtype": "HTML", "label": "Fuel Amount" }, + { + "fieldname": "custom_truck_tank_fuel_balance", + "fieldtype": "HTML", + "label": "Truck Tank Fuel Balance" + }, { "allow_on_submit": 1, "fieldname": "trip_completed_date",