From 4520620e32aeba8f9d02a8df10e56d915e686eb3 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 17 Aug 2026 14:16:16 +0200 Subject: [PATCH 01/21] [ADD] estate: add manifest and init for new module Based on the instructions from the tutorial's Chapter 2, the first steps for the new app for Real Estate management was achieved --- estate/__init__.py | 0 estate/__manifest__.py | 8 ++++++++ 2 files changed, 8 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..234e0373c37 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,8 @@ +{ + "name": "Estate", + "version": "1.0", + "summary": "Estate Management", + "author": "Arturo Yepez", + "depends": ["base"], + "application": True, +} \ No newline at end of file From 55fd4a4e59ef635584df7994defb83ad3facc261 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 17 Aug 2026 15:02:37 +0200 Subject: [PATCH 02/21] [ADD] estate: add new model for properties Based on instructions on tutorial's Chapter 3, it was added a modal to represent the Estate Properties that will be managed --- estate/__init__.py | 1 + estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 27 +++++++++++++++++++++++++++ 4 files changed, 30 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/__manifest__.py b/estate/__manifest__.py index 234e0373c37..f4c234da07c 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,4 +5,5 @@ "author": "Arturo Yepez", "depends": ["base"], "application": True, + "installable": True, } \ No newline at end of file diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..f4c8fd6db6d --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..b8815b4e51c --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,27 @@ +from odoo import fields, models + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Estate Property Management" + + name = fields.Char(string="Title", required=True) + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date(string="Available From", default=fields.Date.today) + 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="Garden Orientation", + selection=[ + ("north", "North"), + ("south", "South"), + ("east", "East"), + ("west", "West"), + ], + ) \ No newline at end of file From 66369214f5360129c68580e79d2b09a8a8021334 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 17 Aug 2026 15:35:06 +0200 Subject: [PATCH 03/21] [ADD] estate: add security rules to the estate base model Based on the tutorial's Chapter 4, some basic security rules were implemented to keep a more tight user access control --- estate/__manifest__.py | 3 +++ estate/security/ir.model.access.csv | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index f4c234da07c..c8863e45bc2 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,4 +6,7 @@ "depends": ["base"], "application": True, "installable": True, + "data": [ + 'security/ir.model.access.csv', + ] } \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..334031a21ca --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file From 8bdc6504177f0120029d442b8981a76065037aea Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Mon, 17 Aug 2026 17:00:05 +0200 Subject: [PATCH 04/21] [IMP] estate: update fields and create basic views for creation, manipulation Based on the tutorial's Chapter 5, several improvements were made to certain fields of the Estate Property model while also creating some customize basic views (as templates) to gain more control for future developments over the app --- estate/__manifest__.py | 3 +++ estate/models/estate_property.py | 27 ++++++++++++++++++++++---- estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 4 files changed, 42 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 c8863e45bc2..f266dde3f52 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -8,5 +8,8 @@ "installable": True, "data": [ 'security/ir.model.access.csv', + + 'views/estate_property_views.xml', + 'views/estate_menus.xml', ] } \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index b8815b4e51c..be9ff27ed13 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import fields, models +from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): _name = "estate.property" @@ -7,10 +8,13 @@ class EstateProperty(models.Model): name = fields.Char(string="Title", required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date(string="Available From", default=fields.Date.today) + date_availability = fields.Date( + string="Available From", + default=lambda _ : fields.Date.today() + relativedelta(months=3) + ) expected_price = fields.Float(required=True) - selling_price = fields.Float() - bedrooms = fields.Integer() + selling_price = fields.Float(readonly=True, copy=False) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() @@ -24,4 +28,19 @@ class EstateProperty(models.Model): ("east", "East"), ("west", "West"), ], - ) \ No newline at end of file + ) + state = fields.Selection( + string="Type of State", + selection=[ + ("new", "New"), + ("offer_received", "Offer Received"), + ("offer_accepted", "Offer Accepted"), + ("sold", "Sold"), + ("cancelled", "Cancelled") + ], + default="new", + copy=False + ) + + # Reserved fields + active = fields.Boolean(default=True) \ 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..f70b77737f5 --- /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..357e109336c --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Estate Property + estate.property + list,form + + \ No newline at end of file From 5ede1f32cf7d0018556cdc86b6d3d2edf7b664b0 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 18 Aug 2026 10:47:04 +0200 Subject: [PATCH 05/21] [ADD] estate: add views for list, form and filters/groups Based on the tutorial's Chapter 6, new views where added to adjust to the app's purpose --- estate/models/estate_property.py | 2 +- estate/views/estate_property_views.xml | 86 ++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index be9ff27ed13..627d82a01c4 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -15,7 +15,7 @@ class EstateProperty(models.Model): expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) - living_area = fields.Integer() + living_area = fields.Integer(string="Living Area (sqm)") facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 357e109336c..09826ffa099 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,4 +5,90 @@ estate.property list,form + + + + estate.property.view.list + estate.property + + + + + + + + + + + + + + + + + estate.property.view.form + estate.property + +
+ + +
+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + estate.property.view.search + estate.property + + + + + + + + + + + + + + + + + + \ No newline at end of file From 7bf0a5b69092402ca0ca9dce4e3648ff9af70dec Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 18 Aug 2026 13:58:08 +0200 Subject: [PATCH 06/21] [ADD] estate: add new relational fields with proper views, actions and access Based on the tutorial's Chapter 7, in order to give more shape to our data, new models and the relations between them were started being specified --- estate/__manifest__.py | 3 ++ estate/models/__init__.py | 5 ++- estate/models/estate_property.py | 25 ++++++++++++++ estate/models/estate_property_offer.py | 25 ++++++++++++++ estate/models/estate_property_tag.py | 7 ++++ estate/models/estate_property_type.py | 8 +++++ estate/security/ir.model.access.csv | 5 ++- estate/views/estate_menus.xml | 9 +++-- estate/views/estate_property_offer_views.xml | 36 ++++++++++++++++++++ estate/views/estate_property_tag_views.xml | 35 +++++++++++++++++++ estate/views/estate_property_type_views.xml | 35 +++++++++++++++++++ estate/views/estate_property_views.xml | 15 +++++++- 12 files changed, 203 insertions(+), 5 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 f266dde3f52..57771a45259 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,6 +10,9 @@ 'security/ir.model.access.csv', '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', ] } \ No newline at end of file diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..09b2099fe84 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ -from . import estate_property \ No newline at end of file +from . import estate_property +from . import estate_property_type +from . import estate_property_tag +from . import estate_property_offer \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 627d82a01c4..e1fdc30b0e2 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -42,5 +42,30 @@ class EstateProperty(models.Model): copy=False ) + # Relational fields + property_type_id = fields.Many2one( + string="Property Type", + comodel_name="estate.property.type" + ) + partner_id = fields.Many2one( + string="Buyer", + comodel_name="res.partner", + copy=False + ) + user_id = fields.Many2one( + string="Salesman", + comodel_name="res.users", + default=lambda self: self.env.user + ) + tag_ids = fields.Many2many( + string="Property Tags", + comodel_name="estate.property.tag" + ) + offer_ids = fields.One2many( + string="Offers", + comodel_name="estate.property.offer", + inverse_name="property_id" + ) + # Reserved fields active = fields.Boolean(default=True) \ No newline at end of file diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..8658b3a62f4 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,25 @@ +from odoo import fields, models + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Estate Property Offer" + + price = fields.Float(string="Offer Price") + status = fields.Selection( + string="Status", + selection=[ + ("accepted", "Accepted"), + ("refused", "Refused") + ], + copy=False + ) + partner_id = fields.Many2one( + string="Buyer", + comodel_name="res.partner", + required=True + ) + property_id = fields.Many2one( + string="Property", + comodel_name="estate.property", + required=True + ) \ No newline at end of file diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..58c366e6ce2 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,7 @@ +from odoo import fields, models + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Estate Property Tag" + + name = fields.Char(string="Tag Name", 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..ca3e5bdb4ee --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Estate Property Type" + + name = fields.Char(string="Property Type", required=True) + \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 334031a21ca..bd2e9fe13df 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_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 +access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer,access_estate_property_offer,estate.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 f70b77737f5..8142530ad62 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,8 +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..8718aa218e4 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,36 @@ + + + + + estate.property.offer.view.list + estate.property.offer + + + + + + + + + + + + estate.property.offer.view.form + 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..5a20d36c8dd --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,35 @@ + + + + Property Tags + estate.property.tag + list,form + + + + + estate.property.tag.view.list + estate.property.tag + + + + + + + + + + estate.property.tag.view.form + estate.property.tag + +
+ + +
+

