From 404841d0df3a3b0ad7ac45822bed8699f7593283 Mon Sep 17 00:00:00 2001 From: aski97 Date: Mon, 17 Aug 2026 13:20:55 +0200 Subject: [PATCH 01/46] [ADD] estate: init the module --- estate/__init__.py | 0 estate/__manifest__.py | 17 +++++++++++++++++ 2 files changed, 17 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..176292dd4df --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,17 @@ +{ + 'name': 'Real Estate', + 'version': '0.1', + 'sequence': 100, + 'summary': 'Real Estate Advertisement', + 'depends': [ + 'base' + ], + 'data': [ + + ], + 'installable': True, + 'application': True, + 'assets': {}, + 'author': 'Odoo S.A.', + 'license': 'LGPL-3', +} From d88a5e6949beb8c33461603bb14f4afb2bf96b00 Mon Sep 17 00:00:00 2001 From: aski97 Date: Mon, 17 Aug 2026 14:31:20 +0200 Subject: [PATCH 02/46] [ADD] estate: init models package and create estate property with basic fields --- estate/__init__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 23 +++++++++++++++++++++++ 3 files changed, 25 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..9a7e03eded3 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models \ 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..495302428b9 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,23 @@ +from odoo import models, fields + +class EstateProperty(models.Model): + _name = 'estate.property' + _description = 'Estate Property' + + name = fields.Char('Property name', required=True, translate=True) + description = fields.Text('Property description', translate=True) + postcode = fields.Char('Property postcode') + date_availability = fields.Date('Date available') + expected_price = fields.Float('Expected price', required=True) + selling_price = fields.Float('Selling price') + bedrooms = fields.Integer('Bedrooms') + living_area = fields.Integer('Living area') + facades = fields.Integer('Facades') + garage = fields.Boolean('Has garage', default=False) + garden = fields.Boolean('Has garden', default=False) + garden_area = fields.Integer('Garden area') + garden_orientation = fields.Selection( + string='Garden orientation', + selection=[('north', 'North'),('south', 'South'),('east', 'East'),('west', 'West')], + help='Garden orientation is important for determining how much sunlight and warmth the outdoor space receives' + ) \ No newline at end of file From 6570e15658dceaabd10816af81df4300aff4f9a9 Mon Sep 17 00:00:00 2001 From: aski97 Date: Mon, 17 Aug 2026 15:12:12 +0200 Subject: [PATCH 03/46] [ADD] estate: init security giving access rights to base users group --- estate/__init__.py | 2 +- estate/__manifest__.py | 2 +- estate/models/__init__.py | 2 +- estate/models/estate_property.py | 5 +++-- estate/security/ir.model.access.csv | 2 ++ 5 files changed, 8 insertions(+), 5 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 176292dd4df..5d2502947a0 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,7 +7,7 @@ 'base' ], 'data': [ - + 'security/ir.model.access.csv' ], 'installable': True, 'application': True, 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 495302428b9..c3cf007224e 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 = 'Estate Property' @@ -18,6 +19,6 @@ class EstateProperty(models.Model): garden_area = fields.Integer('Garden area') garden_orientation = fields.Selection( string='Garden orientation', - selection=[('north', 'North'),('south', 'South'),('east', 'East'),('west', 'West')], + selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')], help='Garden orientation is important for determining how much sunlight and warmth the outdoor space receives' - ) \ 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..0708a38ad31 --- /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,estate.property access,model_estate_property,base.group_user,1,1,1,1 From 16a92c3d1091a52bb5ef54c214c8526aa6aaa4c1 Mon Sep 17 00:00:00 2001 From: aski97 Date: Mon, 17 Aug 2026 16:26:01 +0200 Subject: [PATCH 04/46] [ADD] estate: create menu and actions under the views folder --- estate/__manifest__.py | 2 ++ estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 10 ++++++++++ 3 files changed, 20 insertions(+) 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 5d2502947a0..719980fb238 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,6 +7,8 @@ 'base' ], 'data': [ + 'views/estate_property_views.xml', + 'views/estate_menus.xml', 'security/ir.model.access.csv' ], 'installable': True, diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..ca15ddfa284 --- /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..9303aecebf8 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,10 @@ + + + + + Properties + estate.property + list,form + ` + + \ No newline at end of file From e87b554134689a1e96ff7bd64cb2f23db091b139 Mon Sep 17 00:00:00 2001 From: aski97 Date: Mon, 17 Aug 2026 16:31:39 +0200 Subject: [PATCH 05/46] [CLN] estate: rename the strings of the estate property fields for a better comprehension. --- estate/models/estate_property.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c3cf007224e..4e98a7f53a2 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -5,18 +5,18 @@ class EstateProperty(models.Model): _name = 'estate.property' _description = 'Estate Property' - name = fields.Char('Property name', required=True, translate=True) - description = fields.Text('Property description', translate=True) - postcode = fields.Char('Property postcode') - date_availability = fields.Date('Date available') + name = fields.Char('Title', required=True, translate=True) + description = fields.Text('Description', translate=True) + postcode = fields.Char('Postcode') + date_availability = fields.Date('Available From') expected_price = fields.Float('Expected price', required=True) selling_price = fields.Float('Selling price') bedrooms = fields.Integer('Bedrooms') - living_area = fields.Integer('Living area') + living_area = fields.Integer('Living area (sqm)') facades = fields.Integer('Facades') - garage = fields.Boolean('Has garage', default=False) - garden = fields.Boolean('Has garden', default=False) - garden_area = fields.Integer('Garden area') + garage = fields.Boolean('Garage', default=False) + garden = fields.Boolean('Garden', default=False) + garden_area = fields.Integer('Garden area (sqm)') garden_orientation = fields.Selection( string='Garden orientation', selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')], From 470a5a784ccaba999bff171f383ffe9cd1c196e8 Mon Sep 17 00:00:00 2001 From: aski97 Date: Mon, 17 Aug 2026 17:15:21 +0200 Subject: [PATCH 06/46] [IMP] estate: add active and status field to estate property and enhance field attributes. --- estate/models/estate_property.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 4e98a7f53a2..fccc79c8fc5 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,3 +1,4 @@ +from dateutil.relativedelta import relativedelta from odoo import models, fields @@ -8,10 +9,12 @@ class EstateProperty(models.Model): name = fields.Char('Title', required=True, translate=True) description = fields.Text('Description', translate=True) postcode = fields.Char('Postcode') - date_availability = fields.Date('Available From') - expected_price = fields.Float('Expected price', required=True) - selling_price = fields.Float('Selling price') - bedrooms = fields.Integer('Bedrooms') + date_availability = fields.Date( + string='Available From', + default=lambda _: fields.Date.today() + relativedelta(months=3)) + expected_price = fields.Float('Expected price', required=True, copy=False) + 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('Facades') garage = fields.Boolean('Garage', default=False) @@ -22,3 +25,10 @@ class EstateProperty(models.Model): selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')], help='Garden orientation is important for determining how much sunlight and warmth the outdoor space receives' ) + active = fields.Boolean('Active', default=False) + status = fields.Selection( + string='Status', + selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')], + default='new', + required=True, + ) From 1aeaf0058708d0ce580645509de241f3c6210644 Mon Sep 17 00:00:00 2001 From: aski97 Date: Mon, 17 Aug 2026 17:20:13 +0200 Subject: [PATCH 07/46] [FIX] estate: make date availability not copyable and expected price copyable. --- estate/models/estate_property.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index fccc79c8fc5..d9bf917e0c6 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -11,8 +11,10 @@ class EstateProperty(models.Model): postcode = fields.Char('Postcode') date_availability = fields.Date( string='Available From', - default=lambda _: fields.Date.today() + relativedelta(months=3)) - expected_price = fields.Float('Expected price', required=True, copy=False) + default=lambda _: fields.Date.today() + relativedelta(months=3), + copy=False + ) + 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)') From 2a695bfa0abe1472f53bf4ce773d0ec11179e6e3 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 09:24:35 +0200 Subject: [PATCH 08/46] [CLN] estate: clean unnecessary single quote in the view file --- estate/views/estate_property_views.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 9303aecebf8..6f56e488595 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,6 +5,6 @@ Properties estate.property list,form - ` + \ No newline at end of file From fe65c6e20cf03412b88126ba43c1a1d94b43dd3d Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 11:27:50 +0200 Subject: [PATCH 09/46] [ADD] estate: add basic custom views (list, form, search) --- estate/views/estate_property_views.xml | 74 ++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 6f56e488595..f5b27cf2b88 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,6 +1,80 @@ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +
+

