From 98643d9e31ebe033a58deb227608cd2897f7dd7a Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Tue, 18 Nov 2025 13:23:07 +0100 Subject: [PATCH 01/19] [ADD] estate: Init the module --- estate/__init__.py | 0 estate/__manifest__.py | 12 ++++++++++++ estate/models/testmodels.py | 5 +++++ 3 files changed, 17 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py create mode 100644 estate/models/testmodels.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..2b31f70b133 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,12 @@ +{ + 'name': "My Awesome module", + 'version': '1.0', + 'depends': ['base'], + 'author': "roole", + 'application': True, + 'installable': True, + 'category': 'Tutorials', + 'description': """ + Description text + """, +} \ No newline at end of file diff --git a/estate/models/testmodels.py b/estate/models/testmodels.py new file mode 100644 index 00000000000..c4d05492bab --- /dev/null +++ b/estate/models/testmodels.py @@ -0,0 +1,5 @@ +from odoo import models + +class TestModel(models.Model): + _name = "test_model" + _description = "CRM Recurring revenue plans" \ No newline at end of file From bcaa21c2506f193aa5c6c557ff8a26510ce2bb8c Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Tue, 18 Nov 2025 13:23:42 +0100 Subject: [PATCH 02/19] [IMP] estate: manifest changed Changed the manifest to test the 'git commit' env --- estate/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 2b31f70b133..dfb21e68833 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,5 +1,5 @@ { - 'name': "My Awesome module", + 'name': "My Awesome module Roole", 'version': '1.0', 'depends': ['base'], 'author': "roole", From c0d3abfe6abb7f6ddb37f514d1000377a09a4e94 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Tue, 18 Nov 2025 14:22:31 +0100 Subject: [PATCH 03/19] [IMP] estate: Chapter 3 Chapter 3 finished. --- estate/__init__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_model.py | 22 ++++++++++++++++++++++ estate/models/testmodels.py | 5 ----- 4 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_model.py delete mode 100644 estate/models/testmodels.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..447940a6610 --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_model \ No newline at end of file diff --git a/estate/models/estate_model.py b/estate/models/estate_model.py new file mode 100644 index 00000000000..6267531f5fc --- /dev/null +++ b/estate/models/estate_model.py @@ -0,0 +1,22 @@ +from odoo import fields, models + +class EstateModel(models.Model): + _name = "estate.property" + _description = "Estate description" + _property = "property" + 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( + string='Orientation', + selection=[('north', 'North'), ('south', 'South'), ('west', 'West'), ('east', 'East')] + ) \ No newline at end of file diff --git a/estate/models/testmodels.py b/estate/models/testmodels.py deleted file mode 100644 index c4d05492bab..00000000000 --- a/estate/models/testmodels.py +++ /dev/null @@ -1,5 +0,0 @@ -from odoo import models - -class TestModel(models.Model): - _name = "test_model" - _description = "CRM Recurring revenue plans" \ No newline at end of file From fcc0b5455044d1765491d6e9a7c58a08088b227c Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Tue, 18 Nov 2025 15:30:28 +0100 Subject: [PATCH 04/19] [IMP] estate: Chapter 3 security - the ir.model.access.csv have been created - read, write, create and unlink permissions have been given to the group base.group_user. --- estate/__init__.py | 2 +- estate/__manifest__.py | 5 ++++- estate/models/__init__.py | 2 +- estate/models/estate_model.py | 6 +++--- estate/security/ir.model.access.csv | 2 ++ 5 files changed, 11 insertions(+), 6 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 dfb21e68833..dadbc3ea8ec 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -9,4 +9,7 @@ 'description': """ Description text """, -} \ 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 447940a6610..9779334eb53 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1 @@ -from . import estate_model \ No newline at end of file +from . import estate_model diff --git a/estate/models/estate_model.py b/estate/models/estate_model.py index 6267531f5fc..6436a551e1f 100644 --- a/estate/models/estate_model.py +++ b/estate/models/estate_model.py @@ -1,5 +1,6 @@ from odoo import fields, models + class EstateModel(models.Model): _name = "estate.property" _description = "Estate description" @@ -16,7 +17,6 @@ class EstateModel(models.Model): garage = fields.Boolean() garden = fields.Boolean() garden_area = fields.Integer() - garden_orientation = fields.Selection( + garden_orientation = fields.Selection( string='Orientation', - selection=[('north', 'North'), ('south', 'South'), ('west', 'West'), ('east', 'East')] - ) \ No newline at end of file + selection=[('north', 'North'), ('south', 'South'), ('west', 'West'), ('east', 'East')]) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..4412040c323 --- /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_user,estate.property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file From e0c8d4184f7c941dc085a5f9881d415d755d9f59 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Tue, 18 Nov 2025 16:16:21 +0100 Subject: [PATCH 05/19] [FIX] estate: Fixing for the Green box Fixing manifest with license --- estate/__manifest__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index dadbc3ea8ec..260e40dc363 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -9,6 +9,7 @@ 'description': """ Description text """, + 'license': 'GPL-3', 'data': [ 'security/ir.model.access.csv' ] From 8d8136954477a6787da848897c9e69de105dcb11 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Tue, 18 Nov 2025 17:05:28 +0100 Subject: [PATCH 06/19] [IMP] estate : Chapter 5 - an action have been loaded in the system. - Property view created. - view Menu added. - Extra attributes have been added. --- estate/__manifest__.py | 4 +++- estate/models/estate_model.py | 17 ++++++++++++++--- estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 4 files changed, 33 insertions(+), 4 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 260e40dc363..6f869c9795b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -11,6 +11,8 @@ """, 'license': 'GPL-3', 'data': [ - 'security/ir.model.access.csv' + 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml' ] } diff --git a/estate/models/estate_model.py b/estate/models/estate_model.py index 6436a551e1f..122d8aa10c7 100644 --- a/estate/models/estate_model.py +++ b/estate/models/estate_model.py @@ -1,3 +1,5 @@ +import datetime + from odoo import fields, models @@ -8,10 +10,10 @@ class EstateModel(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date(copy=False,default=datetime.datetime.now() + datetime.timedelta(days=90)) 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() @@ -20,3 +22,12 @@ class EstateModel(models.Model): garden_orientation = fields.Selection( string='Orientation', selection=[('north', 'North'), ('south', 'South'), ('west', 'West'), ('east', 'East')]) + active = fields.Boolean(default=True) + state = fields.Selection( + string="Estate status", + selection=[('new', 'New'), ('offer received', 'Offer Received'), ('offer accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')], + help='This field explain the estate status.', + required=True, + copy=False, + default='new' + ) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..1b812791c58 --- /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..b50f7f925dd --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Test properties + estate.property + list,form + + \ No newline at end of file From 975c2806add5e6db6109827b704e62d08443f459 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Tue, 18 Nov 2025 17:18:31 +0100 Subject: [PATCH 07/19] [FIX] estate: PEP8 correction --- estate/models/estate_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_model.py b/estate/models/estate_model.py index 122d8aa10c7..c475f746823 100644 --- a/estate/models/estate_model.py +++ b/estate/models/estate_model.py @@ -10,9 +10,9 @@ class EstateModel(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date(copy=False,default=datetime.datetime.now() + datetime.timedelta(days=90)) + date_availability = fields.Date(copy=False, default=datetime.datetime.now() + datetime.timedelta(days=90)) expected_price = fields.Float(required=True) - selling_price = fields.Float(readonly=True,copy=False) + selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() From 2db9f2eb330508c025c459f45942b7a0b1b9e71a Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Wed, 19 Nov 2025 10:47:54 +0100 Subject: [PATCH 08/19] [IMP] estate: Chapter 6 - Search + filters added - Enhanced form view - Added ugly column in the view --- estate/views/estate_menus.xml | 2 +- estate/views/estate_property_views.xml | 96 ++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 1b812791c58..704d686b462 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -5,4 +5,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index b50f7f925dd..21e607fa68f 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,8 +1,90 @@ - - - Test properties - estate.property - list,form - - \ No newline at end of file + + + Estate properties + estate.property + list,form + + + + + + estate.property.list + estate.property + + + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ + +
+

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + estate.property.search + estate.property + + + + + + + + + + + + + + +
From 34dff5f68de6e3fbd99851740912199610d057ca Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Wed, 19 Nov 2025 13:53:01 +0100 Subject: [PATCH 09/19] [IMP] estate : Chapter 7 - review remarks applied - implemented the chapter 7 --- estate/__manifest__.py | 10 ++-- estate/models/__init__.py | 5 +- estate/models/estate_model.py | 33 ------------- estate/models/estate_offer.py | 17 +++++++ estate/models/estate_property.py | 50 ++++++++++++++++++++ estate/models/estate_tag.py | 8 ++++ estate/models/estate_type.py | 8 ++++ estate/security/ir.model.access.csv | 5 +- estate/views/estate_menus.xml | 6 ++- estate/views/estate_property_offer_views.xml | 35 ++++++++++++++ estate/views/estate_property_tag_views.xml | 26 ++++++++++ estate/views/estate_property_type_views.xml | 26 ++++++++++ estate/views/estate_property_views.xml | 34 ++++++++----- 13 files changed, 210 insertions(+), 53 deletions(-) delete mode 100644 estate/models/estate_model.py create mode 100644 estate/models/estate_offer.py create mode 100644 estate/models/estate_property.py create mode 100644 estate/models/estate_tag.py create mode 100644 estate/models/estate_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 6f869c9795b..1fcd61f2fb9 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,17 +2,15 @@ 'name': "My Awesome module Roole", 'version': '1.0', 'depends': ['base'], - 'author': "roole", + 'author': "Odoo S.A.", 'application': True, - 'installable': True, 'category': 'Tutorials', - 'description': """ - Description text - """, - 'license': 'GPL-3', + 'license': 'LGPL-3', 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', 'views/estate_menus.xml' ] } diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 9779334eb53..8d8063038f5 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ -from . import estate_model +from . import estate_property +from . import estate_type +from . import estate_tag +from . import estate_offer diff --git a/estate/models/estate_model.py b/estate/models/estate_model.py deleted file mode 100644 index c475f746823..00000000000 --- a/estate/models/estate_model.py +++ /dev/null @@ -1,33 +0,0 @@ -import datetime - -from odoo import fields, models - - -class EstateModel(models.Model): - _name = "estate.property" - _description = "Estate description" - _property = "property" - name = fields.Char(required=True) - description = fields.Text() - postcode = fields.Char() - date_availability = fields.Date(copy=False, default=datetime.datetime.now() + datetime.timedelta(days=90)) - expected_price = fields.Float(required=True) - selling_price = fields.Float(readonly=True, copy=False) - bedrooms = fields.Integer(default=2) - living_area = fields.Integer() - facades = fields.Integer() - garage = fields.Boolean() - garden = fields.Boolean() - garden_area = fields.Integer() - garden_orientation = fields.Selection( - string='Orientation', - selection=[('north', 'North'), ('south', 'South'), ('west', 'West'), ('east', 'East')]) - active = fields.Boolean(default=True) - state = fields.Selection( - string="Estate status", - selection=[('new', 'New'), ('offer received', 'Offer Received'), ('offer accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')], - help='This field explain the estate status.', - required=True, - copy=False, - default='new' - ) diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py new file mode 100644 index 00000000000..7826a9b504b --- /dev/null +++ b/estate/models/estate_offer.py @@ -0,0 +1,17 @@ +from odoo import fields, models + + +class EstateOffer(models.Model): + _name = "estate.property.offer" + _description = "property offer" + + price = fields.Float(string='Price') + partner_id = fields.Many2one('res.partner', required=True, string='Partner') + property_id = fields.Many2one('estate.property', required=True, string='Property') + status = fields.Selection( + string='Status', + copy=False, + selection=[ + ('accepted', 'Accepted'), + ('refused', 'Refused')] + ) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..30eb06121dd --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,50 @@ +from odoo import fields, models + + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Estate description" + + name = fields.Char(required=True) + tag_ids = fields.Many2many("estate.property.tag", string="Tags") + property_type_id = fields.Many2one("estate.property.type", string="Type") + user_id = fields.Many2one('res.users', string='Salesman', index=True, default=lambda self: self.env.user) + buyer_id = fields.Many2one('res.partner', string='Buyer', index=True) + offer_ids = fields.One2many('estate.property.offer',inverse_name='property_id', string="Offers") + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date( + copy=False, + default=fields.Date.add(fields.Date.today(), months=3), + string="Available from", + ) + expected_price = fields.Float(required=True) + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default=2) + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection( + string='Orientation', + selection=[ + ('north', 'North'), + ('south', 'South'), + ('west', 'West'), + ('east', 'East') + ]) + active = fields.Boolean(default=True) + state = fields.Selection( + string="Estate status", + selection=[('new', 'New'), + ('offer_received', 'Offer Received'), + ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled') + ], + help='This field explain the estate status.', + required=True, + copy=False, + default='new' + ) diff --git a/estate/models/estate_tag.py b/estate/models/estate_tag.py new file mode 100644 index 00000000000..5c9699876cf --- /dev/null +++ b/estate/models/estate_tag.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstateTag(models.Model): + _name = "estate.property.tag" + _description = "property tag" + + name = fields.Char(required=True) diff --git a/estate/models/estate_type.py b/estate/models/estate_type.py new file mode 100644 index 00000000000..b8b237cb51c --- /dev/null +++ b/estate/models/estate_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstateType(models.Model): + _name = "estate.property.type" + _description = "property type" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 4412040c323..818b1b8a947 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_user,estate.property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_user,estate.property,model_estate_property,base.group_user,1,1,1,1 +access_estate_type_user,estate.property.type,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_tag_user,estate.property.tag,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_offer_user,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 704d686b462..fddc8772372 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,8 +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..1acd7b7caf4 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,35 @@ + + + + + + estate.property.offer.list + estate.offer + + + + + + + + + + + + + estate.property.offer.form + estate.property.type + +
+ + + + + + + + +
+
+
+
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..538b4d8ec6b --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,26 @@ + + + + Estate Tags + estate.property.tag + list,form + + + + + + + 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..3697369340b --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,26 @@ + + + + Estate Types + estate.property.type + list,form + + + + + + + estate.property.type.form + estate.property.type + +
+ + +

+
+
+
+
+
+ +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 21e607fa68f..eb41d717498 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -13,19 +13,19 @@ estate.property - - - - - - - + + + + + + + - + estate.property.form estate.property @@ -33,13 +33,13 @@
-
-

-
+

+
+ @@ -62,6 +62,18 @@ + + + + + + + + + + + +
From 8e360b3a40e6d6b5020ff3f1af545215b738ae42 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Wed, 19 Nov 2025 15:07:27 +0100 Subject: [PATCH 10/19] [FIX] estate : Fixed Chapter 7 - Added need file to views - Fixed xmls --- estate/__manifest__.py | 5 +++-- estate/models/estate_offer.py | 8 +++----- estate/models/estate_property.py | 9 +++++---- estate/views/estate_property_offer_views.xml | 4 ++-- estate/views/estate_property_views.xml | 18 +++++++----------- 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 1fcd61f2fb9..01a13be2e78 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -11,6 +11,7 @@ 'views/estate_property_views.xml', 'views/estate_property_type_views.xml', 'views/estate_property_tag_views.xml', - 'views/estate_menus.xml' - ] + 'views/estate_property_offer_views.xml', + 'views/estate_menus.xml', + ], } diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 7826a9b504b..0a02ce320a0 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -10,8 +10,6 @@ class EstateOffer(models.Model): property_id = fields.Many2one('estate.property', required=True, string='Property') status = fields.Selection( string='Status', - copy=False, - selection=[ - ('accepted', 'Accepted'), - ('refused', 'Refused')] - ) + copy=False, + selection=[('accepted', 'Accepted'), ('refused', 'Refused')], + ) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 30eb06121dd..8cae5c25377 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -6,7 +6,7 @@ class EstateProperty(models.Model): _description = "Estate description" name = fields.Char(required=True) - tag_ids = fields.Many2many("estate.property.tag", string="Tags") + tag_ids = fields.Many2many("estate.property.tag") property_type_id = fields.Many2one("estate.property.type", string="Type") user_id = fields.Many2one('res.users', string='Salesman', index=True, default=lambda self: self.env.user) buyer_id = fields.Many2one('res.partner', string='Buyer', index=True) @@ -37,14 +37,15 @@ class EstateProperty(models.Model): active = fields.Boolean(default=True) state = fields.Selection( string="Estate status", - selection=[('new', 'New'), + selection=[ + ('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), - ('cancelled', 'Cancelled') + ('cancelled', 'Cancelled'), ], help='This field explain the estate status.', required=True, copy=False, - default='new' + default='new', ) diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 1acd7b7caf4..8074b84f97b 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -4,7 +4,7 @@ estate.property.offer.list - estate.offer + estate.property.offer @@ -18,7 +18,7 @@ estate.property.offer.form - estate.property.type + estate.property.offer
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index eb41d717498..110eb9f0c14 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -32,11 +32,8 @@ - -

- -
- +

+ @@ -64,15 +61,14 @@ - - - - + - - + + + +
From 0593194a2750b38a3477b7b722a88ad203744da9 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Wed, 19 Nov 2025 17:29:59 +0100 Subject: [PATCH 11/19] [IMP] estate : Chapter 8 Implemented the chapter 8 --- estate/models/estate_offer.py | 19 +++++++++++++- estate/models/estate_property.py | 27 +++++++++++++++++++- estate/views/estate_property_offer_views.xml | 22 ++-------------- estate/views/estate_property_tag_views.xml | 4 --- estate/views/estate_property_type_views.xml | 3 --- estate/views/estate_property_views.xml | 10 +++----- 6 files changed, 49 insertions(+), 36 deletions(-) diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 0a02ce320a0..5b1584616a2 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import fields, models, api class EstateOffer(models.Model): @@ -8,8 +8,25 @@ class EstateOffer(models.Model): price = fields.Float(string='Price') partner_id = fields.Many2one('res.partner', required=True, string='Partner') property_id = fields.Many2one('estate.property', required=True, string='Property') + date_creation = fields.Date(readonly=True, default=fields.Date.today) status = fields.Selection( string='Status', copy=False, selection=[('accepted', 'Accepted'), ('refused', 'Refused')], ) + validity = fields.Integer(default=7) + date_deadline = fields.Date( + string='Deadline', compute="_compute_deadline", inverse="_inverse_deadline" + ) + + @api.depends('validity') + def _compute_deadline(self): + for record in self: + record.date_deadline = fields.Date.add( + fields.Date.today(), days=record.validity + ) + + @api.onchange('date_deadline') + def _inverse_deadline(self): + for record in self: + record.validity = (record.date_deadline - fields.Date.today()).days diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 8cae5c25377..9f56ac71b9c 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import fields, models, api class EstateProperty(models.Model): @@ -25,7 +25,9 @@ class EstateProperty(models.Model): facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() + total_area = fields.Integer(compute="_compute_total_area") garden_area = fields.Integer() + best_price = fields.Float(compute="_compute_best_price") garden_orientation = fields.Selection( string='Orientation', selection=[ @@ -49,3 +51,26 @@ class EstateProperty(models.Model): copy=False, default='new', ) + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for record in self: + if len(record.offer_ids) > 0: + record.best_price = max(record.offer_ids.mapped("price")) + else: + record.best_price = 0.0 + + @api.onchange('garden') + def _inverse_garden(self): + for record in self: + if record.garden: + record.garden_orientation = 'north' + record.garden_area = 10 + else: + record.garden_orientation = '' + record.garden_area = 0 diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 8074b84f97b..f1f6733f651 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -1,6 +1,5 @@ - estate.property.offer.list @@ -10,26 +9,9 @@ + + - - - - - 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 index 538b4d8ec6b..6cc61dcd145 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -5,9 +5,6 @@ estate.property.tag list,form
- - - estate.property.tag.form @@ -22,5 +19,4 @@
- diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index 3697369340b..e1d8cf89641 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -5,9 +5,6 @@ estate.property.type list,form
- - - estate.property.type.form diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 110eb9f0c14..54adde69a40 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,8 +5,6 @@ estate.property list,form - - estate.property.list @@ -23,8 +21,6 @@ - - estate.property.form @@ -42,6 +38,7 @@ + @@ -57,6 +54,7 @@ + @@ -75,8 +73,6 @@ - - estate.property.search @@ -91,7 +87,7 @@ - + From b51f5686103f9c99936ba566130c36acc9f8a933 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Thu, 20 Nov 2025 09:32:11 +0100 Subject: [PATCH 12/19] [FIX] estate : check style correction - correction of the estate_property style PEP8 --- 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 9f56ac71b9c..c4d5a32752f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -10,11 +10,13 @@ class EstateProperty(models.Model): property_type_id = fields.Many2one("estate.property.type", string="Type") user_id = fields.Many2one('res.users', string='Salesman', index=True, default=lambda self: self.env.user) buyer_id = fields.Many2one('res.partner', string='Buyer', index=True) - offer_ids = fields.One2many('estate.property.offer',inverse_name='property_id', string="Offers") + offer_ids = fields.One2many( + 'estate.property.offer', inverse_name='property_id', string="Offers" + ) description = fields.Text() postcode = fields.Char() date_availability = fields.Date( - copy=False, + copy=False, default=fields.Date.add(fields.Date.today(), months=3), string="Available from", ) From eb4b23ba2970ba62ad672a4ac0fab8e9c551849c Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Thu, 20 Nov 2025 11:30:10 +0100 Subject: [PATCH 13/19] [IMP] estate : Chapter 9 - Chapter 9, button implementation --- .gitignore | 3 +++ estate/models/estate_offer.py | 17 +++++++++++++++++ estate/models/estate_property.py | 17 +++++++++++++++++ estate/views/estate_property_offer_views.xml | 4 +++- estate/views/estate_property_views.xml | 6 ++++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b6e47617de1..87a4106c2c9 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,6 @@ dmypy.json # Pyre type checker .pyre/ + +#vscode env +.vscode/ \ No newline at end of file diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 5b1584616a2..bf98ed922b1 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -30,3 +30,20 @@ def _compute_deadline(self): def _inverse_deadline(self): for record in self: record.validity = (record.date_deadline - fields.Date.today()).days + + def action_accept(self): + for record in self: + for offers in record.property_id.offer_ids: + offers.status = "refused" + record.status = "accepted" + record.property_id.selling_price = record.price + record.property_id.buyer_id = record.partner_id + return True + + def action_refuse(self): + for record in self: + if record.status == "accepted": + record.property_id.selling_price = 0.0 + record.property_id.buyer_id = None + record.status = "refused" + return True diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c4d5a32752f..e158716fdc6 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import fields, models, api +from odoo.exceptions import UserError class EstateProperty(models.Model): @@ -76,3 +77,19 @@ def _inverse_garden(self): else: record.garden_orientation = '' record.garden_area = 0 + + def action_sold(self): + for record in self: + if record.state == "canceled": + raise UserError("Canceled property can't be sold.") + else: + record.state = "sold" + return True + + def action_cancel(self): + for record in self: + if record.state == "sold": + raise UserError("Sold property can't be canceled.") + else: + record.state = "canceled" + return True \ No newline at end of file diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index f1f6733f651..5fe3e2dc5ab 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -8,9 +8,11 @@ - + + -

-
+ + + + + + + + + + +
- + + + estate.property.type.list + estate.property.type + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index b71ddbadfb6..52826171daf 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,16 +1,11 @@ - - Estate properties - estate.property - list,form - estate.property.list estate.property - + @@ -28,18 +23,17 @@
- -

- + - + @@ -59,14 +53,14 @@ - - + + - + @@ -90,12 +84,18 @@ - + - - + + + + Estate properties + estate.property + list,form + {"search_default_active": True} + From 6c79c9327551de807b0b0fdbef354f02ab67dc7b Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Fri, 21 Nov 2025 12:16:31 +0100 Subject: [PATCH 16/19] [FIX] estate : fixed stored attribut name --- estate/models/estate_offer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 2a9cbc8d5d0..6d988527337 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -20,7 +20,7 @@ class EstateOffer(models.Model): date_deadline = fields.Date( string='Deadline', compute="_compute_deadline", inverse="_inverse_deadline" ) - property_type_id = fields.Many2one(related="property_id.property_type_id", store=True) + property_type_id = fields.Many2one(related="property_id.property_type_id", stored=True) _check_price = models.Constraint( 'CHECK(price >= 0)', 'The offer price must be positive' From f0f7842c77245bc2877dd78bcb54e7aff05fd994 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Fri, 21 Nov 2025 14:27:30 +0100 Subject: [PATCH 17/19] [IMP] estate : Chapter 12 --- estate/models/__init__.py | 1 + estate/models/estate_offer.py | 9 +++++++++ estate/models/estate_property.py | 7 +++++++ estate/models/estate_user_inherited.py | 8 ++++++++ 4 files changed, 25 insertions(+) create mode 100644 estate/models/estate_user_inherited.py diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 8d8063038f5..443781a52f3 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -2,3 +2,4 @@ from . import estate_type from . import estate_tag from . import estate_offer +from . import estate_user_inherited diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 6d988527337..39b03713548 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -63,3 +63,12 @@ def _unlink_offer(self): for record in self: if record.status == "accepted": raise UserError("You can't delete an accepted offer.") + + @api.model + def create(self, vals): + for val in vals: + best_offer = self.env['estate.property'].browse(val["property_id"]).best_price + if val['price'] < best_offer: + raise UserError(f"Impossible to add an offer lower than {best_offer}.") + return super().create(vals) + diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 7bad2d8a0d3..c0c00d0b108 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -111,6 +111,7 @@ def _check_selling_price(self): raise ValidationError( "The selling price cannot be lower than 90% of the expected price." ) + @api.onchange('offer_ids') def _onchange_offers(self): for record in self: @@ -118,3 +119,9 @@ def _onchange_offers(self): record.state = "new" else: record.state = "offer_received" + + @api.ondelete(at_uninstall=False) + def _unlink_property(self): + for record in self: + if record.state not in ["new","cancelled"]: + raise UserError("You can only delete new or cancelled properties.") diff --git a/estate/models/estate_user_inherited.py b/estate/models/estate_user_inherited.py new file mode 100644 index 00000000000..2727d07198c --- /dev/null +++ b/estate/models/estate_user_inherited.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class InheritedEstateUser(models.Model): + _name = ["estate.property.user"] + _inherit = ["res.users"] + + property_ids = fields.One2many("estate.property" , inverse="user_id", domain="['|', ('state', '=', 'new' ),('state', '=', 'offer_received' )]") From f061d137d8d0cccadc39acdd325284f5ac618da9 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Fri, 21 Nov 2025 14:43:37 +0100 Subject: [PATCH 18/19] [FIX] estate : fix chapter 12 --- estate/models/__init__.py | 1 - estate/models/estate_offer.py | 1 - estate/models/estate_property.py | 2 +- estate/models/estate_user_inherited.py | 8 -------- 4 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 estate/models/estate_user_inherited.py diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 443781a52f3..8d8063038f5 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -2,4 +2,3 @@ from . import estate_type from . import estate_tag from . import estate_offer -from . import estate_user_inherited diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 39b03713548..7394984261b 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -71,4 +71,3 @@ def create(self, vals): if val['price'] < best_offer: raise UserError(f"Impossible to add an offer lower than {best_offer}.") return super().create(vals) - diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c0c00d0b108..285ec22af37 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -123,5 +123,5 @@ def _onchange_offers(self): @api.ondelete(at_uninstall=False) def _unlink_property(self): for record in self: - if record.state not in ["new","cancelled"]: + if record.state not in ["new", "cancelled"]: raise UserError("You can only delete new or cancelled properties.") diff --git a/estate/models/estate_user_inherited.py b/estate/models/estate_user_inherited.py deleted file mode 100644 index 2727d07198c..00000000000 --- a/estate/models/estate_user_inherited.py +++ /dev/null @@ -1,8 +0,0 @@ -from odoo import fields, models - - -class InheritedEstateUser(models.Model): - _name = ["estate.property.user"] - _inherit = ["res.users"] - - property_ids = fields.One2many("estate.property" , inverse="user_id", domain="['|', ('state', '=', 'new' ),('state', '=', 'offer_received' )]") From d147bb8afe56fa17d445f2a717864f7c1cd2d8c6 Mon Sep 17 00:00:00 2001 From: roole-odoo Date: Fri, 21 Nov 2025 14:58:59 +0100 Subject: [PATCH 19/19] [FIX] estate : Chapter 12 fix --- estate/models/estate_offer.py | 2 +- estate/models/estate_property.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 7394984261b..8d699ed588a 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -20,7 +20,7 @@ class EstateOffer(models.Model): date_deadline = fields.Date( string='Deadline', compute="_compute_deadline", inverse="_inverse_deadline" ) - property_type_id = fields.Many2one(related="property_id.property_type_id", stored=True) + property_type_id = fields.Many2one(related="property_id.property_type_id") _check_price = models.Constraint( 'CHECK(price >= 0)', 'The offer price must be positive' diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 285ec22af37..ec29a5f41a6 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -107,7 +107,7 @@ def action_cancel(self): def _check_selling_price(self): for record in self: if float_compare(record.selling_price, record.expected_price * 0.9, precision_digits=4) == -1 \ - and (self.state == "offer_accepted"): + and (record.state == "offer_accepted"): raise ValidationError( "The selling price cannot be lower than 90% of the expected price." )