From f8e1bc075e0ca92705039411821f54f727152629 Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Mon, 20 Oct 2025 13:47:30 +0200 Subject: [PATCH 01/25] [ADD] Estate:createthe app --- 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..37d1a3ce94c --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,4 @@ +{ + 'name': "Real Estate", + 'depends': ['base'], +} \ No newline at end of file From c558c6b5e87e4f74bf261520463859c987a4f697 Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Mon, 20 Oct 2025 14:08:12 +0200 Subject: [PATCH 02/25] [IMP] estate : set application to True --- estate/__manifest__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 37d1a3ce94c..6f9768b6650 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,4 +1,5 @@ { 'name': "Real Estate", 'depends': ['base'], + 'application' : True, } \ No newline at end of file From cd524969fcbef7ddfa58d095e7f4c4cf589ac992 Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Mon, 20 Oct 2025 15:14:49 +0200 Subject: [PATCH 03/25] [IMP] Estate : Chapter 3 --- estate/__init__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 33 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..0bb78047ec9 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,31 @@ +from odoo import models, fields + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Property for the Real Estate app" + + + #Adding fields + """ + id = fields.Integer(required=True,default=nextval('estate_property_id_seq'::regclass)) + create_uid = fields.Integer() + create_date = fields.Datetime() + write_uid = fields.Integer() + write_date = fields.Datetime() + """ + 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() + garder_area = fields.Integer() + garden_orientation = fields.Selection(selection=[("North", "north"), ("South", "south"), ("East", "east"), ("West", "west")]) + + + From 8cb8dc5ea5b94ba70c180bd52181b2b43d55a4c9 Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Mon, 20 Oct 2025 15:45:29 +0200 Subject: [PATCH 04/25] [IMP] estate : add security model --- estate/__manifest__.py | 3 +++ estate/models/estate_property.py | 7 ------- estate/security/ir.model.access.csv | 2 ++ 3 files changed, 5 insertions(+), 7 deletions(-) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 6f9768b6650..409f7411b2b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,4 +2,7 @@ 'name': "Real Estate", 'depends': ['base'], 'application' : True, + 'data' : [ + 'security/ir.model.access.csv' + ], } \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0bb78047ec9..f9466d4c3b5 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -6,13 +6,6 @@ class EstateProperty(models.Model): #Adding fields - """ - id = fields.Integer(required=True,default=nextval('estate_property_id_seq'::regclass)) - create_uid = fields.Integer() - create_date = fields.Datetime() - write_uid = fields.Integer() - write_date = fields.Datetime() - """ name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..e57ec5e4eb5 --- /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 +estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,0,0,0 \ No newline at end of file From a0182ea327d77e4c1b34ad1180d3de85f6c8726e Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Mon, 20 Oct 2025 17:13:29 +0200 Subject: [PATCH 05/25] [IMP] estate : adding menus and fields --- estate/__init__.py | 2 +- estate/__manifest__.py | 4 +++- estate/models/__init__.py | 2 +- estate/models/estate_property.py | 15 ++++++++++----- estate/security/ir.model.access.csv | 2 +- estate/views/estate_menus.xml | 7 +++++++ estate/views/estate_property_views.xml | 8 ++++++++ 7 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_views.xml 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 409f7411b2b..c47bac0b6a1 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,6 +3,8 @@ 'depends': ['base'], 'application' : True, 'data' : [ + 'views/estate_property_views.xml', + 'views/estate_menus.xml', 'security/ir.model.access.csv' ], -} \ No newline at end of file +} 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 f9466d4c3b5..b67a051d2a5 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,24 +1,29 @@ from odoo import models, fields +import datetime +from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): _name = "estate.property" _description = "Property for the Real Estate app" + + #Adding fields name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date(copy=False, default=datetime.date.today()+relativedelta(months=+3)) expected_price = fields.Float(required=True) - selling_price = fields.Float() - bedrooms = fields.Integer() + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() garder_area = fields.Integer() - garden_orientation = fields.Selection(selection=[("North", "north"), ("South", "south"), ("East", "east"), ("West", "west")]) - + garden_orientation = fields.Selection(selection=[("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")]) + active = fields.Boolean(default=True) + state = fields.Selection(selection=[("new", "New"), ("offer_received", "Offer Received"), ("offer_accepted", "Offer Accepted"), ("sold", "Sold"), ("cancelled", "Cancelled")], copy=False, required=True, default="new") diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index e57ec5e4eb5..b5005de0d92 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,2 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,0,0,0 \ No newline at end of file +estate.access_estate_property,access_estate_property,model_estate_property,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 new file mode 100644 index 00000000000..b1db5117584 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..dfa0f81c03c --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Properties + estate.property + list,form + + From a44904569169f51b613753441ece637bd76f7c07 Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Tue, 21 Oct 2025 11:13:50 +0200 Subject: [PATCH 06/25] [IMP] estate : adding custom views (chapter 6) --- estate/models/estate_property.py | 2 +- estate/views/estate_property_views.xml | 75 ++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index b67a051d2a5..3740f331246 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -21,7 +21,7 @@ class EstateProperty(models.Model): facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() - garder_area = fields.Integer() + garden_area = fields.Integer() garden_orientation = fields.Selection(selection=[("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")]) active = fields.Boolean(default=True) state = fields.Selection(selection=[("new", "New"), ("offer_received", "Offer Received"), ("offer_accepted", "Offer Accepted"), ("sold", "Sold"), ("cancelled", "Cancelled")], copy=False, required=True, default="new") diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index dfa0f81c03c..6f03ab9927d 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,5 +1,80 @@ + + estate.property.form + estate.property + +
+ +
+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate/property.list + estate.property + + + + + + + + + + + + + + + estate.property.search + estate.property + + + + + + + + + + + + + + Properties estate.property From ded2b6640ee3fbce089ed9950ff366c48c53cddd Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Tue, 21 Oct 2025 13:54:20 +0200 Subject: [PATCH 07/25] [IMP] estate : adding types, tags and offers (chapter 7) --- estate/__init__.py | 1 + estate/__manifest__.py | 5 +++++ estate/models/__init__.py | 3 +++ estate/models/estate_property.py | 9 +++++--- estate/models/estate_property_offer.py | 11 ++++++++++ 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.xml | 6 +++++- estate/views/estate_property_offer_views.xml | 14 +++++++++++++ estate/views/estate_property_tag_views.xml | 8 +++++++ estate/views/estate_property_type_views.xml | 18 ++++++++++++++++ estate/views/estate_property_views.xml | 22 ++++++++++++++++---- 13 files changed, 109 insertions(+), 9 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/__init__.py b/estate/__init__.py index 0650744f6bc..899bcc97f0f 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1 +1,2 @@ from . import models + diff --git a/estate/__manifest__.py b/estate/__manifest__.py index c47bac0b6a1..537541b8a86 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,8 +2,13 @@ 'name': "Real Estate", 'depends': ['base'], 'application' : True, + 'author' : "Jeanne Delneste", + 'license' : "LGPL-3", 'data' : [ 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_menus.xml', 'security/ir.model.access.csv' ], diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ from . import estate_property +from . import estate_property_type +from . import estate_property_tag +from . import estate_property_offer diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 3740f331246..bee7a6e165c 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -5,9 +5,6 @@ class EstateProperty(models.Model): _name = "estate.property" _description = "Property for the Real Estate app" - - - #Adding fields name = fields.Char(required=True) @@ -25,5 +22,11 @@ class EstateProperty(models.Model): garden_orientation = fields.Selection(selection=[("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")]) active = fields.Boolean(default=True) state = fields.Selection(selection=[("new", "New"), ("offer_received", "Offer Received"), ("offer_accepted", "Offer Accepted"), ("sold", "Sold"), ("cancelled", "Cancelled")], copy=False, required=True, default="new") + property_type_id = fields.Many2one("estate.property.type", string="Type") + buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) + salesperson_id = fields.Many2one("res.users", string="Salesman", default=lambda self: self.env.user) + tag_ids = fields.Many2many("estate.property.tag", string="Tags") + offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..d7f62a67e6b --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,11 @@ +from odoo import models, fields + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Offers for properties" + + #Adding fields + price = fields.Float() + status = fields.Selection(copy=False, selection=[("accepted", "Accepted"), ("refused", "Refused")]) + partner_id = fields.Many2one("res.partner", required=True, string="Partner") + property_id = fields.Many2one("estate.property", required=True) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..f1f490c7c00 --- /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 = "Tag of properties" + + #Adding fields + name = fields.Char(required=True) \ No newline at end of file diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..65f33acda69 --- /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 = "Type of properties" + + #Adding fields + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index b5005de0d92..3ed9c675cd0 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 -estate.access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +estate.access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 +estate.access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 +estate.access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 +estate.access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index b1db5117584..93f214399b1 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,7 +1,11 @@ - + + + + + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..fe72d082445 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,14 @@ + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..3ef2b88ca66 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,8 @@ + + + + Property Tags + estate.property.tag + list + + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..3111c1180b0 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,18 @@ + + + + estate.property.type.list + estate.property.type + + + + + + + + + Property Types + estate.property.type + list + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 6f03ab9927d..b650e62ef72 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -9,9 +9,11 @@

+ + @@ -23,7 +25,6 @@ - @@ -36,6 +37,15 @@ + + + + + + + + + @@ -43,12 +53,14 @@
- estate/property.list + estate.property.list estate.property - - + + + + @@ -64,6 +76,7 @@ + @@ -71,6 +84,7 @@ + From 286c82843008facb70c3d3cc9821ba10fe2ed601 Mon Sep 17 00:00:00 2001 From: Mathilde Pascal Date: Tue, 21 Oct 2025 14:28:36 +0200 Subject: [PATCH 08/25] [IMP] estate: Add notes. --- estate/models/estate_property.py | 1 + estate/views/estate_property_views.xml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index bee7a6e165c..a1a43f707ee 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -10,6 +10,7 @@ class EstateProperty(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() + notes = fields.Html() date_availability = fields.Date(copy=False, default=datetime.date.today()+relativedelta(months=+3)) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index b650e62ef72..c1cca910a63 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -37,6 +37,9 @@ + + + From fa3f1eacbf23702cda39e14715b7c5214d2687fa Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Tue, 21 Oct 2025 15:37:09 +0200 Subject: [PATCH 09/25] [IMP] estate: adding computed fields and onchanges (chapter 8) --- estate/__init__.py | 1 - estate/__manifest__.py | 8 +++--- estate/models/estate_property.py | 26 +++++++++++++++++--- estate/models/estate_property_offer.py | 20 +++++++++++++-- estate/models/estate_property_tag.py | 4 +-- estate/models/estate_property_type.py | 2 +- estate/views/estate_menus.xml | 10 ++++---- estate/views/estate_property_offer_views.xml | 5 ++-- estate/views/estate_property_views.xml | 2 ++ 9 files changed, 58 insertions(+), 20 deletions(-) diff --git a/estate/__init__.py b/estate/__init__.py index 899bcc97f0f..0650744f6bc 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1,2 +1 @@ from . import models - diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 537541b8a86..ee3e7fd7190 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,10 +1,10 @@ { 'name': "Real Estate", 'depends': ['base'], - 'application' : True, - 'author' : "Jeanne Delneste", - 'license' : "LGPL-3", - 'data' : [ + 'application': True, + 'author': "Jeanne Delneste", + 'license': "LGPL-3", + 'data': [ 'views/estate_property_views.xml', 'views/estate_property_type_views.xml', 'views/estate_property_tag_views.xml', diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index bee7a6e165c..3dea4962c11 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,16 +1,16 @@ -from odoo import models, fields +from odoo import api, models, fields import datetime from dateutil.relativedelta import relativedelta + class EstateProperty(models.Model): _name = "estate.property" _description = "Property for the Real Estate app" - #Adding fields name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date(copy=False, default=datetime.date.today()+relativedelta(months=+3)) + date_availability = fields.Date(copy=False, default=datetime.date.today() + relativedelta(months=+3)) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) @@ -27,6 +27,26 @@ class EstateProperty(models.Model): salesperson_id = fields.Many2one("res.users", string="Salesman", default=lambda self: self.env.user) tag_ids = fields.Many2many("estate.property.tag", string="Tags") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + total_area = fields.Integer(compute="_compute_total_area", string="Total Area (sqm)") + best_price = fields.Float(compute = "_compute_best_offer", string="Best Offer") + + + @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_offer(self): + for record in self: + record.best_price = max(record.offer_ids.mapped("price")) + @api.onchange("garden") + def _onchange_garden(self): + if self.garden == True: + self.garden_area = 10 + self.garden_orientation = "north" + else: + self.garden_area = 0 + self.garden_orientation = None diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index d7f62a67e6b..9c63983d83d 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,11 +1,27 @@ -from odoo import models, fields +from odoo import api, models, fields +import datetime + class EstatePropertyOffer(models.Model): _name = "estate.property.offer" _description = "Offers for properties" - #Adding fields price = fields.Float() status = fields.Selection(copy=False, selection=[("accepted", "Accepted"), ("refused", "Refused")]) partner_id = fields.Many2one("res.partner", required=True, string="Partner") property_id = fields.Many2one("estate.property", required=True) + validity = fields.Integer(default=7, string="Validity (days)") + create_date = fields.Date(copy=False, default=lambda self : datetime.date.today(), readonly=True) + date_deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline", string="Deadline") + + + @api.depends("create_date", "validity") + def _compute_deadline(self): + for offer in self: + offer.date_deadline = fields.Date.add(offer.create_date, days=offer.validity) + + + def _inverse_deadline(self): + for offer in self: + delta = offer.date_deadline - offer.create_date + offer.validity = delta.days diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index f1f490c7c00..319ab36f90f 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -1,8 +1,8 @@ from odoo import models, fields + class EstatePropertyTag(models.Model): _name = "estate.property.tag" _description = "Tag of properties" - #Adding fields - name = fields.Char(required=True) \ No newline at end of file + name = fields.Char(required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 65f33acda69..2e2407b2b8b 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -1,8 +1,8 @@ from odoo import models, fields + class EstatePropertyType(models.Model): _name = "estate.property.type" _description = "Type of properties" - #Adding fields name = fields.Char(required=True) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 93f214399b1..4304372a1fe 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,11 +1,11 @@ - - + + - - - + + + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index fe72d082445..9d410d9ead6 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -7,8 +7,9 @@ - + + -
\ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index b650e62ef72..35d9b3bd471 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -19,6 +19,7 @@ + @@ -34,6 +35,7 @@ + From 2ff906fa007baa7124aeb5d26f671ba8ac3c95ec Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Tue, 21 Oct 2025 15:46:47 +0200 Subject: [PATCH 10/25] [IMP] estate: style corrections --- estate/models/estate_property.py | 8 +++----- estate/models/estate_property_offer.py | 4 +--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index b66219c7a66..b57b149baae 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -11,7 +11,7 @@ class EstateProperty(models.Model): description = fields.Text() postcode = fields.Char() notes = fields.Html() - date_availability = fields.Date(copy=False, default=datetime.date.today()+relativedelta(months=+3)) + date_availability = fields.Date(copy=False, default=datetime.date.today() + relativedelta(months=+3)) expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) @@ -29,9 +29,7 @@ class EstateProperty(models.Model): tag_ids = fields.Many2many("estate.property.tag", string="Tags") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") total_area = fields.Integer(compute="_compute_total_area", string="Total Area (sqm)") - best_price = fields.Float(compute = "_compute_best_offer", string="Best Offer") - - + best_price = fields.Float(compute="_compute_best_offer", string="Best Offer") @api.depends("living_area", "garden_area") def _compute_total_area(self): @@ -45,7 +43,7 @@ def _compute_best_offer(self): @api.onchange("garden") def _onchange_garden(self): - if self.garden == True: + if self.garden: self.garden_area = 10 self.garden_orientation = "north" else: diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 9c63983d83d..20cd63a390d 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -11,16 +11,14 @@ class EstatePropertyOffer(models.Model): partner_id = fields.Many2one("res.partner", required=True, string="Partner") property_id = fields.Many2one("estate.property", required=True) validity = fields.Integer(default=7, string="Validity (days)") - create_date = fields.Date(copy=False, default=lambda self : datetime.date.today(), readonly=True) + create_date = fields.Date(copy=False, default=lambda self: datetime.date.today(), readonly=True) date_deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline", string="Deadline") - @api.depends("create_date", "validity") def _compute_deadline(self): for offer in self: offer.date_deadline = fields.Date.add(offer.create_date, days=offer.validity) - def _inverse_deadline(self): for offer in self: delta = offer.date_deadline - offer.create_date From 67147af5470cd0ee247e9f27314ad575a954b19f Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Tue, 21 Oct 2025 15:56:50 +0200 Subject: [PATCH 11/25] [IMP] estate: tutorials corrections --- 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 b57b149baae..cb8173f6fa7 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -39,8 +39,10 @@ def _compute_total_area(self): @api.depends("offer_ids.price") def _compute_best_offer(self): for record in self: - record.best_price = max(record.offer_ids.mapped("price")) - + prices = record.offer_ids.mapped("price") + if len(prices) > 0: + record.best_price = max(prices) + @api.onchange("garden") def _onchange_garden(self): if self.garden: From a0a6f3999c3f792a08d2cd41b3ffef05d087db56 Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Tue, 21 Oct 2025 16:02:17 +0200 Subject: [PATCH 12/25] [IMP] estate: tutorials corrections --- estate/models/estate_property.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index cb8173f6fa7..c343ab92c69 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -29,7 +29,7 @@ class EstateProperty(models.Model): tag_ids = fields.Many2many("estate.property.tag", string="Tags") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") total_area = fields.Integer(compute="_compute_total_area", string="Total Area (sqm)") - best_price = fields.Float(compute="_compute_best_offer", string="Best Offer") + best_price = fields.Float(compute="_compute_best_offer", string="Best Offer", default=0) @api.depends("living_area", "garden_area") def _compute_total_area(self): @@ -42,7 +42,7 @@ def _compute_best_offer(self): prices = record.offer_ids.mapped("price") if len(prices) > 0: record.best_price = max(prices) - + @api.onchange("garden") def _onchange_garden(self): if self.garden: From abc4cdf8c7bc795dc75301ce3c95c440e1692719 Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Tue, 21 Oct 2025 16:56:27 +0200 Subject: [PATCH 13/25] [IMP] estate: adding buttons (chapter 9 --- estate/models/estate_property.py | 19 ++++++++++++++++++- estate/models/estate_property_offer.py | 14 ++++++++++++++ estate/views/estate_property_offer_views.xml | 9 ++++++--- estate/views/estate_property_views.xml | 4 ++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c343ab92c69..4925edccb81 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import api, models, fields +from odoo.exceptions import UserError import datetime from dateutil.relativedelta import relativedelta @@ -29,7 +30,8 @@ class EstateProperty(models.Model): tag_ids = fields.Many2many("estate.property.tag", string="Tags") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") total_area = fields.Integer(compute="_compute_total_area", string="Total Area (sqm)") - best_price = fields.Float(compute="_compute_best_offer", string="Best Offer", default=0) + best_price = fields.Float(compute="_compute_best_offer", string="Best Offer") + @api.depends("living_area", "garden_area") def _compute_total_area(self): @@ -42,6 +44,7 @@ def _compute_best_offer(self): prices = record.offer_ids.mapped("price") if len(prices) > 0: record.best_price = max(prices) + else:record.best_price = 0 @api.onchange("garden") def _onchange_garden(self): @@ -51,3 +54,17 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = None + + def sell_property(self): + for record in self: + if record.state == "cancelled": + raise UserError("Error - You cannot sell a cancelled property !") + record.state = "sold" + return True + + def cancel_property(self): + for record in self: + if record.state == "sold": + raise UserError("Error - You cannot cancel a sold property !") + record.state="cancelled" + return True diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 20cd63a390d..0c88d519870 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,5 @@ from odoo import api, models, fields +from odoo.exceptions import UserError import datetime @@ -23,3 +24,16 @@ def _inverse_deadline(self): for offer in self: delta = offer.date_deadline - offer.create_date offer.validity = delta.days + + def accept_offer(self): + for offer in self: + for other_offer in offer.property_id.offer_ids: + if other_offer.status == "accepted" and offer.id != other_offer.id: + raise UserError("An offer is already accepted...") + offer.status = "accepted" + offer.property_id.buyer_id = offer.partner_id + offer.property_id.selling_price = offer.price + + def refuse_offer(self): + for offer in self: + offer.status = "refused" diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 9d410d9ead6..b5a490bd12a 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -6,9 +6,12 @@ - - - + + + + + +
+

+
+ + + + + + + + + + + + + + +
+ + + Property Types estate.property.type - list + list,form diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 8f68143a1d3..44eee967533 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,23 +1,24 @@ - + estate.property.form estate.property
-

- + - + @@ -37,8 +38,8 @@ - - + + @@ -47,7 +48,7 @@ - + @@ -61,11 +62,11 @@ - + estate.property.list estate.property - + @@ -74,12 +75,12 @@ - + - + estate.property.search estate.property @@ -89,18 +90,19 @@ - + - + - + Properties estate.property list,form + {'search_default_available': True} From 6a76b10dc42871e622a99b8c60cb8feda8a035dc Mon Sep 17 00:00:00 2001 From: Jeanne Delneste Date: Wed, 22 Oct 2025 16:04:33 +0200 Subject: [PATCH 17/25] [IMP] estate: adding user inheritance (chapter 12) --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 6 ++++++ estate/models/estate_property_offer.py | 12 +++++++++++- estate/models/estate_property_user.py | 6 ++++++ estate/views/estate_property_type_views.xml | 2 +- estate/views/estate_property_user_views.xml | 18 ++++++++++++++++++ 7 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 estate/models/estate_property_user.py create mode 100644 estate/views/estate_property_user_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 58bd9839a23..82571e3467c 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,7 @@ 'license': "LGPL-3", 'data': [ 'views/estate_property_views.xml', + 'views/estate_property_user_views.xml', 'views/estate_property_offer_views.xml', 'views/estate_property_type_views.xml', 'views/estate_property_tag_views.xml', diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 2f1821a39c1..f0021fa8e22 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -2,3 +2,4 @@ from . import estate_property_type from . import estate_property_tag from . import estate_property_offer +from . import estate_property_user diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 428682ae654..8bc7628581f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -86,3 +86,9 @@ def _check_selling_price(self): if not float_is_zero(record.selling_price, precision_digits=3): if float_compare(record.selling_price, record.expected_price * 0.9, precision_digits=3) < 0: raise UserError(r"The selling price must be at least 90% of the expected price !") + + @api.ondelete(at_uninstall=False) + def _unlike_if_stats_new_or_cancelled(self): + for record in self: + if record.state in ('new', 'cancelled'): + raise UserError("You cannot delete a new or cancelled property !") \ No newline at end of file diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 2752a70b86e..563db66a0ca 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -28,7 +28,7 @@ def _compute_deadline(self): def _inverse_deadline(self): for offer in self: - delta = offer.date_deadline - offer.create_date + delta = offer.date_deadline - (fields.Date.to_date(offer.create_date) or fields.Date.to_date(datetime.date.today())) offer.validity = delta.days def accept_offer(self): @@ -44,3 +44,13 @@ def accept_offer(self): def refuse_offer(self): for offer in self: offer.status = "refused" + + @api.model_create_multi + def create(self, vals_list): + for val in vals_list: + offers = self.env['estate.property.offer'].search([('property_id', '=', val['property_id'])]) + if val['price'] < max(offers.mapped('price')): + raise UserError("You cannot create an offer with a lower amount than an existing offer !") + offers = super().create(vals_list) + offers.property_id.state = 'offer_received' + return offers diff --git a/estate/models/estate_property_user.py b/estate/models/estate_property_user.py new file mode 100644 index 00000000000..7c6dcc307e6 --- /dev/null +++ b/estate/models/estate_property_user.py @@ -0,0 +1,6 @@ +from odoo import fields, models + +class PropertyUser(models.Model): + _inherit = "res.users" + + property_ids = fields.One2many("estate.property", "salesperson_id", domain="[('state', 'in', ('new', 'offer_received'))]") diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index 43df69fe15a..ec29b77f0a6 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -21,7 +21,7 @@