From 1e08d465fe42fa68a840757c9822df62f7609267 Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Thu, 5 Feb 2026 17:18:07 +0530 Subject: [PATCH 01/17] [ADD] added estate property model with basic fields using Odoo ORM --- estate/__init__.py | 1 + estate/__manifest__.py | 5 +++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py 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 new file mode 100644 index 00000000000..9a7e03eded3 --- /dev/null +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..ca06343b61e --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,5 @@ +{ + 'name' : "Real Estate", + 'depends' : ['base'], + 'application' : True, +} \ No newline at end of file diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..f4c8fd6db6d --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..cae45c023b1 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,28 @@ +from odoo import models, fields +class EstateProperty(models.Model): + _name : "estate.property" + _description : "real estate property" + + name = fields.Char() + description = fields.Text(required = True) + postcode = fields.Char() + date_availability = fields.Date() + expected_price = fields.Float() + selling_price = fields.Float() + bedrooms = fields.Integer() + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection([ + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West'), + + ] + + + + ) From 60035e93507d39f2a0eea3afab20e75dd2bb2018 Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Thu, 5 Feb 2026 18:57:02 +0530 Subject: [PATCH 02/17] [CLN] estate: align code with coding guidelines --- estate/__manifest__.py | 11 +++++++---- estate/models/estate_property.py | 27 +++++++++++++-------------- estate/security/ir.model.access.csv | 2 ++ 3 files changed, 22 insertions(+), 18 deletions(-) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index ca06343b61e..2ff90c381c8 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,5 +1,8 @@ { - 'name' : "Real Estate", - 'depends' : ['base'], - 'application' : True, -} \ No newline at end of file + "name": "Real Estate", + "depends": ["base"], + "application": True, + "data": [ + "security/ir.model.access.csv", + ], +} diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index cae45c023b1..8cb01c39f2f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,10 +1,12 @@ -from odoo import models, fields +from odoo import fields, models + + class EstateProperty(models.Model): - _name : "estate.property" - _description : "real estate property" + _name = "estate.property" + _description = "real estate property" name = fields.Char() - description = fields.Text(required = True) + description = fields.Text(required=True) postcode = fields.Char() date_availability = fields.Date() expected_price = fields.Float() @@ -15,14 +17,11 @@ class EstateProperty(models.Model): garage = fields.Boolean() garden = fields.Boolean() garden_area = fields.Integer() - garden_orientation = fields.Selection([ - ('north', 'North'), - ('south', 'South'), - ('east', 'East'), - ('west', 'West'), - - ] - - - + garden_orientation = fields.Selection( + [ + ("north", "North"), + ("south", "South"), + ("east", "East"), + ("west", "West"), + ] ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..0e11f47e58d --- /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_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file From 45334f0158d2bf6d6a7f06866af86cfc792845b2 Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Fri, 6 Feb 2026 10:31:10 +0530 Subject: [PATCH 03/17] [CLN] estate: code cleanup --- estate/__manifest__.py | 4 +--- estate/models/estate_property.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 2ff90c381c8..0712ba5d455 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,7 +2,5 @@ "name": "Real Estate", "depends": ["base"], "application": True, - "data": [ - "security/ir.model.access.csv", - ], + "data": ["security/ir.model.access.csv"], } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 8cb01c39f2f..1adf883413c 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -24,4 +24,4 @@ class EstateProperty(models.Model): ("east", "East"), ("west", "West"), ] - ) + ) \ No newline at end of file From a3de2f7c6323279ceafa91345f29d1a55a53237f Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Fri, 6 Feb 2026 10:40:26 +0530 Subject: [PATCH 04/17] [FIX] estate: add missing author and license to manifest --- estate/__manifest__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 0712ba5d455..c166177f1bf 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,6 +1,13 @@ { "name": "Real Estate", + "version": "1.0", + "category": "Tutorial", + "summary": "Manage real estate properties", + "author": "sasri-odoo", + "license": "LGPL-3", "depends": ["base"], + "data": [ + "security/ir.model.access.csv", + ], "application": True, - "data": ["security/ir.model.access.csv"], } From fdc53764b95f41996ca0ae9c2ff47dbd6a9fed94 Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Fri, 6 Feb 2026 18:18:09 +0530 Subject: [PATCH 05/17] [IMP] estate: add menus, actions, and custom list/form/search views --- estate/__manifest__.py | 6 +- estate/models/estate_property.py | 26 +++++-- estate/views/estate_menus.xml | 12 ++++ estate/views/estate_property_views.xml | 94 ++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index c166177f1bf..068e8bf56ba 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,9 +5,13 @@ "summary": "Manage real estate properties", "author": "sasri-odoo", "license": "LGPL-3", - "depends": ["base"], + "depends": [ + "base", + ], "data": [ "security/ir.model.access.csv", + "views/estate_property_views.xml", + "views/estate_menus.xml", ], "application": True, } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 1adf883413c..42d0b850214 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,3 +1,5 @@ +from datetime import timedelta + from odoo import fields, models @@ -8,10 +10,13 @@ class EstateProperty(models.Model): name = fields.Char() description = fields.Text(required=True) postcode = fields.Char() - date_availability = fields.Date() - expected_price = fields.Float() + date_availability = fields.Date( + default=lambda self: fields.Date.today() + timedelta(days=90), + copy=False, + ) + expected_price = fields.Float(readonly=True, copy=False) selling_price = fields.Float() - bedrooms = fields.Integer() + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() @@ -24,4 +29,17 @@ class EstateProperty(models.Model): ("east", "East"), ("west", "West"), ] - ) \ No newline at end of file + ) + active = fields.Boolean(default=True) + state = fields.Selection( + [ + ("new", "New"), + ("offer_received", "Offer receieved"), + ("offer accepted", "offer accepted"), + ("sold", "sold"), + ("cancelled", "cancelled"), + ], + default="new", + required=True, + copy=False, + ) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..6e14c5d9cc6 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..4156efd250e --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,94 @@ + + + + + + Properties + estate.property + list,form + + + + estate.property.list + estate.property + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + + +
From 331964cee5424ba45fea39bf7f93ec666388055f Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Mon, 9 Feb 2026 18:59:09 +0530 Subject: [PATCH 06/17] [IMP] estate: add many2one relations for property type, buyer, and salesperson --- estate/__manifest__.py | 1 + estate/models/__init__.py | 3 +- estate/models/estate_property.py | 19 +++++++++++- estate/models/estate_property_type.py | 7 +++++ estate/security/ir.model.access.csv | 3 +- estate/views/estate_menus.xml | 7 +++++ estate/views/estate_property_type_views.xml | 34 +++++++++++++++++++++ estate/views/estate_property_views.xml | 9 ++++++ 8 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 068e8bf56ba..21be5aea326 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -11,6 +11,7 @@ "data": [ "security/ir.model.access.csv", "views/estate_property_views.xml", + "views/estate_property_type_views.xml", "views/estate_menus.xml", ], "application": True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..97aee757823 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,2 @@ -from . import estate_property \ No newline at end of file +from . import estate_property +from . import estate_property_type \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 42d0b850214..ef2ae54d614 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -35,7 +35,7 @@ class EstateProperty(models.Model): [ ("new", "New"), ("offer_received", "Offer receieved"), - ("offer accepted", "offer accepted"), + ("offer_accepted", "offer accepted"), ("sold", "sold"), ("cancelled", "cancelled"), ], @@ -43,3 +43,20 @@ class EstateProperty(models.Model): required=True, copy=False, ) + property_type_id = fields.Many2one( + "estate.property.type", + string="Property Type" + ) + + buyer_id = fields.Many2one( + "res.partner", + string="Buyer", + copy=False + ) + + user_id = fields.Many2one( + "res.users", + string="Salesperson", + default=lambda self: self.env.user + ) + diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..9f259fca452 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,7 @@ +from odoo import models, fields + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Property Type" + + name = fields.Char(required=True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 0e11f47e58d..d189a0e2357 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_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 +acees_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 6e14c5d9cc6..ca01310d139 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -8,5 +8,12 @@ action="estate_property_action" /> + + + + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..2fa3ddbf849 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,34 @@ + + + + + Property Types + estate.property.type + list,form + + + + estate.property.type.list + estate.property.type + + + + + + + + + estate.property.type.form + estate.property.type + +
+ + + + + +
+
+
+ +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 4156efd250e..2c5e24827a4 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -15,6 +15,7 @@ + @@ -36,6 +37,7 @@ + @@ -53,6 +55,12 @@ + + + + + + @@ -67,6 +75,7 @@ + From 894cde3bc572b8944c93c27381d0afffc187a244 Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Tue, 10 Feb 2026 18:45:49 +0530 Subject: [PATCH 07/17] [IMP] estate: CH-7 added model relations using many2one, many2many, and one2many --- estate/__manifest__.py | 2 ++ estate/models/__init__.py | 4 ++- estate/models/estate_property.py | 11 +++++++ estate/models/estate_property_offer.py | 27 +++++++++++++++++ estate/models/estate_property_tag.py | 7 +++++ estate/security/ir.model.access.csv | 4 ++- estate/views/estate_menus.xml | 6 ++++ estate/views/estate_property_offer_views.xml | 32 ++++++++++++++++++++ estate/views/estate_property_tag_views.xml | 20 ++++++++++++ estate/views/estate_property_views.xml | 11 +++++++ 10 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/views/estate_property_offer_views.xml create mode 100644 estate/views/estate_property_tag_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 21be5aea326..19bda03cbab 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -12,6 +12,8 @@ "security/ir.model.access.csv", "views/estate_property_views.xml", "views/estate_property_type_views.xml", + "views/estate_property_tag_views.xml", + "views/estate_property_offer_views.xml", "views/estate_menus.xml", ], "application": True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 97aee757823..09b2099fe84 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,2 +1,4 @@ from . import estate_property -from . import estate_property_type \ No newline at end of file +from . import estate_property_type +from . import estate_property_tag +from . import estate_property_offer \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index ef2ae54d614..df4ade2ede3 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -60,3 +60,14 @@ class EstateProperty(models.Model): default=lambda self: self.env.user ) + tag_ids = 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..cb5ee48b1e0 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,27 @@ +from odoo import models, fields + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Property Offer" + + price = fields.Float(required=True) + + status = fields.Selection( + [ + ('accpeted', 'Accepted'), + ('refused', 'Refused'), + ], + copy = False + ) + + partner_id = fields.Many2one( + "res.partner", + string="Buyer", + required=True + ) + + property_id = fields.Many2one( + "estate.property", + string="Property", + required=True + ) \ No newline at end of file diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..9f621f81956 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,7 @@ +from odoo import fields, models + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Property Tag" + + name = fields.Char(required = True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index d189a0e2357..05bd9eefba4 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_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 -acees_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index ca01310d139..29881fccca1 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -14,6 +14,12 @@ action="estate_property_type_action" /> + + + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..0b0041fb4ec --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,32 @@ + + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + + estate.property.offer.form + estate.property.offer + +
+ + + + + + + +
+
+
+ +
\ No newline at end of file diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..4a33acf1f59 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,20 @@ + + + + + Property tag + estate.property.tag + list,form + + + + estate.property.tag.list + estate.property.tag + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 2c5e24827a4..d0ce5d05e9c 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -16,6 +16,7 @@ + @@ -38,6 +39,7 @@ +
@@ -61,6 +63,15 @@ + + + + + + + + + From 2b960e9b7a40dcedeee0896fa09db92d89417b1d Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Tue, 10 Feb 2026 20:59:52 +0530 Subject: [PATCH 08/17] [LINT] Fix linting errors and formatting in estate property models --- estate/models/estate_property.py | 27 +++++++++++++++----------- estate/models/estate_property_offer.py | 19 +++++++++--------- estate/models/estate_property_tag.py | 3 ++- estate/models/estate_property_type.py | 5 +++-- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index df4ade2ede3..815ba868035 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -5,15 +5,17 @@ class EstateProperty(models.Model): _name = "estate.property" - _description = "real estate property" + _description = "Real Estate Property" name = fields.Char() description = fields.Text(required=True) postcode = fields.Char() + date_availability = fields.Date( default=lambda self: fields.Date.today() + timedelta(days=90), copy=False, ) + expected_price = fields.Float(readonly=True, copy=False) selling_price = fields.Float() bedrooms = fields.Integer(default=2) @@ -22,6 +24,7 @@ class EstateProperty(models.Model): garage = fields.Boolean() garden = fields.Boolean() garden_area = fields.Integer() + garden_orientation = fields.Selection( [ ("north", "North"), @@ -30,44 +33,46 @@ class EstateProperty(models.Model): ("west", "West"), ] ) + active = fields.Boolean(default=True) + state = fields.Selection( [ ("new", "New"), - ("offer_received", "Offer receieved"), - ("offer_accepted", "offer accepted"), - ("sold", "sold"), - ("cancelled", "cancelled"), + ("offer_received", "Offer Received"), + ("offer_accepted", "Offer Accepted"), + ("sold", "Sold"), + ("cancelled", "Cancelled"), ], default="new", required=True, copy=False, ) + property_type_id = fields.Many2one( "estate.property.type", - string="Property Type" + string="Property Type", ) buyer_id = fields.Many2one( "res.partner", string="Buyer", - copy=False + copy=False, ) user_id = fields.Many2one( "res.users", string="Salesperson", - default=lambda self: self.env.user + default=lambda self: self.env.user, ) tag_ids = fields.Many2many( "estate.property.tag", - string="Tags" + string="Tags", ) offer_ids = fields.One2many( "estate.property.offer", "property_id", - string = "Offers" + string="Offers", ) - diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index cb5ee48b1e0..b9139a7b597 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,5 @@ -from odoo import models, fields +from odoo import fields, models + class EstatePropertyOffer(models.Model): _name = "estate.property.offer" @@ -7,21 +8,21 @@ class EstatePropertyOffer(models.Model): price = fields.Float(required=True) status = fields.Selection( - [ - ('accpeted', 'Accepted'), - ('refused', 'Refused'), - ], - copy = False + [ + ("accepted", "Accepted"), + ("refused", "Refused"), + ], + copy=False, ) partner_id = fields.Many2one( "res.partner", string="Buyer", - required=True + required=True, ) property_id = fields.Many2one( "estate.property", string="Property", - required=True - ) \ No newline at end of file + required=True, + ) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 9f621f81956..9e477ca23b7 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -1,7 +1,8 @@ from odoo import fields, models + class EstatePropertyTag(models.Model): _name = "estate.property.tag" _description = "Property Tag" - name = fields.Char(required = True) \ No newline at end of file + name = fields.Char(required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 9f259fca452..ea6d79c3a10 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -1,7 +1,8 @@ -from odoo import models, fields +from odoo import fields, models + class EstatePropertyType(models.Model): _name = "estate.property.type" _description = "Property Type" - name = fields.Char(required=True) \ No newline at end of file + name = fields.Char(required=True) From 2fde2797977f2dc17c1e23e485f70e5acff97f7a Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Tue, 10 Feb 2026 21:09:53 +0530 Subject: [PATCH 09/17] [LINT] spacing mistake cleared --- estate/__init__.py | 2 +- estate/models/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/estate/__init__.py b/estate/__init__.py index 9a7e03eded3..0650744f6bc 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1 +1 @@ -from . import models \ No newline at end of file +from . import models diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 09b2099fe84..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,4 +1,4 @@ from . import estate_property from . import estate_property_type from . import estate_property_tag -from . import estate_property_offer \ No newline at end of file +from . import estate_property_offer From f244f8d3b367e58ca1608750a71e50d92f477f04 Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Wed, 11 Feb 2026 18:19:00 +0530 Subject: [PATCH 10/17] [LINT] estate: fixed spacing, quotes(referred docs), view naming --- estate/__manifest__.py | 4 +-- estate/models/estate_property.py | 28 +++++++------------- estate/models/estate_property_offer.py | 4 +-- estate/security/ir.model.access.csv | 2 +- estate/views/estate_property_offer_views.xml | 8 +++--- estate/views/estate_property_tag_views.xml | 4 +-- estate/views/estate_property_type_views.xml | 8 +++--- estate/views/estate_property_views.xml | 12 ++++----- 8 files changed, 29 insertions(+), 41 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 19bda03cbab..ca6f82e4eb4 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,9 +5,7 @@ "summary": "Manage real estate properties", "author": "sasri-odoo", "license": "LGPL-3", - "depends": [ - "base", - ], + "depends": [ "base" ], "data": [ "security/ir.model.access.csv", "views/estate_property_views.xml", diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 815ba868035..a0950dccac5 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -10,12 +10,10 @@ class EstateProperty(models.Model): name = fields.Char() description = fields.Text(required=True) postcode = fields.Char() - date_availability = fields.Date( default=lambda self: fields.Date.today() + timedelta(days=90), copy=False, ) - expected_price = fields.Float(readonly=True, copy=False) selling_price = fields.Float() bedrooms = fields.Integer(default=2) @@ -24,53 +22,45 @@ class EstateProperty(models.Model): garage = fields.Boolean() garden = fields.Boolean() garden_area = fields.Integer() - garden_orientation = fields.Selection( [ - ("north", "North"), - ("south", "South"), - ("east", "East"), - ("west", "West"), + ('north', "North"), + ('south', "South"), + ('east', "East"), + ('west', "West"), ] ) - active = fields.Boolean(default=True) - state = fields.Selection( [ - ("new", "New"), - ("offer_received", "Offer Received"), - ("offer_accepted", "Offer Accepted"), - ("sold", "Sold"), - ("cancelled", "Cancelled"), + ('new', "New"), + ('offer_received', "Offer Received"), + ('offer_accepted', "Offer Accepted"), + ('sold', "Sold"), + ('cancelled', "Cancelled"), ], default="new", required=True, copy=False, ) - property_type_id = fields.Many2one( "estate.property.type", string="Property Type", ) - buyer_id = fields.Many2one( "res.partner", string="Buyer", copy=False, ) - user_id = fields.Many2one( "res.users", string="Salesperson", default=lambda self: self.env.user, ) - tag_ids = fields.Many2many( "estate.property.tag", string="Tags", ) - offer_ids = fields.One2many( "estate.property.offer", "property_id", diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index b9139a7b597..e1c53792da4 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -9,8 +9,8 @@ class EstatePropertyOffer(models.Model): status = fields.Selection( [ - ("accepted", "Accepted"), - ("refused", "Refused"), + ('accepted', "Accepted"), + ('refused', "Refused"), ], copy=False, ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 05bd9eefba4..89f97c50842 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -2,4 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 -access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 0b0041fb4ec..111affb3690 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -1,8 +1,8 @@ - - estate.property.offer.list + + estate.property.offer.view.list estate.property.offer @@ -13,8 +13,8 @@ - - estate.property.offer.form + + estate.property.offer.view.form estate.property.offer
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index 4a33acf1f59..034b180894c 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -7,8 +7,8 @@ list,form - - estate.property.tag.list + + estate.property.tag.view.list estate.property.tag diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index 2fa3ddbf849..7f1806a7d1a 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -7,8 +7,8 @@ list,form - - estate.property.type.list + + estate.property.type.view.list estate.property.type @@ -17,8 +17,8 @@ - - estate.property.type.form + + estate.property.type.view.form estate.property.type diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index d0ce5d05e9c..7cc4bbdb140 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -8,8 +8,8 @@ list,form - - estate.property.list + + estate.property.view.list estate.property @@ -25,8 +25,8 @@ - - estate.property.form + + estate.property.view.form estate.property @@ -78,8 +78,8 @@ - - estate.property.search + + estate.property.view.search estate.property From 87514d8c9796710d1c7dc9311aee212e4801f40d Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Wed, 11 Feb 2026 18:54:50 +0530 Subject: [PATCH 11/17] [LINT] estate: fix End Of File newline in 2files and bracket spacing in manifest --- estate/__manifest__.py | 2 +- estate/models/estate_property_offer.py | 3 --- estate/views/estate_property_offer_views.xml | 2 +- estate/views/estate_property_tag_views.xml | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index ca6f82e4eb4..8b1b6b4c11c 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,7 +5,7 @@ "summary": "Manage real estate properties", "author": "sasri-odoo", "license": "LGPL-3", - "depends": [ "base" ], + "depends": ["base"], "data": [ "security/ir.model.access.csv", "views/estate_property_views.xml", diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index e1c53792da4..b766e1c5fb4 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -6,7 +6,6 @@ class EstatePropertyOffer(models.Model): _description = "Property Offer" price = fields.Float(required=True) - status = fields.Selection( [ ('accepted', "Accepted"), @@ -14,13 +13,11 @@ class EstatePropertyOffer(models.Model): ], copy=False, ) - partner_id = fields.Many2one( "res.partner", string="Buyer", required=True, ) - property_id = fields.Many2one( "estate.property", string="Property", diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 111affb3690..ad6379539b3 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -29,4 +29,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index 034b180894c..58ed7779ecd 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -17,4 +17,4 @@ - \ No newline at end of file + From 4e9557d80266f9beed3bae8964cc92ae2dda91ea Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Thu, 12 Feb 2026 18:36:40 +0530 Subject: [PATCH 12/17] [IMP] estate: implemented computed fields, inverse logic for offer deadline, and garden onchange --- estate/models/estate_property.py | 32 +++++++++++++++++++- estate/models/estate_property_offer.py | 25 ++++++++++++++- estate/views/estate_menus.xml | 6 ++++ estate/views/estate_property_offer_views.xml | 10 ++++++ estate/views/estate_property_views.xml | 4 +++ 5 files changed, 75 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a0950dccac5..858facd6cc3 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,6 @@ from datetime import timedelta -from odoo import fields, models +from odoo import api, fields, models class EstateProperty(models.Model): @@ -66,3 +66,33 @@ class EstateProperty(models.Model): "property_id", string="Offers", ) + total_area = fields.Float( + string="Total Area", + compute="_compute_total_area_" + ) + best_price = fields.Float( + string="Best Price", + compute="_computer_best_price" + ) + + @api.depends("living_area", "garden_area") + def _compute_total_area_(self): + for record in self: + record.total_area = (record.living_area or 0.0) + (record.garden_area or 0.0) + + @api.depends("offer_ids.price") + def _computer_best_price(self): + for record in self: + if record.offer_ids: + record.best_price = max(record.offer_ids.mapped("price")) + else: + record.best_price = 0.0 + + @api.onchange("garden") + def _onchange_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = "north" + else: + self.garden_area = 0 + self.garden_orientation = False diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index b766e1c5fb4..8de2080a456 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,6 @@ -from odoo import fields, models +from datetime import timedelta + +from odoo import api, fields, models class EstatePropertyOffer(models.Model): @@ -23,3 +25,24 @@ class EstatePropertyOffer(models.Model): string="Property", required=True, ) + validity = fields.Integer( + string="Validity (days)", + default=7 + ) + date_deadline = fields.Date( + string="Deadline", + compute="_compute_date_deadline", + inverse="_inverse_date_deadline" + ) + + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for record in self: + base_date = record.create_date.date() if record.create_date else fields.Date.today() + record.date_deadline = base_date + timedelta(days=record.validity) + + def _inverse_date_deadline(self): + for record in self: + if record.create_date and record.date_deadline: + delta = record.date_deadline - record.create_date.date() + record.validity = delta.days diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 29881fccca1..f860e967e20 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -20,6 +20,12 @@ action="estate_property_tag_action" /> + + + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index ad6379539b3..9c7ca1d2776 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -1,6 +1,12 @@ + + Offer + estate.property.offer + list,form + + estate.property.offer.view.list estate.property.offer @@ -9,6 +15,7 @@ + @@ -23,6 +30,9 @@ + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 7cc4bbdb140..c914d2b0cca 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -40,6 +40,8 @@ + + @@ -69,6 +71,8 @@ + +
From 675a77ac0d0a82820e05e4e417f1a3f0dd33337b Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Thu, 12 Feb 2026 19:05:16 +0530 Subject: [PATCH 13/17] [LINT] estate: Fixed spacing and naming issues --- estate/models/estate_property.py | 6 +++--- estate/models/estate_property_offer.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 858facd6cc3..011436e4991 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -76,10 +76,10 @@ class EstateProperty(models.Model): ) @api.depends("living_area", "garden_area") - def _compute_total_area_(self): + def _compute_total_area(self): for record in self: record.total_area = (record.living_area or 0.0) + (record.garden_area or 0.0) - + @api.depends("offer_ids.price") def _computer_best_price(self): for record in self: @@ -87,7 +87,7 @@ def _computer_best_price(self): record.best_price = max(record.offer_ids.mapped("price")) else: record.best_price = 0.0 - + @api.onchange("garden") def _onchange_garden(self): if self.garden: diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 8de2080a456..fe00729acc3 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -40,7 +40,7 @@ def _compute_date_deadline(self): for record in self: base_date = record.create_date.date() if record.create_date else fields.Date.today() record.date_deadline = base_date + timedelta(days=record.validity) - + def _inverse_date_deadline(self): for record in self: if record.create_date and record.date_deadline: From a8a0104c65948d079c0f129510ec6bd4563ba986 Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Fri, 13 Feb 2026 10:31:50 +0530 Subject: [PATCH 14/17] [LINT] estate: Fixed minor spacing issues and formatting issues --- estate/models/estate_property.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 011436e4991..c54196148ae 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -68,7 +68,7 @@ class EstateProperty(models.Model): ) total_area = fields.Float( string="Total Area", - compute="_compute_total_area_" + compute="_compute_total_area" ) best_price = fields.Float( string="Best Price", @@ -79,7 +79,7 @@ class EstateProperty(models.Model): def _compute_total_area(self): for record in self: record.total_area = (record.living_area or 0.0) + (record.garden_area or 0.0) - + @api.depends("offer_ids.price") def _computer_best_price(self): for record in self: From 003b7a382732bcb7d1634e4b043032676efc226c Mon Sep 17 00:00:00 2001 From: sasri-odoo Date: Fri, 13 Feb 2026 19:12:12 +0530 Subject: [PATCH 15/17] [IMP] estate: [CH-9] implemented cancel/sold actions and offer accept/refuse logic --- estate/models/estate_property.py | 20 ++++++++++++++++++++ estate/models/estate_property_offer.py | 26 ++++++++++++++++++++++++++ estate/views/estate_property_views.xml | 22 ++++++++++++++++++++-- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c54196148ae..66a4da19685 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -2,6 +2,8 @@ from odoo import api, fields, models +from odoo.exceptions import UserError + class EstateProperty(models.Model): _name = "estate.property" @@ -39,6 +41,7 @@ class EstateProperty(models.Model): ('sold', "Sold"), ('cancelled', "Cancelled"), ], + readonly=True, default="new", required=True, copy=False, @@ -96,3 +99,20 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = False + + def action_cancel(self): + for record in self: + if record.state == "sold": + raise UserError("A sold property cannot be cancelled") + record.state = "cancelled" + + def action_sold(self): + for record in self: + if record.state == "cancelled": + raise UserError("A cancelled property cannot be sold") + accepted = record.offer_ids.filtered( + lambda o: o.status == "accepted" + ) + if not accepted: + raise UserError("You must accept an offer before selling.") + record.state = "sold" diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index fe00729acc3..3dc2edc3506 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -2,6 +2,8 @@ from odoo import api, fields, models +from odoo.exceptions import UserError + class EstatePropertyOffer(models.Model): _name = "estate.property.offer" @@ -46,3 +48,27 @@ def _inverse_date_deadline(self): if record.create_date and record.date_deadline: delta = record.date_deadline - record.create_date.date() record.validity = delta.days + + def action_accept(self): + for record in self: + if record.property_id.state in ["cancelled", "sold"]: + raise UserError("Cannot accept an offer for a cancelled or sold property") + accepted_offers = record.property_id.offer_ids.filtered( + lambda o: o.status == "accepted" + ) + if accepted_offers: + raise UserError("an offer has already been accepted for this property") + record.status="accepted" + record.property_id.selling_price = record.price + record.property_id.buyer_id = record.partner_id + record.property_id.state = "offer_accepted" + + def action_refuse(self): + for record in self: + if record.property_id.state in ["cancelled", "sold"]: + raise UserError("Cannot modify offers for a sold or cancelled property") + if record.status == "accepted": + record.property_id.selling_price = 0.0 + record.property_id.buyer_id = False + record.property_id.state = "new" + record.status = "refused" diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c914d2b0cca..7a1957a1146 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -31,10 +31,18 @@
+