+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.list + estate.property + + + + + + + + + + + + + Properties estate.property From f69c7ea5e87f4b2c8c0abd4c49f278948b709866 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 13:10:33 +0200 Subject: [PATCH 10/46] [ADD] estate: real estate property type with views and connections to estate property model --- estate/models/__init__.py | 1 + estate/models/estate_property.py | 1 + estate/models/estate_property_type.py | 8 ++++++++ estate/security/ir.model.access.csv | 1 + estate/views/estate_menus.xml | 3 +++ estate/views/estate_property_views.xml | 28 ++++++++++++++++++++++++++ 6 files changed, 42 insertions(+) create mode 100644 estate/models/estate_property_type.py diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..97aee757823 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,2 @@ 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 d9bf917e0c6..6f4f5fdbfc9 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -34,3 +34,4 @@ class EstateProperty(models.Model): default='new', required=True, ) + property_type_id = fields.Many2one("estate.property.type", string='Type') diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..2f444b6ea72 --- /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.type' + _description = 'Estate Property Type' + + name = fields.Char('Name', required=True, translate=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 0708a38ad31..55ad0170ed3 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,estate.property access,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_tye,estate.property.type access,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 ca15ddfa284..7f6bcfe4f3f 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 f5b27cf2b88..308b1e03a77 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,6 +1,8 @@ + + estate.property.search estate.property @@ -12,6 +14,7 @@ + @@ -51,6 +54,7 @@ + @@ -65,6 +69,7 @@ + @@ -80,5 +85,28 @@ estate.property list,form + + + + + + estate.property.type.form + estate.property.type + +
+ +
+