+
+
+
+
+
+
\ No newline at end of file diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..5df6c469fb7 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,35 @@ + + + + Property Types + estate.property.type + list,form + + + + + estate.property.type.view.list + estate.property.type + + + + + + + + + + estate.property.type.view.form + estate.property.type + +
+ + +
+

+
+
+
+
+
+
\ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 09826ffa099..7c76c9f5fc9 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,7 +1,7 @@ - Estate Property + Estate Properties estate.property list,form @@ -13,6 +13,7 @@ + @@ -35,10 +36,12 @@

+ + @@ -63,6 +66,15 @@ + + + + + + + + + @@ -86,6 +98,7 @@ + From 27e083dfe4d04a4a6fb9e518f46263cc35952fc2 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 18 Aug 2026 15:27:48 +0200 Subject: [PATCH 07/21] [ADD] estate: introduce business logic Based on the tutorial's Chapter 8, it was introduced several computed and onchange fields to serve as entry point for business logic that will help solve the business' problems --- estate/models/estate_property.py | 39 +++++++++++++++++++- estate/models/estate_property_offer.py | 20 +++++++++- estate/views/estate_property_offer_views.xml | 10 +++-- estate/views/estate_property_views.xml | 2 + 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index e1fdc30b0e2..9ff28dd4fb9 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 from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): @@ -67,5 +67,40 @@ class EstateProperty(models.Model): inverse_name="property_id" ) + # Computed fields + total_area = fields.Integer( + string="Total Area (sqm)", + compute="_compute_total_area", + readonly=True + ) + best_price = fields.Float( + string="Best Offer", + compute="_compute_best_price", + readonly=True + ) + # Reserved fields - active = fields.Boolean(default=True) \ No newline at end of file + active = fields.Boolean(default=True) + + # Methods + @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") + def _compute_best_price(self): + for record in self: + if record.offer_ids: + record.best_price = max(record.offer_ids.mapped("price")) + else: + record.best_price = 0.0 + + @api.onchange("garden") + def _onchange_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = "north" + else: + self.garden_area = 0 + self.garden_orientation = False \ No newline at end of file diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 8658b3a62f4..0a93d461a4d 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,10 +1,17 @@ -from odoo import fields, models +from odoo import api, fields, models +from dateutil.relativedelta import relativedelta class EstatePropertyOffer(models.Model): _name = "estate.property.offer" _description = "Estate Property Offer" price = fields.Float(string="Offer Price") + validity = fields.Integer(string="Validity (days)", default=7) + date_deadline = fields.Date( + string="Deadline", + compute="_compute_date_deadline", + inverse="_inverse_date_deadline" + ) status = fields.Selection( string="Status", selection=[ @@ -22,4 +29,13 @@ class EstatePropertyOffer(models.Model): string="Property", comodel_name="estate.property", required=True - ) \ No newline at end of file + ) + + @api.depends("validity") + def _compute_date_deadline(self): + for record in self: + record.date_deadline = fields.Date.today() + relativedelta(days=record.validity) + + def _inverse_date_deadline(self): + for record in self: + record.validity = (record.date_deadline - fields.Date.today()).days \ 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 8718aa218e4..3c070ca735e 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -8,7 +8,9 @@ - + + + @@ -23,10 +25,12 @@ - + - + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 7c76c9f5fc9..88154d5923a 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -47,6 +47,7 @@ + @@ -64,6 +65,7 @@ + From f8c5a59e6042dd48f41a8a92fa0660e8994906a5 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 18 Aug 2026 16:19:18 +0200 Subject: [PATCH 08/21] [CLN] estate: apply coding guidelines Based on the comments left by one of the mentors and the documentation, several fixes and improvements have been applied to the whole tutorial new module --- estate/__init__.py | 2 +- estate/__manifest__.py | 2 +- estate/models/__init__.py | 2 +- estate/models/estate_property.py | 11 +++-------- estate/models/estate_property_offer.py | 6 ++++-- estate/models/estate_property_tag.py | 2 +- estate/models/estate_property_type.py | 1 - estate/security/ir.model.access.csv | 2 +- estate/views/estate_menus.xml | 8 +++----- estate/views/estate_property_offer_views.xml | 6 +++--- estate/views/estate_property_tag_views.xml | 7 +++---- estate/views/estate_property_type_views.xml | 7 +++---- estate/views/estate_property_views.xml | 10 ++++------ 13 files changed, 28 insertions(+), 38 deletions(-) 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 57771a45259..13008af7af1 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -15,4 +15,4 @@ 'views/estate_property_offer_views.xml', 'views/estate_menus.xml', ] -} \ No newline at end of file +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 09b2099fe84..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,4 +1,4 @@ from . import estate_property from . import estate_property_type from . import estate_property_tag -from . import estate_property_offer \ No newline at end of file +from . import estate_property_offer diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 9ff28dd4fb9..c90feee4d44 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,7 @@ -from odoo import fields, models, api from dateutil.relativedelta import relativedelta +from odoo import fields, models, api + class EstateProperty(models.Model): _name = "estate.property" _description = "Estate Property Management" @@ -41,8 +42,6 @@ class EstateProperty(models.Model): default="new", copy=False ) - - # Relational fields property_type_id = fields.Many2one( string="Property Type", comodel_name="estate.property.type" @@ -66,8 +65,6 @@ class EstateProperty(models.Model): comodel_name="estate.property.offer", inverse_name="property_id" ) - - # Computed fields total_area = fields.Integer( string="Total Area (sqm)", compute="_compute_total_area", @@ -78,8 +75,6 @@ class EstateProperty(models.Model): compute="_compute_best_price", readonly=True ) - - # Reserved fields active = fields.Boolean(default=True) # Methods @@ -103,4 +98,4 @@ def _onchange_garden(self): self.garden_orientation = "north" else: self.garden_area = 0 - self.garden_orientation = False \ No newline at end of file + self.garden_orientation = False diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 0a93d461a4d..c042d3d4b6e 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,6 +1,7 @@ -from odoo import api, fields, models from dateutil.relativedelta import relativedelta +from odoo import api, fields, models + class EstatePropertyOffer(models.Model): _name = "estate.property.offer" _description = "Estate Property Offer" @@ -31,6 +32,7 @@ class EstatePropertyOffer(models.Model): required=True ) + # Methods @api.depends("validity") def _compute_date_deadline(self): for record in self: @@ -38,4 +40,4 @@ def _compute_date_deadline(self): def _inverse_date_deadline(self): for record in self: - record.validity = (record.date_deadline - fields.Date.today()).days \ No newline at end of file + record.validity = (record.date_deadline - fields.Date.today()).days diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 58c366e6ce2..b002060c22f 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -4,4 +4,4 @@ class EstatePropertyTag(models.Model): _name = "estate.property.tag" _description = "Estate Property Tag" - name = fields.Char(string="Tag Name", required=True) \ No newline at end of file + name = fields.Char(string="Tag Name", required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index ca3e5bdb4ee..d2ab5cca42a 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -5,4 +5,3 @@ class EstatePropertyType(models.Model): _description = "Estate Property Type" name = fields.Char(string="Property Type", required=True) - \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index bd2e9fe13df..64542da710d 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -2,4 +2,4 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1 access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 -access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_offer,access_estate_property_offer,estate.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 8142530ad62..a68098a85db 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,13 +1,11 @@ - - - - + + - \ 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 3c070ca735e..3d747915528 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -1,7 +1,7 @@ - + estate.property.offer.view.list estate.property.offer @@ -16,7 +16,7 @@ - + estate.property.offer.view.form estate.property.offer @@ -37,4 +37,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index 5a20d36c8dd..05d5e52d961 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -7,7 +7,7 @@ - + estate.property.tag.view.list estate.property.tag @@ -18,13 +18,12 @@ - + estate.property.tag.view.form estate.property.tag
-

