From fa36c335088fa066ee5ee95c497320a733ae3568 Mon Sep 17 00:00:00 2001 From: iamtalib13 Date: Sun, 5 Apr 2026 14:31:34 +0530 Subject: [PATCH 1/3] feat: add multi-property support to Lease via Lease Item table - Added 'property_unit' field to Lease Item child table - Updated Lease validation to check for conflicts across all linked properties - Updated Lease submission to update status and create handover checklists for all properties - Maintained backward compatibility with existing header property field --- .../doctype/lease/lease.js | 7 + .../doctype/lease/lease.py | 136 ++++++++++-------- .../doctype/lease_item/lease_item.json | 7 + 3 files changed, 89 insertions(+), 61 deletions(-) diff --git a/propms/property_management_solution/doctype/lease/lease.js b/propms/property_management_solution/doctype/lease/lease.js index aa89c6a..1e8edbd 100755 --- a/propms/property_management_solution/doctype/lease/lease.js +++ b/propms/property_management_solution/doctype/lease/lease.js @@ -11,6 +11,13 @@ frappe.ui.form.on('Lease', { ] }; }); + frm.set_query("property_unit", "lease_item", function() { + return { + "filters": { + "company": frm.doc.company, + } + }; + }); frm.set_query("property", function() { return { "filters": { diff --git a/propms/property_management_solution/doctype/lease/lease.py b/propms/property_management_solution/doctype/lease/lease.py index 595ae90..100d7d3 100755 --- a/propms/property_management_solution/doctype/lease/lease.py +++ b/propms/property_management_solution/doctype/lease/lease.py @@ -11,83 +11,97 @@ class Lease(Document): + def get_all_properties(self): + properties = set() + if self.property: + properties.add(self.property) + for item in self.lease_item: + if item.property_unit: + properties.add(item.property_unit) + return list(properties) + def on_submit(self): try: - checklist_doc = frappe.get_doc("Checklist Checkup Area", "Handover") - if checklist_doc: - check_list = [] - for task in checklist_doc.task: - check = {} - check["checklist_task"] = task.task_name - check_list.append(check) + properties = self.get_all_properties() + for prop in properties: + checklist_doc = frappe.get_doc("Checklist Checkup Area", "Handover") + if checklist_doc: + check_list = [] + for task in checklist_doc.task: + check = {} + check["checklist_task"] = task.task_name + check_list.append(check) - frappe.get_doc( - dict( - doctype="Daily Checklist", - area="Handover", - checkup_date=self.start_date, - daily_checklist_detail=check_list, - property=self.property, - ) - ).insert() + frappe.get_doc( + dict( + doctype="Daily Checklist", + area="Handover", + checkup_date=self.start_date, + daily_checklist_detail=check_list, + property=prop, + ) + ).insert() except Exception as e: app_error_log(frappe.session.user, str(e)) def validate(self): try: + properties = self.get_all_properties() # Lease Status Validation: Prevent multiple active leases per property if self.lease_status == "Active": - # Query for other non-draft leases for the same property - conflicting_leases = frappe.db.get_all( - "Lease", - filters={ - "property": self.property, - "lease_status": ["!=", "Draft"], - "name": ["!=", self.name], - "docstatus": ["<", 2], # Exclude cancelled - }, - fields=["name", "end_date", "lease_status"], - ) - for lease in conflicting_leases: - # If end_date is blank or in the future, block activation - if not lease["end_date"] or getdate(lease["end_date"]) > getdate( - self.start_date - ): - msg = _( - "Cannot activate lease {0} for property {1}.
Conflicting lease: {2} (Status: {3}, End Date: {4})" - ).format( - self.name, - self.property, - lease["name"], - lease["lease_status"], - lease["end_date"] or "None", - ) - frappe.throw(msg, frappe.ValidationError) + for prop in properties: + # Query for other non-draft leases for the same property + conflicting_leases = frappe.db.get_all( + "Lease", + filters={ + "property": prop, + "lease_status": ["!=", "Draft"], + "name": ["!=", self.name], + "docstatus": ["<", 2], # Exclude cancelled + }, + fields=["name", "end_date", "lease_status"], + ) + for lease in conflicting_leases: + # If end_date is blank or in the future, block activation + if not lease["end_date"] or getdate(lease["end_date"]) > getdate( + self.start_date + ): + msg = _( + "Cannot activate lease {0} for property {1}.
Conflicting lease: {2} (Status: {3}, End Date: {4})" + ).format( + self.name, + prop, + lease["name"], + lease["lease_status"], + lease["end_date"] or "None", + ) + frappe.throw(msg, frappe.ValidationError) - if ( - get_datetime(self.start_date) - <= get_datetime(now()) - <= get_datetime(add_months(self.end_date, -3)) - ): - frappe.db.set_value("Property", self.property, "status", "On Lease") - frappe.msgprint(_(f'Property "{self.property}" has now been set On Lease from Active for Lease "{self.name}"')) - if ( - self.skip_end_date == None - ): + for prop in properties: if ( - get_datetime(add_months(self.end_date, -3)) + get_datetime(self.start_date) <= get_datetime(now()) - <= get_datetime(add_months(self.end_date, 3)) + <= get_datetime(add_months(self.end_date, -3)) ): + frappe.db.set_value("Property", prop, "status", "On Lease") + frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"')) + if ( + self.skip_end_date == None + ): + if ( + get_datetime(add_months(self.end_date, -3)) + <= get_datetime(now()) + <= get_datetime(add_months(self.end_date, 3)) + ): + frappe.db.set_value( + "Property", prop, "status", "Off Lease in 3 Months" + ) + frappe.msgprint(_(f'Property "{prop}" has now been set Off Lease in 3 Months for Lease "{self.name}"')) + else: frappe.db.set_value( - "Property", self.property, "status", "Off Lease in 3 Months" + "Property", prop, "status", "On Lease" ) - frappe.msgprint(_(f'Property "{self.property}" has now been set Off Lease in 3 Months for Lease "{self.name}"')) - else: - frappe.db.set_value( - "Property", self.property, "status", "On Lease" - ) - frappe.msgprint(_(f'Property "{self.property}" has now been set On Lease from Active for Lease "{self.name}"')) + frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"')) except Exception as e: app_error_log(frappe.session.user, str(e)) diff --git a/propms/property_management_solution/doctype/lease_item/lease_item.json b/propms/property_management_solution/doctype/lease_item/lease_item.json index 1563ac6..e13fef8 100755 --- a/propms/property_management_solution/doctype/lease_item/lease_item.json +++ b/propms/property_management_solution/doctype/lease_item/lease_item.json @@ -30,6 +30,13 @@ "label": "Lease Item", "options": "Item" }, + { + "fieldname": "property_unit", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Property Unit", + "options": "Property" + }, { "columns": 1, "fieldname": "frequency", From fcc2ada62777f16adcb50ead50cd6c76795bb530 Mon Sep 17 00:00:00 2001 From: iamtalib13 Date: Fri, 10 Apr 2026 17:32:49 +0530 Subject: [PATCH 2/3] fix: restore lease status order for off-lease window --- .../doctype/lease/lease.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/propms/property_management_solution/doctype/lease/lease.py b/propms/property_management_solution/doctype/lease/lease.py index 100d7d3..edbf091 100755 --- a/propms/property_management_solution/doctype/lease/lease.py +++ b/propms/property_management_solution/doctype/lease/lease.py @@ -78,13 +78,6 @@ def validate(self): frappe.throw(msg, frappe.ValidationError) for prop in properties: - if ( - get_datetime(self.start_date) - <= get_datetime(now()) - <= get_datetime(add_months(self.end_date, -3)) - ): - frappe.db.set_value("Property", prop, "status", "On Lease") - frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"')) if ( self.skip_end_date == None ): @@ -97,6 +90,13 @@ def validate(self): "Property", prop, "status", "Off Lease in 3 Months" ) frappe.msgprint(_(f'Property "{prop}" has now been set Off Lease in 3 Months for Lease "{self.name}"')) + elif ( + get_datetime(self.start_date) + <= get_datetime(now()) + <= get_datetime(add_months(self.end_date, -3)) + ): + frappe.db.set_value("Property", prop, "status", "On Lease") + frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"')) else: frappe.db.set_value( "Property", prop, "status", "On Lease" From 3f6e3c350bdb6dfaf8a40f6a215ccc4399a7b002 Mon Sep 17 00:00:00 2001 From: iamtalib13 Date: Sat, 11 Apr 2026 00:05:04 +0530 Subject: [PATCH 3/3] fix(lease): skip property status updates for draft leases --- .../doctype/lease/lease.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/propms/property_management_solution/doctype/lease/lease.py b/propms/property_management_solution/doctype/lease/lease.py index edbf091..40bc2ba 100755 --- a/propms/property_management_solution/doctype/lease/lease.py +++ b/propms/property_management_solution/doctype/lease/lease.py @@ -91,17 +91,21 @@ def validate(self): ) frappe.msgprint(_(f'Property "{prop}" has now been set Off Lease in 3 Months for Lease "{self.name}"')) elif ( + self.lease_status != "Draft" + and ( get_datetime(self.start_date) <= get_datetime(now()) <= get_datetime(add_months(self.end_date, -3)) + ) ): frappe.db.set_value("Property", prop, "status", "On Lease") frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"')) else: - frappe.db.set_value( - "Property", prop, "status", "On Lease" - ) - frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"')) + if self.lease_status != "Draft": + frappe.db.set_value( + "Property", prop, "status", "On Lease" + ) + frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"')) except Exception as e: app_error_log(frappe.session.user, str(e))