+
+
+
+
+
+ + + Property type + estate.property.type + list,form +
\ No newline at end of file From 6760dd32889b361d85e7a53e8d30412b873c732f Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 13:21:51 +0200 Subject: [PATCH 11/46] [ADD] estate: add salesperson and buyer to real estate property. --- estate/models/estate_property.py | 10 ++++++++++ estate/views/estate_property_views.xml | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 6f4f5fdbfc9..c3298117ef5 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -35,3 +35,13 @@ class EstateProperty(models.Model): required=True, ) property_type_id = fields.Many2one("estate.property.type", string='Type') + # Other Info + salesperson_id = fields.Many2one( + comodel_name='res.users', + string='Salesman', + default=lambda self: self.env.user) + buyer_id = fields.Many2one( + comodel_name='res.partner', + string='Buyer', + copy=False, + ) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 308b1e03a77..abe0d78e0ba 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -57,6 +57,12 @@ + + + + + + From aa6894336ed81c8aa79428755d2aa116bbdb6504 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 13:24:45 +0200 Subject: [PATCH 12/46] [FIX] estate: move the property type id from description page to title section (form view) --- estate/views/estate_property_views.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index abe0d78e0ba..19b3133a0b1 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -31,6 +31,7 @@

+ @@ -54,7 +55,6 @@ - From e998054cbdd613915b2dda08cb98dc6a8fb5a3b6 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 13:49:27 +0200 Subject: [PATCH 13/46] [ADD] estate: add property tags model and include it in the property form and menu --- estate/models/__init__.py | 3 ++- estate/models/estate_property.py | 1 + estate/models/estate_property_tag.py | 8 ++++++++ estate/security/ir.model.access.csv | 3 ++- estate/views/estate_menus.xml | 1 + estate/views/estate_property_views.xml | 26 +++++++++++++++++++++++++- 6 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 estate/models/estate_property_tag.py diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 97aee757823..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 \ No newline at end of file +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 c3298117ef5..6d4a5fe8faa 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -35,6 +35,7 @@ class EstateProperty(models.Model): required=True, ) property_type_id = fields.Many2one("estate.property.type", string='Type') + property_tag_ids = fields.Many2many('estate.property.tag', string='Tags') # Other Info salesperson_id = fields.Many2one( comodel_name='res.users', diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..ca8e2006a7a --- /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 Tag' + + name = fields.Char('Name', required=True, translate=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 55ad0170ed3..8587457dd23 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,3 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_estate_property,estate.property access,model_estate_property,base.group_user,1,1,1,1 -access_estate_property_tye,estate.property.type access,model_estate_property_type,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_type,estate.property.type access,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag,estate.property.tag access,model_estate_property_tag,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 7f6bcfe4f3f..7c4a9a3c62d 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 19b3133a0b1..558f3db3c1b 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -29,6 +29,7 @@