@@ -32,4 +31,4 @@
- \ No newline at end of file + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index 5df6c469fb7..c1115b169a9 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -7,7 +7,7 @@
- + estate.property.type.view.list estate.property.type @@ -18,13 +18,12 @@ - + estate.property.type.view.form estate.property.type
-

@@ -32,4 +31,4 @@
- \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 88154d5923a..86f51eb3cc3 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -7,7 +7,7 @@
- + estate.property.view.list estate.property @@ -26,7 +26,7 @@ - + estate.property.view.form estate.property @@ -84,7 +84,7 @@ - + estate.property.view.search estate.property @@ -101,9 +101,7 @@ - - - \ No newline at end of file + From 9da69bcad5ce3554b7110e510dac229f68ed4fc0 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Tue, 18 Aug 2026 16:31:14 +0200 Subject: [PATCH 09/21] [IMP] estate: enhance the domain for the filter in property view --- estate/__manifest__.py | 5 +++-- estate/views/estate_property_views.xml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 13008af7af1..2e711c63d24 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,7 +2,6 @@ "name": "Estate", "version": "1.0", "summary": "Estate Management", - "author": "Arturo Yepez", "depends": ["base"], "application": True, "installable": True, @@ -14,5 +13,7 @@ 'views/estate_property_tag_views.xml', 'views/estate_property_offer_views.xml', 'views/estate_menus.xml', - ] + ], + "author": "Arturo Yepez", + "license": 'LGPL-3', } diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 86f51eb3cc3..2cae951f955 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -98,7 +98,7 @@ - + From c6d95e39f82ecc9dc37295e8b781a92c63e345b8 Mon Sep 17 00:00:00 2001 From: Arturo Yepez Date: Wed, 19 Aug 2026 09:27:25 +0200 Subject: [PATCH 10/21] [ADD] estate: add new actions for handling property offers Based on the tutorial's Chapter 9, new actions and their UI elements were added. This will allow to have a better handling for property offers, adapting better to business needs --- estate/models/estate_property.py | 20 +++++++++++++++++++- estate/models/estate_property_offer.py | 18 ++++++++++++++++++ estate/views/estate_property_offer_views.xml | 6 ++++-- estate/views/estate_property_views.xml | 7 +++++-- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c90feee4d44..7c9bef7d96d 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 fields, models, api +from odoo.exceptions import UserError class EstateProperty(models.Model): _name = "estate.property" @@ -49,7 +50,8 @@ class EstateProperty(models.Model): partner_id = fields.Many2one( string="Buyer", comodel_name="res.partner", - copy=False + copy=False, + readonly=True ) user_id = fields.Many2one( string="Salesman", @@ -99,3 +101,19 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = False + + def action_sold(self): + self.ensure_one() + + if self.state == "cancelled": + raise UserError("Cancelled properties cannot be sold.") + + self.state = "sold" + + def action_cancel(self): + self.ensure_one() + + if self.state == "sold": + raise UserError("Sold properties cannot be cancelled.") + + self.state = "cancelled" diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index c042d3d4b6e..9351b022136 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -41,3 +41,21 @@ def _compute_date_deadline(self): def _inverse_date_deadline(self): for record in self: record.validity = (record.date_deadline - fields.Date.today()).days + + def action_accept(self): + for record in self: + record.status = "accepted" + record.property_id.state = "offer_accepted" + record.property_id.partner_id = record.partner_id + record.property_id.selling_price = record.price + + # Refuse other offers for the same property + other_offers = self.search([ + ("property_id", "=", record.property_id.id), + ("id", "!=", record.id) + ]) + other_offers.write({"status": "refused"}) + + def action_refuse(self): + for record in self: + record.status = "refused" diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 3d747915528..8e9a720d8dd 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -7,10 +7,12 @@ - - + + + + + +

+ + + + + + + + + + + +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 1f4325aa7ac..42f47ba62f4 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -4,6 +4,8 @@ Estate Properties estate.property list,form + + {'search_default_available_properties': True}
@@ -11,7 +13,11 @@ estate.property.view.list estate.property - + @@ -19,7 +25,7 @@ - +
@@ -32,19 +38,20 @@
-

- + - + @@ -59,20 +66,29 @@ - - - + + - + + + + + + + +