From 63c3ad31aa3334ed6b5672353cd89a5edbce9f52 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Tue, 15 Sep 2026 10:42:21 +0200 Subject: [PATCH 01/15] [ADD] Add the basic setup for the estate module --- estate/__init__.py | 0 estate/__manifest__.py | 4 ++++ 2 files changed, 4 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..7389e7b3207 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,4 @@ +{ + 'name': "Estate", + 'depends': ["base"] +} \ No newline at end of file From 92218f63583481805b71fc66a75b3cb60a05ba48 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Tue, 15 Sep 2026 11:49:26 +0200 Subject: [PATCH 02/15] [IMP] estate: Added models --- estate/__init__.py | 1 + estate/__manifest__.py | 6 ++++-- estate/models/__init__.py | 1 + estate/models/estate_property.py | 25 +++++++++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py diff --git a/estate/__init__.py b/estate/__init__.py index e69de29bb2d..0650744f6bc 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 7389e7b3207..a870fad2e0e 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,4 +1,6 @@ { 'name': "Estate", - 'depends': ["base"] -} \ No newline at end of file + 'depends': ["base"], + 'application': True, + 'installable': True +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..90643661aa8 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,25 @@ +from odoo import fields, models + +class EstatePropertyModel(models.Model): + _name = "estate_property" + _description = "The details of a property" + + name= fields.Char('Estate Name', required= True) + description= fields.Text() + 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_orientation= fields.Selection(string='Type', + selection=[('east', 'East'), + ('west', 'West'), + ('north', 'North'), + ('south', 'South') + ] + ) + From 2e76d0bc6939ec959e97214ca2855f29ad5bce9a Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Tue, 15 Sep 2026 13:33:42 +0200 Subject: [PATCH 03/15] [FIX] Made field mandatory --- estate/models/estate_property.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 90643661aa8..0e3ba8344bc 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -8,7 +8,7 @@ class EstatePropertyModel(models.Model): description= fields.Text() postcode= fields.Char() date_availability= fields.Date() - expected_price= fields.Float() + expected_price= fields.Float(required= True) selling_price= fields.Float() bedrooms= fields.Integer() living_area= fields.Integer() From a2ee7f419f8bdab24da8a685549b6da452877a44 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Tue, 15 Sep 2026 14:10:44 +0200 Subject: [PATCH 04/15] [FIX] Styling issues [ADD] estate: add estate access for base users --- estate/__manifest__.py | 5 ++++- estate/models/estate_property.py | 24 ++++++++++++------------ estate/security/ir.model.access.csv | 2 ++ 3 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index a870fad2e0e..c8bb2c5254c 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,5 +2,8 @@ 'name': "Estate", 'depends': ["base"], 'application': True, - 'installable': True + 'installable': True, + 'data': [ + 'security/ir.model.access.csv' + ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0e3ba8344bc..07fe3174c9f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,20 +1,21 @@ from odoo import fields, models + class EstatePropertyModel(models.Model): _name = "estate_property" _description = "The details of a property" - name= fields.Char('Estate Name', 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() + name = fields.Char('Estate Name', 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() + has_garage = fields.Boolean() + has_garden = fields.Boolean() garden_orientation= fields.Selection(string='Type', selection=[('east', 'East'), ('west', 'West'), @@ -22,4 +23,3 @@ class EstatePropertyModel(models.Model): ('south', 'South') ] ) - diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..6923cfaaa0c --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_estate_model,access_estate_model,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file From 2b37ba84310f7e31b7f75d1c8981cb2a1e381ef9 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Tue, 15 Sep 2026 14:29:57 +0200 Subject: [PATCH 05/15] [FIX] formatting issues --- estate/models/estate_property.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 07fe3174c9f..d05decfdfb6 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -16,10 +16,12 @@ class EstatePropertyModel(models.Model): facades = fields.Integer() has_garage = fields.Boolean() has_garden = fields.Boolean() - garden_orientation= fields.Selection(string='Type', - selection=[('east', 'East'), - ('west', 'West'), - ('north', 'North'), - ('south', 'South') - ] - ) + garden_orientation = fields.Selection( + string='Type', + selection=[ + ('east', 'East'), + ('west', 'West'), + ('north', 'North'), + ('south', 'South'), + ] + ) \ No newline at end of file From 52aac90103802e0ce7faa6e30a17664fff645f4a Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Tue, 15 Sep 2026 17:06:12 +0200 Subject: [PATCH 06/15] [ADD] estate: Added menus in app added fields and default values in fields of new estate [IMP] estate: updated gitignore to exclude vs config --- .gitignore | 1 + estate/__manifest__.py | 4 +++- estate/models/estate_property.py | 29 +++++++++++++++++++------- estate/views/estate_menus_views.xml | 11 ++++++++++ estate/views/estate_property_views.xml | 10 +++++++++ 5 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 estate/views/estate_menus_views.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/.gitignore b/.gitignore index b6e47617de1..1b77d315ad1 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ dmypy.json # Pyre type checker .pyre/ +.vscode/settings.json diff --git a/estate/__manifest__.py b/estate/__manifest__.py index c8bb2c5254c..510b76e2c36 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -4,6 +4,8 @@ 'application': True, 'installable': True, 'data': [ - 'security/ir.model.access.csv' + 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus_views.xml' ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d05decfdfb6..0204231a1d1 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 fields, models @@ -5,23 +6,35 @@ class EstatePropertyModel(models.Model): _name = "estate_property" _description = "The details of a property" - name = fields.Char('Estate Name', required = True) + name = fields.Char('Estate Name', 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() + date_availability = fields.Date(copy=False, default=fields.Date.today()+relativedelta(months=3)) + expected_price = fields.Float(required=True) + selling_price = fields.Float(readonly=True) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() has_garage = fields.Boolean() has_garden = fields.Boolean() garden_orientation = fields.Selection( - string='Type', + string='Garden Orientation', selection=[ ('east', 'East'), ('west', 'West'), ('north', 'North'), ('south', 'South'), - ] - ) \ No newline at end of file + ] + ) + state = fields.Selection( + string='State', + selection=[ + ('new', 'New'), + ('offer_received', 'Offer Received'), + ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled'), + ], + default='new' + ) + active = fields.Boolean(default=True) \ No newline at end of file diff --git a/estate/views/estate_menus_views.xml b/estate/views/estate_menus_views.xml new file mode 100644 index 00000000000..f7e63c097f0 --- /dev/null +++ b/estate/views/estate_menus_views.xml @@ -0,0 +1,11 @@ + + + + + + + + \ 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..56f23c1f8d0 --- /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 861aa1772a0e38bc277e61ac803884bdb11c30f0 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Wed, 16 Sep 2026 10:51:45 +0200 Subject: [PATCH 07/15] [ADD] estate: Added garden area field [IMP] estate: Update List, Form and Search for cleaner display --- estate/models/estate_property.py | 1 + estate/views/estate_property_views.xml | 90 +++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0204231a1d1..d9e6db28143 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -17,6 +17,7 @@ class EstatePropertyModel(models.Model): facades = fields.Integer() has_garage = fields.Boolean() has_garden = fields.Boolean() + garden_area = fields.Integer() garden_orientation = fields.Selection( string='Garden Orientation', selection=[ diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 56f23c1f8d0..ea097ad5b56 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -7,4 +7,92 @@ list,form - \ No newline at end of file + + + estate.property.list + estate_property + + + + + + + + + + + + + + + + estate.property.form + estate_property + +
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + estate.property.search + estate_property + + + + + + + + + + + + + + + + + + + From 6d3ba9638d38f86a3aff00f611119c0b0ee92cb0 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Thu, 17 Sep 2026 09:44:27 +0200 Subject: [PATCH 08/15] [IMP] estate: Added buyers, salespersons, tags and offers Created offers Created tags --- estate/__manifest__.py | 7 ++- estate/models/__init__.py | 3 ++ estate/models/estate_property.py | 14 ++++-- estate/models/estate_property_offer.py | 17 ++++++++ estate/models/estate_property_tag.py | 8 ++++ estate/models/estate_property_type.py | 8 ++++ estate/security/ir.model.access.csv | 5 ++- estate/views/estate_menus_views.xml | 14 +++--- estate/views/estate_property_offer_views.xml | 17 ++++++++ estate/views/estate_property_tag_views.xml | 33 ++++++++++++++ estate/views/estate_property_type_views.xml | 33 ++++++++++++++ estate/views/estate_property_views.xml | 45 ++++++++++++++------ 12 files changed, 177 insertions(+), 27 deletions(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_offer_views.xml create mode 100644 estate/views/estate_property_tag_views.xml create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 510b76e2c36..ebccc6f6092 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,9 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', - 'views/estate_menus_views.xml' - ] + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', + 'views/estate_property_offer_views.xml', + 'views/estate_menus_views.xml', + ], } diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..f2347942227 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ +from . import estate_property_type +from . import estate_property_tag +from . import estate_property_offer from . import estate_property diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d9e6db28143..d10b1149da8 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from dateutil.relativedelta import relativedelta + from odoo import fields, models @@ -9,7 +10,7 @@ class EstatePropertyModel(models.Model): name = fields.Char('Estate Name', required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date(copy=False, default=fields.Date.today()+relativedelta(months=3)) + date_availability = fields.Date(copy=False, default=fields.Date.today() + relativedelta(months=3)) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True) bedrooms = fields.Integer(default=2) @@ -25,7 +26,7 @@ class EstatePropertyModel(models.Model): ('west', 'West'), ('north', 'North'), ('south', 'South'), - ] + ], ) state = fields.Selection( string='State', @@ -36,6 +37,11 @@ class EstatePropertyModel(models.Model): ('sold', 'Sold'), ('cancelled', 'Cancelled'), ], - default='new' + default='new', ) - active = fields.Boolean(default=True) \ No newline at end of file + active = fields.Boolean(default=True) + property_type_id = fields.Many2one("estate_property_type", string="Type") + buyer_id = fields.Many2one("res.partner", string="Buyer") + salesperson_id = fields.Many2one('res.users', string='Salesperson', index=True, tracking=True, default=lambda self: self.env.user) + property_tags_ids = fields.Many2many("estate_property_tag", string="Tags") + offers_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..0f8f9a2f589 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,17 @@ +from odoo import fields, models + + +class EstatePropertyOfferModel(models.Model): + _name = "estate_property_offer" + _description = "An offer for an estate" + + price = fields.Float() + status = fields.Selection(string='State', + selection=[ + ('refused', 'Refused'), + ('accepted', 'Offer Accepted'), + ], + copy=False, + ) + partner_id = fields.Many2one("res.partner", string="Buyer") + property_id = fields.Many2one("estate_property", string="Property") diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..80bd9afca63 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstatePropertyTagModel(models.Model): + _name = "estate_property_tag" + _description = "The tag of an estate" + + name = fields.Char(required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..c63bc509af3 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstatePropertyTypeModel(models.Model): + _name = "estate_property_type" + _description = "The type of an estate" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 6923cfaaa0c..fd53e8c2af8 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_estate_model,access_estate_model,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_model,access_estate_model,model_estate_property,base.group_user,1,1,1,1 +access_estate_type_model,access_estate_type_model,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_tag_model,access_estate_tag_model,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_offer_model,access_estate_offer_model,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus_views.xml b/estate/views/estate_menus_views.xml index f7e63c097f0..7e77c8e2109 100644 --- a/estate/views/estate_menus_views.xml +++ b/estate/views/estate_menus_views.xml @@ -1,11 +1,13 @@ - - + + + + + + + - \ No newline at end of file + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..684097a75c1 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,17 @@ + + + + + estate.property.offer.list + 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..934e0a37409 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,33 @@ + + + + + Property Tags + estate_property_tag + list,form + + + + + estate.property.tag.list + estate_property_tag + + + + + + + + + + estate.property.tag.form + estate_property_tag + +
+ +

+
+
+
+
+
diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..b33e85f12c3 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,33 @@ + + + + + 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 ea097ad5b56..4e0094ab13f 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -2,24 +2,24 @@ - Properties + Property estate_property list,form - + estate.property.list estate_property - + - + - + @@ -35,7 +35,7 @@ - + @@ -48,13 +48,30 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -76,7 +93,7 @@ - + From fc7c422fca1193073401aa1b98c6600ee0ffebda Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Thu, 17 Sep 2026 09:49:27 +0200 Subject: [PATCH 09/15] [FIX] corrections of a couple of wrong names --- estate/views/estate_property_offer_views.xml | 2 +- estate/views/estate_property_tag_views.xml | 2 +- estate/views/estate_property_type_views.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 684097a75c1..29e2a212789 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -1,7 +1,7 @@ - + estate.property.offer.list estate_property_offer diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index 934e0a37409..759f4061884 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -8,7 +8,7 @@ - + estate.property.tag.list estate_property_tag diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index b33e85f12c3..5689e1ad855 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -8,7 +8,7 @@ - + estate.property.type.list estate_property_type From dce0b6347f4ee253cf380c11b3a2406d3c107d49 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Thu, 17 Sep 2026 09:52:28 +0200 Subject: [PATCH 10/15] [FIX] Added missing keys in manifest --- estate/__manifest__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index ebccc6f6092..03b5d0ec870 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,5 +1,7 @@ { 'name': "Estate", + 'author': "delje", + 'license': "LGPL-3", 'depends': ["base"], 'application': True, 'installable': True, From 0a6d7d57af0b3d242ffb5bf39e2f24d8e8946071 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Thu, 17 Sep 2026 09:54:19 +0200 Subject: [PATCH 11/15] [FIX] Removed unnecessary tracking in salesperson_id --- estate/models/estate_property.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d10b1149da8..5e9731451a9 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -42,6 +42,6 @@ class EstatePropertyModel(models.Model): active = fields.Boolean(default=True) property_type_id = fields.Many2one("estate_property_type", string="Type") buyer_id = fields.Many2one("res.partner", string="Buyer") - salesperson_id = fields.Many2one('res.users', string='Salesperson', index=True, tracking=True, default=lambda self: self.env.user) + salesperson_id = fields.Many2one('res.users', string='Salesperson', index=True, default=lambda self: self.env.user) property_tags_ids = fields.Many2many("estate_property_tag", string="Tags") offers_ids = fields.One2many("estate_property_offer", "property_id", string="Offers") From 679e80d22cfa5afface9d56de7a37a039bfc571c Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Thu, 17 Sep 2026 11:38:35 +0200 Subject: [PATCH 12/15] [IMP] estate: added computed fields on total_area and best price added computed field on offer validity added onchange on has_garden --- estate/models/estate_property.py | 25 ++++++++++++++++++++++--- estate/models/estate_property_offer.py | 15 ++++++++++++++- estate/views/estate_property_views.xml | 6 ++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5e9731451a9..11e80fe5ed7 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 fields, models +from odoo import api, fields, models class EstatePropertyModel(models.Model): @@ -14,11 +14,11 @@ class EstatePropertyModel(models.Model): expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True) bedrooms = fields.Integer(default=2) - living_area = fields.Integer() + living_area = fields.Integer(default=0) facades = fields.Integer() has_garage = fields.Boolean() has_garden = fields.Boolean() - garden_area = fields.Integer() + garden_area = fields.Integer(default=0) garden_orientation = fields.Selection( string='Garden Orientation', selection=[ @@ -26,6 +26,7 @@ class EstatePropertyModel(models.Model): ('west', 'West'), ('north', 'North'), ('south', 'South'), + ('', ''), ], ) state = fields.Selection( @@ -45,3 +46,21 @@ class EstatePropertyModel(models.Model): salesperson_id = fields.Many2one('res.users', string='Salesperson', index=True, default=lambda self: self.env.user) property_tags_ids = fields.Many2many("estate_property_tag", string="Tags") offers_ids = fields.One2many("estate_property_offer", "property_id", string="Offers") + total_area = fields.Integer(compute="_compute_total_area", readonly=True) + best_price = fields.Float(compute="_compute_best_price", string="Best Offer", readonly=True) + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.garden_area + record.living_area + + @api.depends("offers_ids") + def _compute_best_price(self): + for record in self: + if record.offers_ids: + record.best_price = max(record.offers_ids.mapped('price')) + + @api.onchange("has_garden") + def _onchange_has_garden(self): + self.garden_area = 10 if self.has_garden else 0 + self.garden_orientation = 'south' if self.has_garden else '' diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 0f8f9a2f589..7884f74f1d4 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 dateutil.relativedelta import relativedelta + +from odoo import api, fields, models class EstatePropertyOfferModel(models.Model): @@ -15,3 +17,14 @@ class EstatePropertyOfferModel(models.Model): ) partner_id = fields.Many2one("res.partner", string="Buyer") property_id = fields.Many2one("estate_property", string="Property") + validity = fields.Integer(default=7) + date_deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline") + + @api.depends("validity") + def _compute_deadline(self): + for record in self: + record.date_deadline = record.create_date + relativedelta(days=record.validity) + + def _inverse_deadline(self): + for record in self: + record.validity = (record.date_deadline - record.create_date.date()).days diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 4e0094ab13f..919100d84a2 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -54,12 +54,14 @@ - - + + + + From 85e2b75459d92eb306878e7b5b970e37f5a46626 Mon Sep 17 00:00:00 2001 From: Delvaux-Jean-Baptiste Date: Thu, 17 Sep 2026 15:40:28 +0200 Subject: [PATCH 13/15] [IMP] estate: Added Cancel and Sold buttons to estate Added the accept offer and refuse offer to offers --- estate/models/estate_property.py | 19 ++++++++++++++ estate/models/estate_property_offer.py | 26 +++++++++++++++++++- estate/views/estate_property_offer_views.xml | 2 ++ estate/views/estate_property_views.xml | 5 ++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 11e80fe5ed7..b15b5da5283 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,7 @@ from dateutil.relativedelta import relativedelta from odoo import api, fields, models +from odoo.exceptions import UserError class EstatePropertyModel(models.Model): @@ -59,8 +60,26 @@ def _compute_best_price(self): for record in self: if record.offers_ids: record.best_price = max(record.offers_ids.mapped('price')) + else: + record.best_price = 0 @api.onchange("has_garden") def _onchange_has_garden(self): self.garden_area = 10 if self.has_garden else 0 self.garden_orientation = 'south' if self.has_garden else '' + + def action_sell_property(self): + for record in self: + if self.state == 'cancelled': + UserError("Cannot sell a cancelled property") + return False + self.state = 'sold' + return True + + def action_cancel_property(self): + for record in self: + if self.state == 'sold': + UserError("Cannot cancel a sold property") + return False + self.state = 'cancelled' + return True diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 7884f74f1d4..716e1e0496e 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,3 +1,5 @@ +from datetime import datetime + from dateutil.relativedelta import relativedelta from odoo import api, fields, models @@ -23,8 +25,30 @@ class EstatePropertyOfferModel(models.Model): @api.depends("validity") def _compute_deadline(self): for record in self: - record.date_deadline = record.create_date + relativedelta(days=record.validity) + if record.create: + record.date_deadline = datetime.now() + relativedelta(days=record.validity) + else: + record.date_deadline = record.create_date.date() + relativedelta(days=record.validity) def _inverse_deadline(self): for record in self: record.validity = (record.date_deadline - record.create_date.date()).days + + @api.depends("property_id") + def action_accept_offer(self): + for record in self: + if self.status == "accepted" or self.property_id.state in ['cancelled', 'sold', 'offer_accepted']: + return False + self.property_id.selling_price = self.price + self.property_id.state = 'offer_accepted' + self.property_id.buyer_id = self.partner_id + self.status = 'accepted' + return True + + @api.depends("property_id") + def action_refuse_offer(self): + for record in self: + if self.property_id.state in ['cancelled', 'sold']: + return False + self.status = 'refused' + return True diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 29e2a212789..820eff413fc 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -9,6 +9,8 @@ +