+ @@ -110,9 +111,32 @@ - Property type + Property Type estate.property.type list,form + + + + + + estate.property.tag.form + estate.property.tag + +
+ + + + + +
+
+
+ + + Property Tags + estate.property.tag + list,form + \ No newline at end of file From f8082f960aeba6e4345237395d78cbaba066097f Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 14:17:18 +0200 Subject: [PATCH 14/46] [ADD] estate: add property offer model and include it in the property views --- estate/models/__init__.py | 1 + estate/models/estate_property.py | 1 + estate/models/estate_property_offer.py | 23 +++++++++++++++++++++++ estate/security/ir.model.access.csv | 3 ++- estate/views/estate_property_views.xml | 18 ++++++++++++++++++ 5 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 estate/models/estate_property_offer.py diff --git a/estate/models/__init__.py b/estate/models/__init__.py index c620ac481a3..09b2099fe84 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 \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 6d4a5fe8faa..a19f97cae18 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -36,6 +36,7 @@ class EstateProperty(models.Model): ) property_type_id = fields.Many2one("estate.property.type", string='Type') property_tag_ids = fields.Many2many('estate.property.tag', string='Tags') + property_offer_ids = fields.One2many('estate.property.offer', inverse_name="property_id", string='Offers', copy=False) # Other Info salesperson_id = fields.Many2one( comodel_name='res.users', diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..e146c91d7ab --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,23 @@ +from odoo import models, fields + + +class EstatePropertyOffer(models.Model): + _name = 'estate.property.offer' + _description = 'Estate Property Offer' + + price = fields.Float(string='Price') + status = fields.Selection( + string='Status', + selection=[("accepted", "Accepted"), ("refused", "Refused")], + copy=False, + ) + partner_id = fields.Many2one( + comodel_name='res.partner', + string='Partner', + required=True, + ) + property_id = fields.Many2one( + comodel_name='estate.property', + string='Property', + required=True, + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 8587457dd23..236c9b19446 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,4 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_estate_property,estate.property access,model_estate_property,base.group_user,1,1,1,1 access_estate_property_type,estate.property.type access,model_estate_property_type,base.group_user,1,1,1,1 -access_estate_property_tag,estate.property.tag access,model_estate_property_tag,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_tag,estate.property.tag access,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer,estate.property.offer access,model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 558f3db3c1b..4d95dd3a907 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -58,6 +58,9 @@
+ + + @@ -138,5 +141,20 @@ estate.property.tag list,form + + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + \ No newline at end of file From 8db5a915138d38a5f019af728a46de27de4b6ca2 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 14:54:55 +0200 Subject: [PATCH 15/46] [LINT] estate: add missing end-of-line to files. --- estate/models/__init__.py | 2 +- estate/views/estate_menus.xml | 2 +- estate/views/estate_property_views.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 7c4a9a3c62d..3aafa46be3a 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 4d95dd3a907..8b3139b1aa8 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -157,4 +157,4 @@ - \ No newline at end of file + From b8b5eca02368023aad45de3ed25d00cd78bb3601 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 15:16:14 +0200 Subject: [PATCH 16/46] [ADD] estate: add total area field (compute) to estate property model and include in form --- estate/models/estate_property.py | 9 ++++++++- estate/views/estate_property_views.xml | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a19f97cae18..a70742cca10 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 models, fields +from odoo import models, fields, api class EstateProperty(models.Model): @@ -37,6 +37,8 @@ class EstateProperty(models.Model): property_type_id = fields.Many2one("estate.property.type", string='Type') property_tag_ids = fields.Many2many('estate.property.tag', string='Tags') property_offer_ids = fields.One2many('estate.property.offer', inverse_name="property_id", string='Offers', copy=False) + # Computed Field + total_area = fields.Integer('Total area (sqm)', compute='_compute_total_area', readonly=True, copy=False) # Other Info salesperson_id = fields.Many2one( comodel_name='res.users', @@ -47,3 +49,8 @@ class EstateProperty(models.Model): string='Buyer', copy=False, ) + + @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 8b3139b1aa8..b7b2c0429cb 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -56,6 +56,7 @@ + From ffd399c5cf18bf0996010a71ac94b7ef6dafa834 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 15:40:05 +0200 Subject: [PATCH 17/46] [ADD] estate: add best price field (compute) to estate property model and include in form --- estate/models/estate_property.py | 9 +++++++++ estate/views/estate_property_views.xml | 1 + 2 files changed, 10 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a70742cca10..5f6e5c95953 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -39,6 +39,7 @@ class EstateProperty(models.Model): property_offer_ids = fields.One2many('estate.property.offer', inverse_name="property_id", string='Offers', copy=False) # Computed Field total_area = fields.Integer('Total area (sqm)', compute='_compute_total_area', readonly=True, copy=False) + best_price = fields.Float('Best offer', compute='_compute_best_price', readonly=True, copy=False) # Other Info salesperson_id = fields.Many2one( comodel_name='res.users', @@ -54,3 +55,11 @@ class EstateProperty(models.Model): def _compute_total_area(self): for record in self: record.total_area = record.living_area + record.garden_area + + @api.depends("property_offer_ids.price") + def _compute_best_price(self): + for record in self: + if record.property_offer_ids: + record.best_price = max(record.property_offer_ids.mapped('price')) + else: + record.best_price = 0 diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index b7b2c0429cb..0124f480acb 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -38,6 +38,7 @@
+ From 4040916581545d50e270ea312fe8c977943ec21c Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 15:46:18 +0200 Subject: [PATCH 18/46] [FIX] estate: create property offers form in order to remove property field --- estate/views/estate_property_views.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 0124f480acb..ddc06b1e116 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -146,6 +146,23 @@ + + estate.property.offer.form + estate.property.offer + +
+ + + + + + + + +
+
+
+ estate.property.offer.list estate.property.offer From d0194c00ad4e7923559a000fad8a3806fad1fc90 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 16:41:46 +0200 Subject: [PATCH 19/46] [ADD] estate: add validity and date deadline fields in the property offers model and views (list and form). --- estate/models/estate_property_offer.py | 29 +++++++++++++++++++++++++- estate/views/estate_property_views.xml | 7 +++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index e146c91d7ab..95d9c57726e 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 datetime import date, timedelta +from odoo import models, fields, api class EstatePropertyOffer(models.Model): @@ -21,3 +22,29 @@ class EstatePropertyOffer(models.Model): string='Property', required=True, ) + validity = fields.Integer( + string='Validity (days)', + default=7, + required=True, + ) + # Computed Field + date_deadline = fields.Date( + string='Date of Deadline', + compute='_compute_date_deadline', + inverse='_inverse_date_deadline', + ) + + @api.depends('create_date', 'validity') + def _compute_date_deadline(self): + for record in self: + record.date_deadline = self._get_date_or_today(record.create_date) + timedelta(days=record.validity) + + def _inverse_date_deadline(self): + for record in self: + create_date_or_today = self._get_date_or_today(record.create_date) + record.validity = (record.date_deadline - create_date_or_today).days + + @staticmethod + def _get_date_or_today(datetime_to_evaluate): + """ Returns the date part of a given datetime if present, otherwise returns today's date """ + return datetime_to_evaluate.date() if datetime_to_evaluate else date.today() diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index ddc06b1e116..23e7ac00f19 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -155,8 +155,9 @@ + + - @@ -169,7 +170,9 @@ - + + + From 44f9335b891ef1074d6b031d153318298d8a3758 Mon Sep 17 00:00:00 2001 From: aski97 Date: Tue, 18 Aug 2026 16:54:47 +0200 Subject: [PATCH 20/46] [ADD] estate: set default values on garden area and orientation when flag/unflag garden field in the estate property form view --- estate/models/estate_property.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5f6e5c95953..93fe8128586 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -63,3 +63,13 @@ def _compute_best_price(self): record.best_price = max(record.property_offer_ids.mapped('price')) else: record.best_price = 0 + + @api.onchange('garden') + def _onchange_garden_flag(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = 'north' + return + + self.garden_area = None + self.garden_orientation = None From 1ee002f1fac4d4e308137055c77c3fd74f55571c Mon Sep 17 00:00:00 2001 From: aski97 Date: Wed, 19 Aug 2026 09:49:57 +0200 Subject: [PATCH 21/46] [IMP] estate: add cancel and sold buttons in estate property model form view in order to set its status. --- estate/models/estate_property.py | 33 +++++++++++++++++++++++++- estate/views/estate_property_views.xml | 10 +++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 93fe8128586..53a61def043 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 models, fields, api +from odoo import models, fields, api, exceptions class EstateProperty(models.Model): @@ -73,3 +73,34 @@ def _onchange_garden_flag(self): self.garden_area = None self.garden_orientation = None + + def ensure_status_is_not(self, statuses, error_message=None): + """ Ensures that the status is not equal or included in statuses """ + if not statuses: + return + + if isinstance(statuses, str): + statuses = [statuses] + + if not isinstance(statuses, (list, tuple, set)): + raise TypeError('statuses must be a string, list, tuple, or set') + + if self.status in statuses: + msg = error_message or f'Action not allowed for status: {self.status}' + raise exceptions.UserError(msg) + + + # ACTIONS + def action_set_status_cancelled(self): + for record in self: + record.ensure_status_is_not("sold", error_message="You cannot cancel a sold property") + record.status = "cancelled" + + return True + + def action_set_status_sold(self): + for record in self: + record.ensure_status_is_not("cancelled", error_message="You cannot sell a cancelled property") + record.status = "sold" + + return True diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 23e7ac00f19..e078926068a 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -26,12 +26,21 @@ estate.property
+
+

