From 047322ca6d660715370f39ac46d2e98d565922cd Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Thu, 2 Jul 2026 17:47:08 +0530 Subject: [PATCH 01/18] [ADD] estate: implement module for Real Estate businesses Create a new Real Estate module which helps businesses to manage their properties and purchasing & selling part. The module helps companies to organize their business in a structure manner, provide a way to to monitor their business and keep clean records. It helps businesses to manage their properties at ease and manage their purchasing & selling part efficiently. --- estate/__init__.py | 0 estate/__manifest__.py | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..fddbfb4d77d --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,9 @@ +{ + "name": "Real Estate", + "version": "1.0", + "author": "Mauryan Kansara", + "category": "Tutorials", + "depends": ["base"], + "application": True, + "installable": True +} \ No newline at end of file From 87187bf4e5b06b2498b5fe2a08e06b8c404b7736 Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 3 Jul 2026 11:17:31 +0530 Subject: [PATCH 02/18] [FIX] estate: added missing license in __manifest__.py Add the missing license information to the Real Estate module manifest so that the module is properly described and follows the expected module metadata requirements. --- estate/__manifest__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index fddbfb4d77d..2bd2fa297e4 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,9 +1,10 @@ { "name": "Real Estate", "version": "1.0", + "license": "LGPL-3", "author": "Mauryan Kansara", "category": "Tutorials", "depends": ["base"], "application": True, "installable": True -} \ No newline at end of file +} From 8d20b43d8dbe0bf5a4594f9634961c0216976f6f Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 3 Jul 2026 12:38:13 +0530 Subject: [PATCH 03/18] [IMP] estate: added estate_property model Add the real-estate property model to provide a structured way to manage property information in the Real Estate module. The model allows properties to be described through their basic information, availability, pricing, size, and characteristics, making it possible to keep property records organized and easily manageable. --- estate/__init__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py diff --git a/estate/__init__.py b/estate/__init__.py index e69de29bb2d..0650744f6bc 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..9bb2d528b6b --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,21 @@ +from odoo import models, fields + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Real Estate properties" + + name = fields.Char(required=True) + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date() + expected_price = fields.Float(reuired=True) + selling_price = fields.Float() + bedrooms = fields.Integer() + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Boolean() + garden_orientation = fields.Selection( + selection = [('north', 'North'), ('south', 'South'), ('east', 'East')], + ) From a37edf007840633fa25f78ad66a97ea417bf7f93 Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 3 Jul 2026 17:12:13 +0530 Subject: [PATCH 04/18] [FIX] estate: fix ['estate.property'] have no access rules warning Fix the missing access rules warning for the estate.property model. Ensure the property model is correctly configured so that properties can be managed without triggering warnings about missing access rules. --- estate/__manifest__.py | 5 ++++- estate/models/estate_property.py | 5 +++-- estate/security/ir.model.access.csv | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 2bd2fa297e4..06620ffe95b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,5 +6,8 @@ "category": "Tutorials", "depends": ["base"], "application": True, - "installable": True + "installable": True, + "data": [ + "security/ir.model.access.csv", + ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 9bb2d528b6b..0751b88752d 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,6 @@ from odoo import models, fields + class EstateProperty(models.Model): _name = "estate.property" _description = "Real Estate properties" @@ -8,7 +9,7 @@ class EstateProperty(models.Model): description = fields.Text() postcode = fields.Char() date_availability = fields.Date() - expected_price = fields.Float(reuired=True) + expected_price = fields.Float(required=True) selling_price = fields.Float() bedrooms = fields.Integer() living_area = fields.Integer() @@ -17,5 +18,5 @@ class EstateProperty(models.Model): garden = fields.Boolean() garden_area = fields.Boolean() garden_orientation = fields.Selection( - selection = [('north', 'North'), ('south', 'South'), ('east', 'East')], + selection=[('north', 'North'), ('south', 'South'), ('east', 'East')], ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..62797f8607d --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_estate_model,access_estate_property,model_estate_property,base.group_user,1,0,0,0 From 58b0e7c581f13177c4a19345f9c82a7636ebbbbd Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Mon, 6 Jul 2026 17:58:01 +0530 Subject: [PATCH 05/18] [IMP] estate: add basic UI Add the basic user interface for managing real-estate properties. Provide users with a dedicated Properties menu and the necessary views to access and manage property records from the Odoo interface. Complete the initial property configuration with the information needed to properly manage property availability and status throughout its lifecycle. --- .vscode/settings.json | 3 +++ estate/__manifest__.py | 2 ++ estate/models/estate_property.py | 23 +++++++++++++++++++---- estate/security/ir.model.access.csv | 2 +- estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 6 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..ff5300ef481 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.languageServer": "None" +} \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 06620ffe95b..6c20cd6cf70 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -9,5 +9,7 @@ "installable": True, "data": [ "security/ir.model.access.csv", + "views/estate_menus.xml", + "views/estate_property_views.xml", ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0751b88752d..d9b40e46d2d 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,22 +1,37 @@ from odoo import models, fields +from odoo.tools.rendering_tools import relativedelta_proxy class EstateProperty(models.Model): _name = "estate.property" _description = "Real Estate properties" - name = fields.Char(required=True) + name = fields.Char(required=True, default="Unkown") + last_seen = fields.Datetime("Last Seen", default=fields.Datetime.now) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date( + default=lambda self: fields.Date.today() + relativedelta_proxy(months=3) + ) expected_price = fields.Float(required=True) - selling_price = fields.Float() - bedrooms = fields.Integer() + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() + active = fields.Boolean(default=True) garden_area = fields.Boolean() garden_orientation = fields.Selection( selection=[('north', 'North'), ('south', 'South'), ('east', 'East')], ) + state = fields.Selection( + [ + ("new", "New"), + ("offer_received", "Offer Received"), + ("offer_accepted", "Offer Accepted"), + ("sold", "Sold"), + ("cancelled", "Cancelled"), + ], + default="new", + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 62797f8607d..3dc956424fe 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,2 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_estate_model,access_estate_property,model_estate_property,base.group_user,1,0,0,0 +access_estate_model,access_estate_property,model_estate_property,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..ab2171b5e30 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..76d784804cb --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Properties + estate.property + list,form + + From 0d9c3a1d45e6ec38e1e897bbd0beba606a145d04 Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Tue, 7 Jul 2026 18:07:25 +0530 Subject: [PATCH 06/18] [IMP] estate: add basic views like custom form, list, search filters & group by Improve the property management interface by providing dedicated views for displaying and editing real-estate properties. Add search options to help users quickly find available properties and group them by postcode, making it easier to organize and navigate property records. Also improve the default values and constraints of property information to provide more consistent and useful records. --- estate/__manifest__.py | 2 +- estate/models/estate_property.py | 14 ++-- estate/views/estate_property_views.xml | 91 +++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 11 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 6c20cd6cf70..c14d2eb631d 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -9,7 +9,7 @@ "installable": True, "data": [ "security/ir.model.access.csv", - "views/estate_menus.xml", "views/estate_property_views.xml", + "views/estate_menus.xml", ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d9b40e46d2d..f8cbd901379 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -6,17 +6,17 @@ class EstateProperty(models.Model): _name = "estate.property" _description = "Real Estate properties" - name = fields.Char(required=True, default="Unkown") + title = fields.Char(required=True, default="Unknown") last_seen = fields.Datetime("Last Seen", default=fields.Datetime.now) description = fields.Text() - postcode = fields.Char() - date_availability = fields.Date( + postcode = fields.Char("Postcode") + date_availability = fields.Date("Available From", default=lambda self: fields.Date.today() + relativedelta_proxy(months=3) ) - expected_price = fields.Float(required=True) - selling_price = fields.Float(readonly=True, copy=False) - bedrooms = fields.Integer(default=2) - living_area = fields.Integer() + expected_price = fields.Float("Expected Price", required=True) + selling_price = fields.Float("Selling Price", readonly=True, copy=False) + bedrooms = fields.Integer("Bedrooms", default=2) + living_area = fields.Integer("Living Area (sqm)") facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 76d784804cb..ec51ffc6baf 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,8 +1,93 @@ - Properties - estate.property - list,form + Properties + estate.property + list,form + + + + estate.property.list + estate.property + + + + + + + + + + + + + + + estate.property_form + estate.property + +
+ + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + + + +
From fd8b0611b0b58c4a73c41c05957c9ea8a903d4fc Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Wed, 8 Jul 2026 18:18:28 +0530 Subject: [PATCH 07/18] [FIX] estate: fix property state and menu configuration Fix the property state configuration to ensure that every property has a valid status and starts in the New state when created. Also fix misalgined indentation in real estate menu configuration. --- estate/models/estate_property.py | 4 +++- estate/views/estate_menus.xml | 2 +- estate/views/estate_property_views.xml | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index f8cbd901379..c3c161be5ac 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -21,7 +21,7 @@ class EstateProperty(models.Model): garage = fields.Boolean() garden = fields.Boolean() active = fields.Boolean(default=True) - garden_area = fields.Boolean() + garden_area = fields.Integer("Garden Area (sqm)") garden_orientation = fields.Selection( selection=[('north', 'North'), ('south', 'South'), ('east', 'East')], ) @@ -33,5 +33,7 @@ class EstateProperty(models.Model): ("sold", "Sold"), ("cancelled", "Cancelled"), ], + required=True, + copy=False, default="new", ) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index ab2171b5e30..d679db23a61 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,6 +1,6 @@ - + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index ec51ffc6baf..8df57f75288 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,9 +1,9 @@ - Properties - estate.property - list,form + Properties + estate.property + list,form From e49442a236aca9b15cb73116dc9ab20f5a877e42 Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 10 Jul 2026 18:53:55 +0530 Subject: [PATCH 08/18] [ADD] estate: add estate_property_type model Add property type model to allow properties to be categorized and managed according to their type. Add buyer and seller information to property records so that the people involved in a property transaction can be tracked directly from the property. Provide a dedicated Settings menu to manage property types separately from the properties themselves. --- estate/models/__init__.py | 1 + estate/models/estate_property.py | 4 ++++ estate/models/estate_property_type.py | 8 ++++++++ estate/security/ir.model.access.csv | 1 + estate/views/estate_menus.xml | 5 ++++- estate/views/estate_property_views.xml | 12 ++++++++++++ 6 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 estate/models/estate_property_type.py diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..40092a2d810 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,2 @@ from . import estate_property +from . import estate_property_type diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c3c161be5ac..850b9ed586b 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -37,3 +37,7 @@ class EstateProperty(models.Model): copy=False, default="new", ) + + property_type_id = fields.Many2one("estate.property.types", string="Property Type") + buyer_id = fields.Many2one("res.partner", string="Buyer") + seller_id = fields.Many2one("res.partner", string="Seller") diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..133fb8a7435 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class EstatePropertyType(models.Model): + _name = "estate.property.types" + _description = "Estate Property Types" + + name = fields.Char("Property Type", required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 3dc956424fe..53e32619649 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_estate_model,access_estate_property,model_estate_property,base.group_user,1,1,1,1 +estate.access_estate_property_types,access_estate_property_types,estate.model_estate_property_types,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index d679db23a61..ce191009cc5 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -4,5 +4,8 @@ + + + - \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 8df57f75288..a7e1b571151 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,6 +6,12 @@ list,form + + Property Types + estate.property.types + list,form + + estate.property.list estate.property @@ -55,6 +61,12 @@ + + + + + + From b6cddcdd1e9e9903432bf128bf285762c11d4e5e Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Mon, 13 Jul 2026 18:57:19 +0530 Subject: [PATCH 09/18] [ADD] estate: add estate_property_tag model Add property tags model to make it easier to categorize and identify properties based on their characteristics. Allow properties to have multiple tags and provide a dedicated way to manage these tags, while ensuring that transaction-related information is handled appropriately when properties are duplicated. --- estate/models/__init__.py | 1 + estate/models/estate_property.py | 5 +++-- estate/models/estate_property_tag.py | 8 ++++++++ estate/security/ir.model.access.csv | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 estate/models/estate_property_tag.py diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 40092a2d810..c620ac481a3 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,2 +1,3 @@ from . import estate_property from . import estate_property_type +from . import estate_property_tag diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 850b9ed586b..8079bb15bdf 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -39,5 +39,6 @@ class EstateProperty(models.Model): ) property_type_id = fields.Many2one("estate.property.types", string="Property Type") - buyer_id = fields.Many2one("res.partner", string="Buyer") - seller_id = fields.Many2one("res.partner", string="Seller") + buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) + seller_id = fields.Many2one("res.users", string="Seller", default=lambda self: self.env.user) + tags_id = fields.Many2many("estate.property.tag", string="Tags") diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..626d957db5f --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Estate Property Tags" + + name = fields.Char("Name", required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 53e32619649..a71d379b82b 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,3 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_estate_model,access_estate_property,model_estate_property,base.group_user,1,1,1,1 estate.access_estate_property_types,access_estate_property_types,estate.model_estate_property_types,base.group_user,1,1,1,1 +estate.access_estate_property_tag, access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 + From ff4b4eea2beba0ab52da28f3213252d0b0b9d0bc Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Tue, 14 Jul 2026 18:46:45 +0530 Subject: [PATCH 10/18] [ADD] estate: add estate_property_offer model and split view files into smaller view files Allow users to manage offers received for real-estate properties. Add the ability to record the offered price, the interested buyer, and the current status of each offer, making it easier to follow the offers received for a property. Provide dedicated views to review and manage offers while displaying the relevant offer information directly from the property interface. --- .vscode/settings.json | 3 -- estate/__manifest__.py | 2 ++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 2 +- estate/models/estate_property_offer.py | 11 +++++++ estate/security/ir.model.access.csv | 1 + estate/views/estate_property_offer.xml | 30 +++++++++++++++++++ estate/views/estate_property_search.xml | 32 +++++++++++++++++++++ estate/views/estate_property_views.xml | 38 +++++-------------------- 9 files changed, 85 insertions(+), 35 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/views/estate_property_offer.xml create mode 100644 estate/views/estate_property_search.xml diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index ff5300ef481..00000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "python.languageServer": "None" -} \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py index c14d2eb631d..6c4f73d8fa0 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -9,6 +9,8 @@ "installable": True, "data": [ "security/ir.model.access.csv", + "views/estate_property_offer.xml", + "views/estate_property_search.xml", "views/estate_property_views.xml", "views/estate_menus.xml", ] diff --git a/estate/models/__init__.py b/estate/models/__init__.py index c620ac481a3..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,3 +1,4 @@ from . import estate_property from . import estate_property_type from . import estate_property_tag +from . import estate_property_offer diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 8079bb15bdf..54a7c435ab0 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -37,8 +37,8 @@ class EstateProperty(models.Model): copy=False, default="new", ) - property_type_id = fields.Many2one("estate.property.types", string="Property Type") buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) seller_id = fields.Many2one("res.users", string="Seller", default=lambda self: self.env.user) tags_id = fields.Many2many("estate.property.tag", string="Tags") + offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..fb65d72c91d --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,11 @@ +from odoo import models, fields + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Estate Property Offer" + + price = fields.Integer() + status = fields.Selection([("accepted", "Accepted"), ("refused", "Refused")], copy=False) + partner_id = fields.Many2one("res.partner", required=True) + property_id = fields.Many2one("estate.property", required=True, readonly=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index a71d379b82b..3731b02ae1a 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -2,4 +2,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_estate_model,access_estate_property,model_estate_property,base.group_user,1,1,1,1 estate.access_estate_property_types,access_estate_property_types,estate.model_estate_property_types,base.group_user,1,1,1,1 estate.access_estate_property_tag, access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 +estate.access_estate_property_offer, access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_property_offer.xml b/estate/views/estate_property_offer.xml new file mode 100644 index 00000000000..6e407f6d218 --- /dev/null +++ b/estate/views/estate_property_offer.xml @@ -0,0 +1,30 @@ + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + + estate.property.offer.form + estate.property.offer + +
+ + + + + + + +
+
+
+
diff --git a/estate/views/estate_property_search.xml b/estate/views/estate_property_search.xml new file mode 100644 index 00000000000..103aa11deed --- /dev/null +++ b/estate/views/estate_property_search.xml @@ -0,0 +1,32 @@ + + + + estate.property.search + estate.property + + + + + + + + + + + + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index a7e1b571151..cbd15ef96ee 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -32,11 +32,14 @@ estate.property_form estate.property -
+

+ + + @@ -61,6 +64,9 @@ + + + @@ -72,34 +78,4 @@
- - - estate.property.search - estate.property - - - - - - - - - - - - - - - - - From 80c719f08cf0a539725b95ae0d2c5778153566ed Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 24 Jul 2026 17:26:04 +0530 Subject: [PATCH 11/18] [IMP] estate: replace relativedelta_proxy() with add() for date_availability Replace old custom logic with Odoo's built-in util methods to calculate property's date availability more reliably. --- estate/models/estate_property.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 54a7c435ab0..7021c6583ea 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,4 @@ from odoo import models, fields -from odoo.tools.rendering_tools import relativedelta_proxy class EstateProperty(models.Model): @@ -11,7 +10,7 @@ class EstateProperty(models.Model): description = fields.Text() postcode = fields.Char("Postcode") date_availability = fields.Date("Available From", - default=lambda self: fields.Date.today() + relativedelta_proxy(months=3) + default=lambda self: fields.Date.add(fields.Date.today(), months=3) ) expected_price = fields.Float("Expected Price", required=True) selling_price = fields.Float("Selling Price", readonly=True, copy=False) From fd94fa5652fb68695d76102f20f8cd412885c2a9 Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 24 Jul 2026 19:03:20 +0530 Subject: [PATCH 12/18] [IMP] estate: add total area field in property form Add Total Area field which computes the total area of the property from Living Area and Garden Area to showcase the actual area of the property. --- estate/models/estate_property.py | 11 ++++++++++- estate/views/estate_property_views.xml | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 7021c6583ea..41448861399 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api class EstateProperty(models.Model): @@ -41,3 +41,12 @@ class EstateProperty(models.Model): seller_id = fields.Many2one("res.users", string="Seller", default=lambda self: self.env.user) tags_id = fields.Many2many("estate.property.tag", string="Tags") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + total_area = fields.Integer( + "Total Area (sqm)", + compute="_compute_total_area", + ) + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index cbd15ef96ee..39d7283403b 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -56,12 +56,13 @@ + - + From 531dd944318cd15681d6e2ffe20fc9a15c651090 Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Mon, 27 Jul 2026 15:02:36 +0530 Subject: [PATCH 13/18] [IMP] estate: add best price to estate property Display the highest offer received for a property directly on its property form. Best price helps real estate agents or property owners to find the highest price offered to the property quickly and helps them to make the best deal they can. --- estate/models/estate_property.py | 12 ++++++++++++ estate/views/estate_property_views.xml | 1 + 2 files changed, 13 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 41448861399..7b644e7e187 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -45,8 +45,20 @@ class EstateProperty(models.Model): "Total Area (sqm)", compute="_compute_total_area", ) + best_price = fields.Integer( + "Best Price", + compute="_compute_best_price" + ) @api.depends("living_area", "garden_area") def _compute_total_area(self): for record in self: record.total_area = record.living_area + record.garden_area + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for record in self: + record.best_price = max( + record.offer_ids.mapped("price"), + default = 0 + ) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 39d7283403b..20076a75f85 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -48,6 +48,7 @@ + From 7a3f91e2a37a63eeff875026b3d34d8b611323d0 Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Wed, 29 Jul 2026 17:41:06 +0530 Subject: [PATCH 14/18] [FIX] estate: add menuitem for property tag and improve record ids A separate view for property tag was missing, added a new menuitem for that which allows user to list all tags and directly create them. --- estate/models/estate_property.py | 2 +- estate/views/estate_menus.xml | 3 ++- estate/views/estate_property_views.xml | 9 ++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 7b644e7e187..427a692c278 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -60,5 +60,5 @@ def _compute_best_price(self): for record in self: record.best_price = max( record.offer_ids.mapped("price"), - default = 0 + default=0 ) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index ce191009cc5..377a86366a1 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -5,7 +5,8 @@ - + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 20076a75f85..ba77eba6529 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,12 +6,18 @@ list,form - + Property Types estate.property.types list,form + + Property Tags + estate.property.tag + list,form + + estate.property.list estate.property @@ -24,6 +30,7 @@ + From 90439ebb9eb9bd0b0504fbf7dc6c182941ccad61 Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Thu, 30 Jul 2026 17:41:24 +0530 Subject: [PATCH 15/18] [IMP] estate: track offer validity deadline Allow offers to have a validity period so that each offer clearly indicates how long it remains valid. This makes it easier to determine whether an offer is still actionable and gives users a direct way to adjust its deadline when needed. Compute the deadline from the offer validity and keep the validity synchronized when the deadline is changed manually. --- estate/models/estate_property_offer.py | 16 +++++++++++++++- estate/views/estate_property_offer.xml | 4 ++++ estate/views/estate_property_views.xml | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index fb65d72c91d..ee701e7aa17 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api class EstatePropertyOffer(models.Model): @@ -9,3 +9,17 @@ class EstatePropertyOffer(models.Model): status = fields.Selection([("accepted", "Accepted"), ("refused", "Refused")], copy=False) partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True, readonly=True) + validity = fields.Integer("Validity", default=7) + date_deadline = fields.Date("Deadline", compute="_compute_deadline", inverse="_inverse_deadline") + + @api.depends("create_date", "validity") + def _compute_deadline(self): + for record in self: + record.date_deadline = fields.Date.add( + fields.Date.today(), + days=record.validity, + ) + + def _inverse_deadline(self): + for record in self: + record.validity = (record.date_deadline - fields.Date.today()).days diff --git a/estate/views/estate_property_offer.xml b/estate/views/estate_property_offer.xml index 6e407f6d218..7058829797a 100644 --- a/estate/views/estate_property_offer.xml +++ b/estate/views/estate_property_offer.xml @@ -8,6 +8,8 @@ + + @@ -22,6 +24,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index ba77eba6529..ef769397987 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -36,7 +36,7 @@ - estate.property_form + estate.property.form estate.property
From e4166cb6366c445e104875a1a435d92e2b664ffb Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 31 Jul 2026 14:19:19 +0530 Subject: [PATCH 16/18] [IMP] estate: simplify garden information entry Simplify property data entry by automatically providing sensible garden information when a garden is present and clearing garden-specific information when it is not. Allow users to adjust the suggested garden area according to the actual property while keeping the property's total area consistent with the entered garden area. --- estate/models/estate_property.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 427a692c278..d0057cd9ce8 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -62,3 +62,12 @@ def _compute_best_price(self): record.offer_ids.mapped("price"), default=0 ) + + @api.onchange("garden") + def _onchange_garden(self): + if self.garden: + self.garden_orientation = "north" + self.garden_area = 10 + else: + self.garden_orientation = "" + self.garden_area = 0 From 34aef43c7a7b14ae097152dca5286074f7be4ccb Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 31 Jul 2026 18:05:40 +0530 Subject: [PATCH 17/18] [IMP] estate: allow properties to be sold or cancelled Allow users to mark properties as sold or cancelled as they progress through their lifecycle. Prevent contradictory actions by ensuring that cancelled properties cannot be sold and sold properties cannot be cancelled. --- estate/models/estate_property.py | 17 ++++++++++++++++- estate/views/estate_property_views.xml | 7 +++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d0057cd9ce8..031cb821231 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import models, fields, api +from odoo import models, fields, api, exceptions class EstateProperty(models.Model): @@ -9,6 +9,7 @@ class EstateProperty(models.Model): last_seen = fields.Datetime("Last Seen", default=fields.Datetime.now) description = fields.Text() postcode = fields.Char("Postcode") + status = fields.Char("Status", readonly=True, default="New") date_availability = fields.Date("Available From", default=lambda self: fields.Date.add(fields.Date.today(), months=3) ) @@ -71,3 +72,17 @@ def _onchange_garden(self): else: self.garden_orientation = "" self.garden_area = 0 + + def action_sell_property(self): + for record in self: + if record.status == "Cancelled": + raise exceptions.UserError("Cancelled properties cannot be sold") + record.status = "Sold" + return True + + def action_cancel_property(self): + for record in self: + if record.status == "Sold": + raise exceptions.UserError("Sold properties cannot be cancelled") + record.status = "Cancelled" + return True diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index ef769397987..242c60194d3 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -40,10 +40,17 @@ estate.property +
+ + +

+ + + From fbe46cce366387f94407aad9ba4841e7f3b646ce Mon Sep 17 00:00:00 2001 From: Mauryan Kansara Date: Fri, 7 Aug 2026 17:41:10 +0530 Subject: [PATCH 18/18] [IMP] estate: add missing property type in UI add property type field in UI so user can select the type of the property. --- estate/views/estate_property_views.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 242c60194d3..a3b06d72f2c 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -52,6 +52,7 @@ +