diff --git a/propms/property_management_solution/doctype/lease/lease.js b/propms/property_management_solution/doctype/lease/lease.js
index 1e8edbd..1212957 100755
--- a/propms/property_management_solution/doctype/lease/lease.js
+++ b/propms/property_management_solution/doctype/lease/lease.js
@@ -27,15 +27,17 @@ frappe.ui.form.on('Lease', {
});
},
refresh: function(frm) {
+ frm.trigger("custom_set_intro");
+
cur_frm.add_custom_button(__("Make Invoice Schedule"), function() {
make_lease_invoice_schedule(cur_frm);
- });
+ }, __("Actions"));
cur_frm.add_custom_button(__("Generate Pending Invoice"), function() {
generate_pending_invoice();
- });
+ }, __("Actions"));
cur_frm.add_custom_button(__("Make Invoice Schedule for all Lease"), function() {
getAllLease(cur_frm);
- });
+ }, __("Actions"));
// Add custom buttons for Accounts Receivable and Accounting Ledger
if (!frm.doc.__islocal) {
@@ -62,6 +64,39 @@ frappe.ui.form.on('Lease', {
},
__("View")
);
+
+ // Add "Renew Lease" custom button
+ if (
+ ["Active", "Expired"].includes(frm.doc.lease_status) &&
+ (frappe.user.has_role("Property Manager") ||
+ frappe.user.has_role("System Manager"))
+ ) {
+ frm.add_custom_button(
+ __("Renew Lease"),
+ function () {
+ frappe.confirm(__("Are you sure you want to renew this lease?"), function () {
+ frappe.call({
+ method: "propms.property_management_solution.doctype.lease.lease.initiate_lease_renewal",
+ args: {
+ source_lease_name: frm.doc.name,
+ },
+ freeze: true,
+ freeze_message: __("Initiating Lease Renewal..."),
+ callback: function (r) {
+ if (r.message) {
+ frappe.show_alert({
+ message: __("Lease renewal draft created successfully: {0}", [r.message]),
+ indicator: "green",
+ });
+ frappe.set_route("Form", "Lease", r.message);
+ }
+ },
+ });
+ });
+ },
+ __("Actions")
+ );
+ }
}
},
skip_end_date: function(frm) {
@@ -111,7 +146,50 @@ frappe.ui.form.on('Lease', {
}
});
}
- }
+ },
+
+ custom_set_intro: function (frm) {
+ // Show intro banner if this is a renewal lease
+ if (frm.doc.renewed_from) {
+ frappe.db
+ .get_value("Lease", frm.doc.name, ["renewal_initiated_by", "creation"])
+ .then((r) => {
+ if (r.message) {
+ let d = r.message;
+ let created_on = moment(d.creation).format("DD-MM-YYYY hh:mm A");
+
+ frm.set_intro(
+ __("This lease is a renewal of {0}, initiated by {1} on {2}.", [
+ `${frm.doc.renewed_from}`,
+ `${d.renewal_initiated_by || __("Unknown")}`,
+ `${created_on}`,
+ ]),
+ "blue"
+ );
+ }
+ });
+ }
+
+ // Show intro banner if this lease has already been renewed
+ frappe.db
+ .get_value("Lease", { renewed_from: frm.doc.name }, ["name", "renewal_initiated_by", "creation"])
+ .then((r) => {
+ if (r && r.message && r.message.name) {
+ let d = r.message;
+
+ let creation_time = moment(d.creation).format("DD-MM-YYYY hh:mm A");
+
+ frm.set_intro(
+ __("This lease has been renewed: {0} by {1} on {2}.", [
+ `${d.name}`,
+ `${d.renewal_initiated_by || ""}`,
+ `${creation_time}`,
+ ]),
+ "green"
+ );
+ }
+ });
+ },
});
var make_lease_invoice_schedule = function(frm){
diff --git a/propms/property_management_solution/doctype/lease/lease.json b/propms/property_management_solution/doctype/lease/lease.json
index 9399c9f..1d1ebe6 100755
--- a/propms/property_management_solution/doctype/lease/lease.json
+++ b/propms/property_management_solution/doctype/lease/lease.json
@@ -53,7 +53,11 @@
"witness_2",
"witness_1",
"section_break_36",
- "amended_from"
+ "amended_from",
+ "lease_renewal_details_section",
+ "renewed_from",
+ "column_break_renewal",
+ "renewal_initiated_by"
],
"fields": [
{
@@ -325,6 +329,32 @@
"fieldname": "skip_end_date",
"fieldtype": "Check",
"label": "Skip End Date"
+ },
+ {
+ "fieldname": "lease_renewal_details_section",
+ "fieldtype": "Section Break",
+ "label": "Lease Renewal Details",
+ "collapsible": 1
+ },
+ {
+ "fieldname": "renewed_from",
+ "fieldtype": "Link",
+ "label": "Renewed From",
+ "options": "Lease",
+ "read_only": 1,
+ "no_copy": 1
+ },
+ {
+ "fieldname": "column_break_renewal",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "renewal_initiated_by",
+ "fieldtype": "Link",
+ "label": "Renewal Initiated By",
+ "options": "User",
+ "read_only": 1,
+ "no_copy": 1
}
],
"links": [],
diff --git a/propms/property_management_solution/doctype/lease/lease.py b/propms/property_management_solution/doctype/lease/lease.py
index 72b1b52..3e71687 100755
--- a/propms/property_management_solution/doctype/lease/lease.py
+++ b/propms/property_management_solution/doctype/lease/lease.py
@@ -506,3 +506,62 @@ def make_lease_invoice_schedule(leasedoc):
except Exception as e:
frappe.msgprint("Exception error! Check app error log.")
app_error_log(frappe.session.user, str(e))
+
+
+@frappe.whitelist()
+def initiate_lease_renewal(source_lease_name):
+ # 1. Permission checks
+ if not any(r in frappe.get_roles() for r in ["Property Manager", "System Manager"]):
+ frappe.throw(_("You are not authorized to renew leases. Only Property Managers and System Managers can perform this action."), frappe.PermissionError)
+
+ # 2. Fetch source document
+ source_doc = frappe.get_doc("Lease", source_lease_name)
+
+ # 3. Status checks
+ if source_doc.lease_status not in ["Active", "Expired"]:
+ frappe.throw(_("Lease {0} is not eligible for renewal. Status must be Active or Expired.").format(source_lease_name), frappe.ValidationError)
+
+ # 4. Duplicate renewal check (ignoring terminated or aborted renewals)
+ duplicate_exists = frappe.db.exists("Lease", {
+ "renewed_from": source_lease_name,
+ "lease_status": ["not in", ["Not Materialized", "Terminated"]]
+ })
+ if duplicate_exists:
+ frappe.throw(_("A renewal lease already exists for this lease: {0}").format(duplicate_exists), frappe.ValidationError)
+
+ # 5. Clone Lease using standard frappe.copy_doc
+ new_lease = frappe.copy_doc(source_doc)
+ new_lease.set("lease_invoice_schedule", []) # Clear old invoice schedules
+
+ # Set renewal reference fields
+ new_lease.renewed_from = source_lease_name
+ new_lease.lease_status = "Renewal to Previous Lease"
+ new_lease.renewal_initiated_by = frappe.session.user
+
+ # Calculate dates
+ if source_doc.end_date:
+ new_lease.start_date = add_days(source_doc.end_date, 1)
+ if source_doc.start_date:
+ duration_days = (getdate(source_doc.end_date) - getdate(source_doc.start_date)).days
+ new_lease.end_date = add_days(new_lease.start_date, duration_days)
+ else:
+ new_lease.start_date = today()
+
+ # Update valid_from date for items
+ for item in new_lease.lease_item:
+ item.valid_from = new_lease.start_date
+
+ # Insert and save the new Lease as draft
+ new_lease.insert(ignore_permissions=True)
+
+ # Post a message/comment to the old lease with initiator and link details
+ comment_text = _("Lease renewal draft {0} has been initiated by {1} on {2}.").format(
+ new_lease.name,
+ frappe.session.user,
+ frappe.utils.formatdate(today())
+ )
+ source_doc.add_comment(text=comment_text)
+
+ return new_lease.name
+
+