+ @@ -56,7 +65,6 @@ - From 65923129885cf860337e4e2413f8829f7eb670f2 Mon Sep 17 00:00:00 2001 From: aski97 Date: Wed, 19 Aug 2026 10:15:33 +0200 Subject: [PATCH 22/46] [IMP] estate: add accept and refuse buttons in the property offers list view --- estate/models/estate_property_offer.py | 17 +++++++++++++++++ estate/views/estate_property_views.xml | 5 +++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 95d9c57726e..5057179ae04 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -11,6 +11,7 @@ class EstatePropertyOffer(models.Model): string='Status', selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False, + readonly=True, ) partner_id = fields.Many2one( comodel_name='res.partner', @@ -48,3 +49,19 @@ def _inverse_date_deadline(self): def _get_date_or_today(datetime_to_evaluate): """ Returns the date part of a given datetime if present, otherwise returns today's date """ return datetime_to_evaluate.date() if datetime_to_evaluate else date.today() + + + # ACTIONS + + def action_accept_offer(self): + for record in self: + record.status = "accepted" + record.property_id.selling_price = self.price + + return True + + def action_refuse_offer(self): + for record in self: + record.status = "refused" + + return True diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index e078926068a..a4d693df04f 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -165,7 +165,7 @@ - + @@ -181,10 +181,11 @@ + +

@@ -204,5 +217,11 @@
+ + + Offers + estate.property.offer + list + From 000a2469813a15dedc045fd6625f1dc43ef7e854 Mon Sep 17 00:00:00 2001 From: aski97 Date: Wed, 19 Aug 2026 16:59:35 +0200 Subject: [PATCH 34/46] [IMP] estate: add domain filter for offers action --- estate/views/estate_property_views.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 429b57f2be9..f272652583a 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -130,11 +130,10 @@
+
+
+

+
+ + + + + + + + + + + + + +
+ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 32e2db12664..dac7cd2b896 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,7 +1,12 @@ - + + Properties + estate.property + list,form,kanban + {'search_default_available': True} + estate.property.search @@ -101,14 +106,6 @@ - - Properties - estate.property - list,form,kanban - {'search_default_available': True} - - - estate.property.kanban estate.property @@ -136,121 +133,5 @@ - - - - - estate.property.type.list - estate.property.type - - - - - - - - - - estate.property.type.form - estate.property.type - -
- -
- -
-
-

-
- - - - - - - - - - - -
-
-
-
- - - Property Type - estate.property.type - list,form - - - - - - estate.property.tag.list - estate.property.tag - - - - - - - - - Property Tags - estate.property.tag - list,form - - - - - - estate.property.offer.form - estate.property.offer - -
- - - - - - - - - -
-
-
- - - estate.property.offer.list - estate.property.offer - - - - - - -