From 987e6c26aae16304bd630a51162d8bcf9fa624ab Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Wed, 31 Dec 2025 16:11:34 +0530 Subject: [PATCH 01/16] [ADD] estate: initial module setup and ORM configuration Add estate module Configure custom module structure in Odoo 19 Introduce addons structure and ORM models Define database tables using Odoo ORM CHAPTER 1,2,3 --- estate/__init__.py | 1 + estate/__manifest__.py | 8 ++++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 22 ++++++++++++++++++++++ 4 files changed, 32 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..5ab1b6a63ce --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,8 @@ +{ + 'name': "Real Estate", + 'category': 'Real Estate', + 'version': '1.0', + 'summary': 'Manage real estate properties', + '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..1b4e010c76a --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,22 @@ +from odoo import fields, models + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Estate Property Model" + + name = fields.Char(required=True) + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date() + expected_price = fields.Float(required=True) + 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')] + ) \ No newline at end of file From a94c3cd1feccf9881c54c65fd3acd0bba7aa7c46 Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Wed, 31 Dec 2025 18:34:49 +0530 Subject: [PATCH 02/16] [ADD] estate: add access rights for user groups Define model access rules for the estate module using ir.model.access.csv to control user permissions. CHAPTER 4 --- estate/__init__.py | 2 +- estate/__manifest__.py | 7 ++++++- estate/models/__init__.py | 2 +- estate/models/estate_property.py | 4 +++- estate/security/ir.model.access.csv | 2 ++ 5 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 estate/security/ir.model.access.csv 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/__manifest__.py b/estate/__manifest__.py index 5ab1b6a63ce..8773356642b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,7 +2,12 @@ 'name': "Real Estate", 'category': 'Real Estate', 'version': '1.0', + 'author': 'Radhey Detroja(RADET)', + 'license': 'LGPL-3', 'summary': 'Manage real estate properties', 'depends': ['base'], 'application': True, -} \ No newline at end of file + 'data': [ + 'security/ir.model.access.csv', + ] +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..5e1963c9d2f 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1 @@ -from . import estate_property \ No newline at end of file +from . import estate_property diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 1b4e010c76a..aad1b3ced8f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,6 @@ from odoo import fields, models + class EstateProperty(models.Model): _name = "estate.property" _description = "Estate Property Model" @@ -19,4 +20,5 @@ class EstateProperty(models.Model): garden_orientation = fields.Selection( [('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')] - ) \ No newline at end of file + ) + \ No newline at end of file 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 8a7b04cbe2848fe532a0d57906f539186a5e7962 Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Thu, 1 Jan 2026 16:12:53 +0530 Subject: [PATCH 03/16] [ADD] estate: add UI actions, menus and defaults Make estate.property accessible from the UI with proper defaults and field constraints. --- estate/__manifest__.py | 2 ++ estate/models/estate_property.py | 28 +++++++++++++++++++++++--- estate/views/estate_menus.xml | 12 +++++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 4 files changed, 47 insertions(+), 3 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 8773356642b..aaecc537d88 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -9,5 +9,7 @@ 'application': True, 'data': [ 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml', ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index aad1b3ced8f..0595f939630 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import fields, models +from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): @@ -8,10 +9,16 @@ class EstateProperty(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date( + copy=False, + default=lambda self: fields.Date.today() + relativedelta(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() @@ -21,4 +28,19 @@ class EstateProperty(models.Model): garden_orientation = fields.Selection( [('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')] ) + + state = fields.Selection( + [ + ('new', 'New'), + ('offer_received', 'Offer Received'), + ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled'), + ], + required=True, + copy=False, + default='new', + ) + + active = fields.Boolean() \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..3dfef0a4ddf --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ 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..3ee8842e984 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Real Estate + estate.property + list,form + + \ No newline at end of file From 3981aa4732fae41c2a1d8a352c1ef1f4a81f71bd Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Thu, 1 Jan 2026 18:11:36 +0530 Subject: [PATCH 04/16] [FIX] runbot: fix checkpoints error CHAPTER 5 --- estate/models/estate_property.py | 1 - 1 file changed, 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0595f939630..6955fb39a0b 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -43,4 +43,3 @@ class EstateProperty(models.Model): ) active = fields.Boolean() - \ No newline at end of file From 0bee4eae8584d06ee95e516ccd252f8ae90e3ff4 Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Fri, 2 Jan 2026 14:59:11 +0530 Subject: [PATCH 05/16] [IMP] estate: add custom list, form, and search views Improve estate.property usability by organizing fields and adding filters. CHAPTER 6 --- estate/__manifest__.py | 2 +- estate/models/estate_property.py | 4 +- estate/security/ir.model.access.csv | 2 +- estate/views/estate_menus.xml | 2 +- estate/views/estate_property_views.xml | 82 +++++++++++++++++++++++++- 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index aaecc537d88..f1a4d0deedb 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,5 +1,5 @@ { - 'name': "Real Estate", + 'name': 'Real Estate', 'category': 'Real Estate', 'version': '1.0', 'author': 'Radhey Detroja(RADET)', diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 6955fb39a0b..3da461a58ca 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,5 @@ -from odoo import fields, models from dateutil.relativedelta import relativedelta +from odoo import fields, models class EstateProperty(models.Model): @@ -42,4 +42,4 @@ class EstateProperty(models.Model): default='new', ) - active = fields.Boolean() + active = fields.Boolean(default=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 0e11f47e58d..32389642d4f 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_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 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 3dfef0a4ddf..6dca6c2d375 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -9,4 +9,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 3ee8842e984..8477a450c33 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,8 +1,88 @@ + + estate.property.list + estate.property + + + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + + Real Estate estate.property list,form -
\ No newline at end of file + From 464369eabe93e8f645846a511ea1c6481130a5b6 Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Mon, 5 Jan 2026 18:43:59 +0530 Subject: [PATCH 06/16] [IMP] Add many2one relation between two tables Improve data link between related models. CHAPTER 7 --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 5 ++- estate/models/estate_property_type.py | 8 ++++ estate/security/ir.model.access.csv | 1 + estate/views/estate_menus.xml | 3 ++ estate/views/estate_property_type_views.xml | 32 +++++++++++++++ estate/views/estate_property_views.xml | 45 ++++++++++++--------- 8 files changed, 75 insertions(+), 21 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 f1a4d0deedb..7269953a0a7 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -9,6 +9,7 @@ 'application': True, 'data': [ 'security/ir.model.access.csv', + 'views/estate_property_type_views.xml', 'views/estate_property_views.xml', 'views/estate_menus.xml', ] 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 3da461a58ca..dbd00be1a06 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -28,7 +28,6 @@ class EstateProperty(models.Model): garden_orientation = fields.Selection( [('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')] ) - state = fields.Selection( [ ('new', 'New'), @@ -41,5 +40,7 @@ class EstateProperty(models.Model): copy=False, default='new', ) - + property_type_id = fields.Many2one("estate.property.type") + buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) + salesperson_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user) active = fields.Boolean(default=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..0fcda47d95b --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class PropertyType(models.Model): + _name = "estate.property.type" + _description = "Defines types of property" + + name = fields.Char(string="Name", required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 32389642d4f..11da225066f 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 +access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 6dca6c2d375..8082d4f27df 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -6,6 +6,9 @@ + + + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..384607f07ca --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,32 @@ + + + + estate.property.type.list + estate.property.type + + + + + + + + + estate.property.type.form + estate.property.type + +
+ + + + + +
+
+
+ + + Property Types + estate.property.type + list,form + +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 8477a450c33..cbb199f4643 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,6 +6,7 @@ + @@ -27,34 +28,39 @@ - - - + - - - - - - - - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + @@ -66,6 +72,7 @@ + From 359d3d51f081c69f057f00e148be93870b1447ea Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Tue, 6 Jan 2026 14:13:42 +0530 Subject: [PATCH 07/16] [IMP] estate: add many2many and one2many relations to properties CHAPTER 7 --- estate/__manifest__.py | 2 ++ estate/models/__init__.py | 2 ++ estate/models/estate_property.py | 5 ++-- estate/models/estate_property_offer.py | 17 +++++++++++ estate/models/estate_property_tag.py | 8 ++++++ estate/security/ir.model.access.csv | 2 ++ estate/views/estate_menus.xml | 19 ++++++------- estate/views/estate_property_offer_views.xml | 30 ++++++++++++++++++++ estate/views/estate_property_tag_views.xml | 20 +++++++++++++ estate/views/estate_property_views.xml | 14 +++++++-- 10 files changed, 104 insertions(+), 15 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 7269953a0a7..599e950d4b3 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,7 +10,9 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', 'views/estate_property_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_menus.xml', ] } diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 40092a2d810..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,2 +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 dbd00be1a06..c9d9538d11f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -24,7 +24,7 @@ class EstateProperty(models.Model): garage = fields.Boolean() garden = fields.Boolean() garden_area = fields.Integer() - + active = fields.Boolean(default=True) garden_orientation = fields.Selection( [('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')] ) @@ -43,4 +43,5 @@ class EstateProperty(models.Model): property_type_id = fields.Many2one("estate.property.type") buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) salesperson_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user) - active = fields.Boolean(default=True) + 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..63871439819 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,17 @@ +from odoo import fields, models + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Estate Property Offer Model" + + price = fields.Float() + 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) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..7e741da3dfd --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class PropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Property Tags" + + name = fields.Char(string="Name") diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 11da225066f..89f97c50842 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 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 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 8082d4f27df..9fa1f648406 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,15 +1,12 @@ - - - - - - - - - + + + - - + + + + + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..11a833cfd53 --- /dev/null +++ b/estate/views/estate_property_offer_views.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_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..fd502eddc49 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,20 @@ + + + + + Tag Name + estate.property.tag + list,form + + + + + estate.property.tag.list + estate.property.tag + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index cbb199f4643..f33bb3fad56 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -28,8 +28,9 @@ + - + @@ -51,9 +52,18 @@ - + + + + + + + + + + From 9d74e9c7a66f15d69fca2e0fa2563766d818c0e0 Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Wed, 7 Jan 2026 12:51:25 +0530 Subject: [PATCH 08/16] [IMP] estate: computed fields and onchanges - Add computed fields for total area and best offer to ensure data consistency - Implement computed validity date with inverse logic on property offers - Add onchange to assist garden-related form inputs without business logic CHAPTER - 8 --- estate/models/estate_property.py | 24 +++++++++++++++++++- estate/models/estate_property_offer.py | 22 +++++++++++++++++- estate/views/estate_property_offer_views.xml | 4 ++++ estate/views/estate_property_views.xml | 4 ++++ 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c9d9538d11f..00a2ef654d2 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,5 @@ from dateutil.relativedelta import relativedelta -from odoo import fields, models +from odoo import api, fields, models class EstateProperty(models.Model): @@ -18,6 +18,7 @@ class EstateProperty(models.Model): readonly=True, copy=False, ) + best_price = fields.Float(compute="_compute_best_price", string="Best Offer") bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() @@ -28,6 +29,7 @@ class EstateProperty(models.Model): garden_orientation = fields.Selection( [('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')] ) + total_area = fields.Integer(compute="_compute_total_area", string="Total Area (sqm)") state = fields.Selection( [ ('new', 'New'), @@ -45,3 +47,23 @@ class EstateProperty(models.Model): salesperson_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", string="Offers") + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = (record.living_area or 0) + (record.garden_area or 0) + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for record in self: + prices = record.offer_ids.mapped("price") + record.best_price = max(prices) if prices else 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 = None diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 63871439819..26a149347f6 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,5 @@ -from odoo import fields, models +from dateutil.relativedelta import relativedelta +from odoo import api, fields, models class EstatePropertyOffer(models.Model): @@ -15,3 +16,22 @@ class EstatePropertyOffer(models.Model): ) partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True) + validity = fields.Integer(default=7) + date_deadline = fields.Date(compute="_compute_date_deadline", inverse="_inverse_date_deadline", store=True) + + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for record in self: + if record.create_date: + record.date_deadline = record.create_date.date() + relativedelta(days=record.validity) + else: + record.date_deadline = fields.Date.today() + relativedelta(days=record.validity) + + def _inverse_date_deadline(self): + for record in self: + if record.date_deadline and record.create_date: + delta = record.date_deadline - record.create_date.date() + record.validity = delta.days + elif record.date_deadline: + delta = record.date_deadline - fields.Date.today() + record.validity = delta.days diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 11a833cfd53..2a068857a12 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -7,6 +7,8 @@ + + @@ -21,6 +23,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index f33bb3fad56..f2ea09737af 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -35,6 +35,7 @@ + @@ -52,6 +53,7 @@ + @@ -60,6 +62,8 @@ + + From 163c6d9b99a2d10e957710259ea1730ddf2f68f3 Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Wed, 7 Jan 2026 18:20:29 +0530 Subject: [PATCH 09/16] [IMP] estate: add action buttons for property and offer states - Add Cancel and Sold actions on properties with state validation - Add Accept and Refuse actions on offers with business constraints - Ensure accepted offer sets buyer and selling price, enforcing uniqueness CHAPTER - 9 --- estate/models/estate_property.py | 16 +++++++++++++++- estate/models/estate_property_offer.py | 24 +++++++++++++++++++++++- estate/views/estate_property_views.xml | 7 ++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 00a2ef654d2..4bf278aebfa 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,5 @@ from dateutil.relativedelta import relativedelta -from odoo import api, fields, models +from odoo import api, exceptions, fields, models class EstateProperty(models.Model): @@ -67,3 +67,17 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = None + + def action_sold(self): + for record in self: + if record.state == 'cancelled': + raise exceptions.UserError("Cancelled properties cannot be sold.") + record.state = 'sold' + return True + + def action_cancel(self): + for record in self: + if record.state == 'sold': + raise exceptions.UserError("Sold properties cannot be cancelled.") + record.state = 'cancelled' + return True diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 26a149347f6..01465091831 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,5 +1,5 @@ from dateutil.relativedelta import relativedelta -from odoo import api, fields, models +from odoo import api, exceptions, fields, models class EstatePropertyOffer(models.Model): @@ -35,3 +35,25 @@ def _inverse_date_deadline(self): elif record.date_deadline: delta = record.date_deadline - fields.Date.today() record.validity = delta.days + + def action_accept(self): + for record in self: + if record.property_id.buyer_id: + raise exceptions.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" + return True + + def action_refuse(self): + for record in self: + if record.property_id.buyer_id == record.partner_id and record.property_id.state == "offer_accepted": + record.property_id.selling_price = 0.0 + record.property_id.buyer_id = False + other_offers = record.property_id.offer_ids - record + has_other_offers = other_offers.filtered(lambda o: o.status != "refused") + record.property_id.state = "offer_received" if has_other_offers else "new" + record.status = "refused" + return True diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index f2ea09737af..0e57dacc01e 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -24,6 +24,10 @@ estate.property
+
+
@@ -65,6 +69,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 9feb594615b..f13e20ee0bb 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -21,7 +21,7 @@
- +
From dadd89338c9f8ca457ccbf77258b1bb8531e1a86 Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Mon, 19 Jan 2026 17:48:27 +0530 Subject: [PATCH 14/16] [ADD] estate_account: integrate invoicing on property sale - Create a link module to connect estate and account without tight coupling - Extend the property sold action using inheritance and super calls - Automatically generate a customer invoice with commission and admin fees - Keep accounting logic isolated for modularity and maintainability CHAPTER - 13 --- estate/.gitignore | 1 + estate/__manifest__.py | 1 + estate/models/estate_property.py | 2 +- estate/models/estate_property_offer.py | 2 +- estate/views/res_users_views.xml | 25 ++++++++++++++++++ estate_account/__init__.py | 1 + estate_account/__manifest__.py | 14 +++++++++++ estate_account/models/__init__.py | 1 + estate_account/models/estate_property.py | 32 ++++++++++++++++++++++++ 9 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 estate/.gitignore create mode 100644 estate/views/res_users_views.xml create mode 100644 estate_account/__init__.py create mode 100644 estate_account/__manifest__.py create mode 100644 estate_account/models/__init__.py create mode 100644 estate_account/models/estate_property.py diff --git a/estate/.gitignore b/estate/.gitignore new file mode 100644 index 00000000000..9eb78ba821b --- /dev/null +++ b/estate/.gitignore @@ -0,0 +1 @@ +i18n/ \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py index d90b3058b24..7246137fb5d 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -13,6 +13,7 @@ 'views/estate_property_offer_views.xml', 'views/estate_property_type_views.xml', 'views/estate_property_tag_views.xml', + 'views/res_users_views.xml', 'views/estate_menus.xml', ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c51e9c4b0f3..624eb9ad63f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,6 @@ from dateutil.relativedelta import relativedelta -from odoo import _, api, exceptions, fields, models +from odoo import api, exceptions, fields, models, _ from odoo.tools.float_utils import float_compare, float_is_zero diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index e51254d7b74..d2cfd9ff781 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,6 +1,6 @@ from dateutil.relativedelta import relativedelta -from odoo import _, api, exceptions, fields, models +from odoo import api, exceptions, fields, models, _ class EstatePropertyOffer(models.Model): diff --git a/estate/views/res_users_views.xml b/estate/views/res_users_views.xml new file mode 100644 index 00000000000..5a37af94f06 --- /dev/null +++ b/estate/views/res_users_views.xml @@ -0,0 +1,25 @@ + + + res.users.view.form.inherit.property + res.users + + + + + + + + + + + + + + + + + + + + + diff --git a/estate_account/__init__.py b/estate_account/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/estate_account/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py new file mode 100644 index 00000000000..ec4ace32819 --- /dev/null +++ b/estate_account/__manifest__.py @@ -0,0 +1,14 @@ +{ + 'name': 'Real Estate Account', + 'category': 'Real Estate', + 'version': '1.0', + 'author': 'Radhey Detroja(RADET)', + 'license': 'LGPL-3', + 'summary': 'Manage real estate properties', + "depends": [ + "estate", + "account", + ], + 'installable': True, + 'application': True, +} diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/estate_account/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py new file mode 100644 index 00000000000..76f6f2020ac --- /dev/null +++ b/estate_account/models/estate_property.py @@ -0,0 +1,32 @@ +from odoo import Command, models + + +class EstateProperty(models.Model): + _inherit = "estate.property" + + def action_sold(self): + res = super().action_sold() + for record in self: + self.env["account.move"].create( + { + "partner_id": record.buyer_id.id, + "move_type": "out_invoice", + "invoice_line_ids": [ + Command.create( + { + "name": "6% Commission", + "quantity": 1, + "price_unit": record.selling_price * 0.06, + } + ), + Command.create( + { + "name": "Administrative Fees", + "quantity": 1, + "price_unit": 100.0, + } + ), + ], + } + ) + return res From 1f062940897f75c40d3667fb035202e628ddc95c Mon Sep 17 00:00:00 2001 From: radet-odoo Date: Tue, 20 Jan 2026 18:48:24 +0530 Subject: [PATCH 15/16] [IMP] estate: add kanban view for properties - Add a kanban view to provide a visual, card-based representation of properties - Display key information including prices and tags with conditional visibility - Group properties by type by default and disable drag-and-drop for consistency CHAPTER 14 --- estate/views/estate_property_views.xml | 79 ++++++++++++++++++++------ 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index f13e20ee0bb..38f66a29d7e 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,16 +1,25 @@ + + Real Estate + estate.property + kanban,list,form + {'search_default_active': True} + + estate.property.list estate.property - + - + @@ -21,15 +30,19 @@
- -
- + @@ -58,15 +71,20 @@ - - + + - + + + diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..b8c8d1061db 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,13 @@ -import { Component } from "@odoo/owl"; +import { Component, markup } from "@odoo/owl"; +import { Counter } from "./counter/counter" +import { Card } from "./card/card"; export class Playground extends Component { static template = "awesome_owl.playground"; + static components = { Counter, Card }; + + setup() { + this.str1 = "
some content
"; + this.str2 = markup("
some content
"); + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..4b8bce64d44 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -4,6 +4,14 @@
hello world +
+ + +
+
+ + +
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index d2cfd9ff781..9f8a7607853 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -45,7 +45,7 @@ def create(self, vals_list): return offers @api.depends("create_date", "validity") - def _compute_date_deadline(self): + def _compute_deadline(self): for record in self: if record.create_date: record.date_deadline = record.create_date.date() + relativedelta(days=record.validity) diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py index ec4ace32819..a352f9fcfbd 100644 --- a/estate_account/__manifest__.py +++ b/estate_account/__manifest__.py @@ -5,9 +5,10 @@ 'author': 'Radhey Detroja(RADET)', 'license': 'LGPL-3', 'summary': 'Manage real estate properties', - "depends": [ - "estate", - "account", + 'depends': [ + 'estate', + 'account', + 'accountant', ], 'installable': True, 'application': True, diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py index 5e1963c9d2f..e6773ef3d05 100644 --- a/estate_account/models/__init__.py +++ b/estate_account/models/__init__.py @@ -1 +1,2 @@ from . import estate_property +from . import account_move diff --git a/estate_account/models/account_move.py b/estate_account/models/account_move.py new file mode 100644 index 00000000000..ea28b7a5391 --- /dev/null +++ b/estate_account/models/account_move.py @@ -0,0 +1,12 @@ +from odoo import models, fields + + +class AccountMove(models.Model): + _inherit = "account.move" + + property_id = fields.Many2one( + "estate.property", + string="Property", + ondelete="cascade", + required=False, + ) diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py index 76f6f2020ac..d75071e0b92 100644 --- a/estate_account/models/estate_property.py +++ b/estate_account/models/estate_property.py @@ -5,10 +5,10 @@ class EstateProperty(models.Model): _inherit = "estate.property" def action_sold(self): - res = super().action_sold() for record in self: self.env["account.move"].create( { + "property_id": record.id, "partner_id": record.buyer_id.id, "move_type": "out_invoice", "invoice_line_ids": [ @@ -29,4 +29,4 @@ def action_sold(self): ], } ) - return res + return super().action_sold()