From 0df3fbd5240925f9ff0890c2d84fa50f844b795a Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Mon, 17 Aug 2026 13:30:33 +0200 Subject: [PATCH 01/25] [ADD] estate: real estate module --- estate/__init__.py | 1 + estate/__manifest__.py | 16 +++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 62 ++++++++++++++++++++++++++ estate/security/ir.model.access.csv | 2 + estate/views/estate_menus.xml | 8 ++++ estate/views/estate_property_views.xml | 8 ++++ 7 files changed, 98 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py create mode 100644 estate/security/ir.model.access.csv 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 new file mode 100644 index 00000000000..9a7e03eded3 --- /dev/null +++ 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 new file mode 100644 index 00000000000..631cdb207c6 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,16 @@ +{ + 'name': 'Real Estate', + 'version': '0.0', + 'depends': [ + 'base' + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml', + ], + 'installable': True, + 'application': True, + 'author': 'juica', + 'license': 'LGPL-3', +} 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..573d87e5b55 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,62 @@ +from odoo import fields, models +from dateutil.relativedelta import relativedelta + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Real Estate Property" + # _order = ... ? + + + name = fields.Char('Property Name', required=True, translate=True) + description = fields.Text('Description', translate=True) + postcode = fields.Char('Post Code', required=True) + date_availability = fields.Date( + 'Availability Date', + required=True, + copy=False, + default=fields.Date.today() + relativedelta(months=3) + ) + + expected_price = fields.Float('Expected Price') + selling_price = fields.Float( + 'Selling Price', + readonly=True, + copy=False + ) + + bedrooms = fields.Integer( + '# Bedrooms', + default=2 + ) + facades = fields.Integer('# Facades') + + garage = fields.Boolean('Garage') + garden = fields.Boolean('Garden') + + living_area = fields.Integer('Living Area mt²') + garden_area = fields.Integer('Garden mt²') + garden_orientation = fields.Selection( + string='Garden Orientation', + selection=[ + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West'), + ] + ) + + active = fields.Boolean('Active', default=True) + state = fields.Selection( + string='State', + selection=[ + ('new', 'New'), + ('offer', 'Offer'), + ('received', 'Received'), + ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled'), + ], + default="new", + copy=False, + required=True + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..976b61e8cb3 --- /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,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..6a60ca700e0 --- /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..affda3b8abc --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Properties + estate.property + list,form + + \ No newline at end of file From 72686d089eefc80610fba275bffeac51bf1e8479 Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Mon, 17 Aug 2026 16:12:48 +0200 Subject: [PATCH 02/25] [ADD] estate: models views & search filters --- estate/views/estate_property_views.xml | 83 ++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index affda3b8abc..bc4ea322bbb 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,4 +5,87 @@ estate.property list,form + + + estate.property.list + estate.property + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + + + \ No newline at end of file From 0e13e3784fa4c3bc8d929d289caa7c08703d643c Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Tue, 18 Aug 2026 09:54:09 +0200 Subject: [PATCH 03/25] [ADD] estate: new state, buyer, & type fields --- estate/__manifest__.py | 1 + estate/models/__init__.py | 3 +- estate/models/estate_property.py | 12 ++++++ estate/models/estate_property_type.py | 7 ++++ estate/security/ir.model.access.csv | 5 ++- estate/views/estate_menus.xml | 9 +++-- estate/views/estate_property_type_views.xml | 44 +++++++++++++++++++++ estate/views/estate_property_views.xml | 10 ++++- 8 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 631cdb207c6..4ae9ccd6c49 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,6 +7,7 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', 'views/estate_menus.xml', ], 'installable': True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..97aee757823 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,2 @@ -from . import estate_property \ No newline at end of file +from . import estate_property +from . import estate_property_type \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 573d87e5b55..3cad4ba6587 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -16,6 +16,18 @@ class EstateProperty(models.Model): copy=False, default=fields.Date.today() + relativedelta(months=3) ) + + type_id = fields.Many2one("estate.property.type", string="Type", required=True) + salesperson_id = fields.Many2one( + "res.users", + string="Salesperson", + default=lambda self: self.env.user + ) + buyer_id = fields.Many2one( + "res.partner", + string="Buyer", + copy=False + ) expected_price = fields.Float('Expected Price') selling_price = fields.Float( diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..bf6aade02be --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,7 @@ +from odoo import fields, models + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Real Estate Property Type" + + name = fields.Char('Type Name', required=True, translate=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 976b61e8cb3..0d5289e75db 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,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file + +access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_type_user,access_estate_property_type_user,model_estate_property_type,base.group_user,1,0,0,0 +access_estate_property_type_admin,access_estate_property_type_admin,model_estate_property_type,base.group_system,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 6a60ca700e0..4532525aac5 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,8 +1,11 @@ - - - + + + + + + \ 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..e928f3cffcd --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,44 @@ + + + + Property Types + estate.property.type + list,form + + + + estate.property.type.list + estate.property.type + + + + + + + + + estate.property.type.form + estate.property.type + +
+ +
+

+ +

+
+
+
+
+
+ + + estate.property.type.search + 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 bc4ea322bbb..c845664736c 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,12 +5,13 @@ estate.property list,form - + estate.property.list estate.property + @@ -35,6 +36,7 @@ + @@ -56,6 +58,12 @@ + + + + + + From f9b100313a2f3ff44225fa9d34861f6803a3dcf2 Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Tue, 18 Aug 2026 10:58:17 +0200 Subject: [PATCH 04/25] [ADD] estate: tags records, tags on form & list views for estate record --- estate/__manifest__.py | 1 + estate/models/__init__.py | 3 +- estate/models/estate_property.py | 1 + estate/models/estate_property_tag.py | 8 ++++ estate/security/ir.model.access.csv | 4 +- estate/views/estate_menus.xml | 1 + estate/views/estate_property_tag_views.xml | 44 ++++++++++++++++++++++ estate/views/estate_property_views.xml | 2 + 8 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/views/estate_property_tag_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 4ae9ccd6c49..a1612cabee7 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -8,6 +8,7 @@ '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', ], 'installable': True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 97aee757823..fdab6f99948 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,2 +1,3 @@ from . import estate_property -from . import estate_property_type \ No newline at end of file +from . import estate_property_type +from . import estate_property_tag \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 3cad4ba6587..d910befd14a 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -18,6 +18,7 @@ class EstateProperty(models.Model): ) type_id = fields.Many2one("estate.property.type", string="Type", required=True) + tag_ids = fields.Many2many("estate.property.tag", string="Tags") salesperson_id = fields.Many2one( "res.users", string="Salesperson", diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..5eed4c8512a --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import fields, models + +class EstatePropertyType(models.Model): + _name = "estate.property.tag" + _description = "Real Estate Property Tags" + + name = fields.Char('Tag Name', required=True, translate=True) + diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 0d5289e75db..5bd4df56d87 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -2,4 +2,6 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 access_estate_property_type_user,access_estate_property_type_user,model_estate_property_type,base.group_user,1,0,0,0 -access_estate_property_type_admin,access_estate_property_type_admin,model_estate_property_type,base.group_system,1,1,1,1 \ No newline at end of file +access_estate_property_type_admin,access_estate_property_type_admin,model_estate_property_type,base.group_system,1,1,1,1 +access_estate_property_tag_user,access_estate_property_tag_user,model_estate_property_tag,base.group_user,1,0,0,0 +access_estate_property_tag_admin,access_estate_property_tag_admin,model_estate_property_tag,base.group_system,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 4532525aac5..9302854cfb8 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..8c30cbb2215 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,44 @@ + + + + Property Tags + estate.property.tag + list,form + + + + estate.property.tag.list + estate.property.tag + + + + + + + + + estate.property.tag.form + estate.property.tag + +
+ +
+

+ +

+
+
+
+
+
+ + + estate.property.tag.search + estate.property.tag + + + + + + +
\ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c845664736c..2beaf7d9ac2 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -19,6 +19,7 @@ +
@@ -33,6 +34,7 @@

+ From db402b1c4f4c83b444f3996a20b3d0395b29c24a Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Tue, 18 Aug 2026 11:48:01 +0200 Subject: [PATCH 05/25] [ADD] estate: property offers, offer vies, & offers tab on property view --- estate/__manifest__.py | 1 + estate/models/__init__.py | 3 +- estate/models/estate_property.py | 1 + estate/models/estate_property_offer.py | 18 ++++++++++ estate/models/estate_property_tag.py | 2 +- estate/security/ir.model.access.csv | 3 +- estate/views/estate_property_offer_views.xml | 36 ++++++++++++++++++++ estate/views/estate_property_tag_views.xml | 6 ++-- estate/views/estate_property_views.xml | 3 ++ 9 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/views/estate_property_offer_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index a1612cabee7..9f913e84b73 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -9,6 +9,7 @@ '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', ], 'installable': True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index fdab6f99948..09b2099fe84 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,3 +1,4 @@ from . import estate_property from . import estate_property_type -from . import estate_property_tag \ No newline at end of file +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 d910befd14a..69990b08003 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -18,6 +18,7 @@ class EstateProperty(models.Model): ) type_id = fields.Many2one("estate.property.type", string="Type", required=True) + offer_ids = fields.One2many("estate.property.offer", "property_id") tag_ids = fields.Many2many("estate.property.tag", string="Tags") salesperson_id = fields.Many2one( "res.users", diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..7a85ebb9a0a --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,18 @@ +from odoo import fields, models + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Real Estate Property Offer" + + price = fields.Float('Price', required=True) + property_id = fields.Many2one('estate.property', 'property_id', required=True) + partner_id = fields.Many2one('res.partner', required=True) + status = fields.Selection( + string='Status', + copy=False, + selection=[ + ('accepted', 'Accepted'), + ('refused', 'Refused'), + ] + ) + diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 5eed4c8512a..d364c2f2aa0 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -2,7 +2,7 @@ class EstatePropertyType(models.Model): _name = "estate.property.tag" - _description = "Real Estate Property Tags" + _description = "Real Estate Property Tag" name = fields.Char('Tag Name', required=True, translate=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 5bd4df56d87..cc31a1ef9aa 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -4,4 +4,5 @@ access_estate_property,access_estate_property,model_estate_property,base.group_u access_estate_property_type_user,access_estate_property_type_user,model_estate_property_type,base.group_user,1,0,0,0 access_estate_property_type_admin,access_estate_property_type_admin,model_estate_property_type,base.group_system,1,1,1,1 access_estate_property_tag_user,access_estate_property_tag_user,model_estate_property_tag,base.group_user,1,0,0,0 -access_estate_property_tag_admin,access_estate_property_tag_admin,model_estate_property_tag,base.group_system,1,1,1,1 \ No newline at end of file +access_estate_property_tag_admin,access_estate_property_tag_admin,model_estate_property_tag,base.group_system,1,1,1,1 +access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..8c3c9d29cbe --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,36 @@ + + + + Property Offers + estate.property.offer + list,form + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + + estate.property.offer.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 index 8c30cbb2215..c95fa71b0de 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -6,7 +6,7 @@ list,form - + estate.property.tag.list estate.property.tag @@ -16,7 +16,7 @@ - + estate.property.tag.form estate.property.tag @@ -32,7 +32,7 @@ - + estate.property.tag.search estate.property.tag diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 2beaf7d9ac2..46223fc0a5f 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -60,6 +60,9 @@
+ + + From c508088b5c1391a849e630205b59bd96eee81b1e Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Tue, 18 Aug 2026 11:50:31 +0200 Subject: [PATCH 06/25] [ADD] estate: computed & onchange fields --- estate/models/estate_property.py | 28 +++++++++++++++++++- estate/models/estate_property_offer.py | 24 ++++++++++++++++- estate/views/estate_menus.xml | 4 +-- estate/views/estate_property_offer_views.xml | 4 +++ estate/views/estate_property_views.xml | 2 ++ 5 files changed, 58 insertions(+), 4 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 69990b08003..ebf4f6241e3 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 api, fields, models from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): @@ -74,3 +74,29 @@ class EstateProperty(models.Model): copy=False, required=True ) + + total_area = fields.Integer( + "Total Area m²", + compute="_compute_total_area", + ) + + best_price = fields.Float( + "Best Price", + compute="_compute_best_price", + ) + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for property in self: + property.total_area = property.living_area + property.garden_area + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for property in self: + property.best_price = max(property.offer_ids.mapped("price"), default=0) + + @api.onchange("garden") + def _onchange_garden(self): + self.garden_area = 10 if self.garden else 0 + # Nasty magic string. I should turn the option into a variable and then reference it + self.garden_orientation = "north" if self.garden else None diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 7a85ebb9a0a..5fefd1c5f6a 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,5 @@ -from odoo import fields, models +from odoo import api, fields, models +from dateutil.relativedelta import relativedelta class EstatePropertyOffer(models.Model): _name = "estate.property.offer" @@ -16,3 +17,24 @@ class EstatePropertyOffer(models.Model): ] ) + validity = fields.Integer( + "Validity (days)", + default=7, + required=True, + ) + date_deadline = fields.Date( + "Deadline", + required=True, + compute="_compute_deadline", + inverse="_inverse_deadline" + ) + + @api.depends("validity") + def _compute_deadline(self): + for offer in self: + offer.date_deadline = fields.Date.today() + relativedelta(days=offer.validity) + + def _inverse_deadline(self): + for offer in self: + delta = offer.date_deadline - fields.Date.today() + offer.validity = delta.days \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 9302854cfb8..b71ca48334d 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,10 +1,10 @@ - + - + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 8c3c9d29cbe..0555b22c9a4 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -13,6 +13,8 @@ + + @@ -27,6 +29,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 46223fc0a5f..6cf9f99686e 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -44,6 +44,7 @@ +
@@ -58,6 +59,7 @@ + From d89413f23a2444a5af43c5cb5abb5c8744859af3 Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Tue, 18 Aug 2026 15:50:40 +0200 Subject: [PATCH 07/25] [ADD] estate: sold & cancel property buttons, & accept & refuse offer buttons --- estate/models/estate_property.py | 24 +++++++++++++++++++- estate/models/estate_property_offer.py | 24 +++++++++++++++++++- estate/views/estate_property_offer_views.xml | 14 ++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index ebf4f6241e3..f89cc418886 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,6 @@ from odoo import api, fields, models from dateutil.relativedelta import relativedelta +from odoo.exceptions import UserError class EstateProperty(models.Model): _name = "estate.property" @@ -72,7 +73,8 @@ class EstateProperty(models.Model): ], default="new", copy=False, - required=True + required=True, + readonly=True, ) total_area = fields.Integer( @@ -100,3 +102,23 @@ def _onchange_garden(self): self.garden_area = 10 if self.garden else 0 # Nasty magic string. I should turn the option into a variable and then reference it self.garden_orientation = "north" if self.garden else None + + def action_sold(self): + for property in self: + if property.state == "cancelled": + raise UserError("You can not sell a cancelled property") + + property.state = "sold" + + return True + + + def action_cancel(self): + for property in self: + if property.state == "sold": + raise UserError("You can not cancell a sold property") + + property.state = "cancelled" + + return True + diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 5fefd1c5f6a..b3973415b93 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,5 +1,7 @@ from odoo import api, fields, models from dateutil.relativedelta import relativedelta +from odoo.exceptions import UserError + class EstatePropertyOffer(models.Model): _name = "estate.property.offer" @@ -16,6 +18,8 @@ class EstatePropertyOffer(models.Model): ('refused', 'Refused'), ] ) + # Pretty sure I shouldn't be doing this but I couldn't find another way + property_state = fields.Selection(related="property_id.state") validity = fields.Integer( "Validity (days)", @@ -37,4 +41,22 @@ def _compute_deadline(self): def _inverse_deadline(self): for offer in self: delta = offer.date_deadline - fields.Date.today() - offer.validity = delta.days \ No newline at end of file + offer.validity = delta.days + + def action_offer_accept(self): + for offer in self: + if offer.property_state in ('offer_accepted', 'sold', 'cancelled'): + raise UserError("You can not accept more offers for this property") + + offer.status = "accepted" + offer.property_id.state = "offer_accepted" + offer.property_id.buyer_id = offer.partner_id + offer.property_id.selling_price = offer.price + + return True + + def action_offer_refuse(self): + for offer in self: + offer.status = "refused" + + return True diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 0555b22c9a4..95b7b646aa9 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -16,6 +16,20 @@ + + +

From 6ca141601c3e4a2d3b476acf666f7138bbf36e5c Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Wed, 19 Aug 2026 15:11:51 +0200 Subject: [PATCH 15/25] [ADD] estate: set state to offer when creating an offer & prevent property deletion when pending --- estate/models/estate_property.py | 6 ++++++ estate/models/estate_property_offer.py | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 9149d83c714..07d2a495eb1 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -137,3 +137,9 @@ def _check_selling_price(self): limit = 0.9 * property.expected_price if float_compare(property.selling_price, limit, precision_digits=2) == -1: raise ValidationError("The property selling price must be at least 90% of the expected price") + + @api.ondelete(at_uninstall=False) + def _unlink_except_not_new_or_cancelled(self): + for property in self: + if property.state not in ('new', 'cancelled'): + raise UserError("You can not delete a property that is not either 'new' or 'cancelled'") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 25c2e0d4f97..7852fb65b4c 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -66,3 +66,25 @@ def action_offer_refuse(self): 'CHECK(price > 0)', 'The offer price must be strictly positive' ) + + @api.model_create_multi + def create(self, values): + # Get the biggest offers for the properties + prop_max_offer = {} + for v in values: + pid = v['property_id'] + prop_max_offer[pid] = max(prop_max_offer.get(pid, 0), v['price']) + + # Get the properties that the offers reference + properties = self.env['estate.property'].browse(prop_max_offer.keys()) + + for prop in properties: + # Check if the offer is higher than the best one + best_existing = max(prop.offer_ids.mapped('price'), default=0) + if prop_max_offer[prop.id] < best_existing: + raise UserError("You can not offer less than the biggest offer") + + if prop.state == 'new': + prop.state = 'offer' + + return super().create(values) From e25bc5880d8bbf8edb002a19110e5bba21388fe5 Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Wed, 19 Aug 2026 15:40:23 +0200 Subject: [PATCH 16/25] [ADD] estate: show associated properties on user view --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/res_user.py | 10 ++++++++++ estate/views/res_users_views.xml | 19 +++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 estate/models/res_user.py create mode 100644 estate/views/res_users_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 9f913e84b73..98c758e4cb1 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,6 +10,7 @@ 'views/estate_property_type_views.xml', 'views/estate_property_tag_views.xml', 'views/estate_property_offer_views.xml', + 'views/res_users_views.xml', 'views/estate_menus.xml', ], 'installable': True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 2f1821a39c1..8f914bbb526 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 res_user diff --git a/estate/models/res_user.py b/estate/models/res_user.py new file mode 100644 index 00000000000..9c9b50951d4 --- /dev/null +++ b/estate/models/res_user.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class ResUser(models.Model): + _inherit = "res.users" + + property_ids = fields.One2many( + "estate.property", "salesperson_id", + domain="[('state', 'in', ['new', 'offer'])]" + ) diff --git a/estate/views/res_users_views.xml b/estate/views/res_users_views.xml new file mode 100644 index 00000000000..a9ddc43d538 --- /dev/null +++ b/estate/views/res_users_views.xml @@ -0,0 +1,19 @@ + + + + + + res.users.view.form.inherit.properties_ids + res.users + + + + + + + + + + + + From 8488d14522943aef9b2d4293ae2003cd18e42daf Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Wed, 19 Aug 2026 15:43:23 +0200 Subject: [PATCH 17/25] [FIX] estate: prevent batch creation of offers lower than the best offer --- estate/models/estate_property_offer.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 7852fb65b4c..3ab8ca8971c 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -69,22 +69,21 @@ def action_offer_refuse(self): @api.model_create_multi def create(self, values): - # Get the biggest offers for the properties - prop_max_offer = {} + # Get the lowest new offer per property + prop_min_offer = {} for v in values: pid = v['property_id'] - prop_max_offer[pid] = max(prop_max_offer.get(pid, 0), v['price']) + prop_min_offer[pid] = min(prop_min_offer.get(pid, float('inf')), v.get('price', 0)) - # Get the properties that the offers reference - properties = self.env['estate.property'].browse(prop_max_offer.keys()) + # Browse the properties referenced by the new offers + properties = self.env['estate.property'].browse(prop_min_offer.keys()) for prop in properties: - # Check if the offer is higher than the best one + # No new offer may be lower than the best existing one best_existing = max(prop.offer_ids.mapped('price'), default=0) - if prop_max_offer[prop.id] < best_existing: + if prop_min_offer[prop.id] < best_existing: raise UserError("You can not offer less than the biggest offer") - if prop.state == 'new': - prop.state = 'offer' + properties.filtered(lambda p: p.state == 'new').state = 'offer' return super().create(values) From 35311e6dc10387a6f7d5fd820cf9ba31ba57cf4e Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Wed, 19 Aug 2026 16:47:01 +0200 Subject: [PATCH 18/25] [FIX] estate: views order so that runbot runs --- estate/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 98c758e4cb1..d0cbf4f326e 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,9 +7,9 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_property_type_views.xml', 'views/estate_property_tag_views.xml', - 'views/estate_property_offer_views.xml', 'views/res_users_views.xml', 'views/estate_menus.xml', ], From 94b7ef5e596ed682b537ed6990ba8597e0801071 Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Thu, 20 Aug 2026 09:34:52 +0200 Subject: [PATCH 19/25] [ADD] estate: create invoice on property sold --- estate_account/__init__.py | 1 + estate_account/__manifest__.py | 15 +++++++++++++ estate_account/models/__init__.py | 1 + estate_account/models/estate_property.py | 27 ++++++++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 estate_account/__init__.py create mode 100644 estate_account/__manifest__.py create mode 100644 estate_account/models/__init__.py create mode 100644 estate_account/models/estate_property.py diff --git a/estate_account/__init__.py b/estate_account/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/estate_account/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py new file mode 100644 index 00000000000..9a84206005e --- /dev/null +++ b/estate_account/__manifest__.py @@ -0,0 +1,15 @@ +{ + 'name': 'Real Estate Accounting', + 'version': '0.0', + 'depends': [ + 'base', + 'estate', + 'account' + ], + 'data': [ + ], + 'installable': True, + 'application': True, + 'author': 'juica', + 'license': 'LGPL-3', +} diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py new file mode 100644 index 00000000000..f4c8fd6db6d --- /dev/null +++ b/estate_account/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property \ No newline at end of file diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py new file mode 100644 index 00000000000..648155495c1 --- /dev/null +++ b/estate_account/models/estate_property.py @@ -0,0 +1,27 @@ +from odoo import models, Command + + +class InheritedModel(models.Model): + _inherit = "estate.property" + + def action_sold(self): + for property in self: + move = self.env['account.move'].create({ + 'partner_id': property.buyer_id.id, + 'move_type': 'out_invoice', + 'line_ids': [ + Command.create({ + 'name': f"Property: {property.name}", # Prob should use _(%d) for translating, but its just a demo ¯\_(ツ)_/¯ + 'quantity': 1, + 'price_unit': .06 * property.selling_price, + }), + Command.create({ + 'name': "Administrative fees", + 'quantity': 1, + 'price_unit': 100, + }) + ] + }) + + + return super().action_sold() From 48dec558b7e11883b966e0db46ecfc2c1ac47650 Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Thu, 20 Aug 2026 10:20:06 +0200 Subject: [PATCH 20/25] [ADD] estate: kanban view on real state properties --- estate/models/estate_property.py | 1 + estate/views/estate_property_views.xml | 36 +++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 07d2a495eb1..f5bd48bb590 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -70,6 +70,7 @@ class EstateProperty(models.Model): copy=False, required=True, readonly=True, + #group_expand=True ) total_area = fields.Integer( "Total Area m²", diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index ca5675b25ce..6fd4a4baef2 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -3,7 +3,7 @@ Properties estate.property - list,form + list,form,kanban {'search_default_available': 1} @@ -132,4 +132,38 @@ + + + estate.property.kanban + estate.property + + + + + +
+ +
+
+
+ Expected Price: + +
+
+ Best Offer: + +
+
+ Selling Price: + +
+
+
+ +
+
+
+
+
+
From 3527b065e09a48c13082b87a38328fc857486367 Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Thu, 20 Aug 2026 10:23:08 +0200 Subject: [PATCH 21/25] [REF] estate: merge `offer` & `received` property states into one --- estate/models/estate_property.py | 3 +-- estate/models/estate_property_offer.py | 2 +- estate/models/res_user.py | 2 +- estate/views/estate_property_views.xml | 8 ++++---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index f5bd48bb590..72aee6d05cc 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -60,8 +60,7 @@ class EstateProperty(models.Model): string='State', selection=[ ('new', 'New'), - ('offer', 'Offer'), - ('received', 'Received'), + ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled'), diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 3ab8ca8971c..38ea1ab4953 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -84,6 +84,6 @@ def create(self, values): if prop_min_offer[prop.id] < best_existing: raise UserError("You can not offer less than the biggest offer") - properties.filtered(lambda p: p.state == 'new').state = 'offer' + properties.filtered(lambda p: p.state == 'new').state = 'offer_received' return super().create(values) diff --git a/estate/models/res_user.py b/estate/models/res_user.py index 9c9b50951d4..8f3bccb7986 100644 --- a/estate/models/res_user.py +++ b/estate/models/res_user.py @@ -6,5 +6,5 @@ class ResUser(models.Model): property_ids = fields.One2many( "estate.property", "salesperson_id", - domain="[('state', 'in', ['new', 'offer'])]" + domain="[('state', 'in', ['new', 'offer_received'])]" ) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 6fd4a4baef2..3031fa237f8 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -12,7 +12,7 @@ estate.property @@ -56,7 +56,7 @@ invisible="state in ('sold', 'cancelled')" /> - +
@@ -121,7 +121,7 @@
-
+
Best Offer:
From 38ab99b775bc94021f80f4a956428e7d4bd7ce2a Mon Sep 17 00:00:00 2001 From: "Juan I. Casareski" Date: Thu, 20 Aug 2026 10:38:42 +0200 Subject: [PATCH 22/25] [IMP] estate: remove unnecessary check for property state & offer status --- estate/__manifest__.py | 2 +- estate/models/estate_property.py | 46 ++++++++++---------- estate/models/estate_property_offer.py | 19 ++++---- estate/models/estate_property_tag.py | 2 +- estate/models/estate_property_type.py | 4 +- estate/models/res_user.py | 4 +- estate/views/estate_property_offer_views.xml | 4 +- estate_account/__manifest__.py | 2 +- estate_account/models/__init__.py | 2 +- estate_account/models/estate_property.py | 13 +++--- 10 files changed, 49 insertions(+), 49 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index d0cbf4f326e..86f41074884 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,7 +2,7 @@ 'name': 'Real Estate', 'version': '0.0', 'depends': [ - 'base' + 'base', ], 'data': [ 'security/ir.model.access.csv', diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 72aee6d05cc..1ea7a573d02 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,6 @@ -from odoo import api, fields, models from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models from odoo.exceptions import UserError, ValidationError from odoo.tools.float_utils import float_compare, float_is_zero @@ -13,33 +14,33 @@ class EstateProperty(models.Model): description = fields.Text('Description', translate=True) postcode = fields.Char('Post Code', required=True) date_availability = fields.Date( - 'Availability Date', - required=True, + 'Availability Date', + required=True, copy=False, - default=fields.Date.today() + relativedelta(months=3) + default=fields.Date.today() + relativedelta(months=3), ) type_id = fields.Many2one("estate.property.type", string="Type", required=True) offer_ids = fields.One2many("estate.property.offer", "property_id") tag_ids = fields.Many2many("estate.property.tag", string="Tags") salesperson_id = fields.Many2one( "res.users", - string="Salesperson", - default=lambda self: self.env.user + string="Salesperson", + default=lambda self: self.env.user, ) buyer_id = fields.Many2one( - "res.partner", + "res.partner", string="Buyer", - copy=False + copy=False, ) expected_price = fields.Float('Expected Price') selling_price = fields.Float( - 'Selling Price', - readonly=True, - copy=False + 'Selling Price', + readonly=True, + copy=False, ) bedrooms = fields.Integer( - '# Bedrooms', - default=2 + '# Bedrooms', + default=2, ) facades = fields.Integer('# Facades') garage = fields.Boolean('Garage') @@ -53,7 +54,7 @@ class EstateProperty(models.Model): ('south', 'South'), ('east', 'East'), ('west', 'West'), - ] + ], ) active = fields.Boolean('Active', default=True) state = fields.Selection( @@ -69,7 +70,7 @@ class EstateProperty(models.Model): copy=False, required=True, readonly=True, - #group_expand=True + # group_expand=True ) total_area = fields.Integer( "Total Area m²", @@ -105,35 +106,36 @@ def action_sold(self): for property in self: if property.state == "cancelled": raise UserError("You can not sell a cancelled property") - + property.state = "sold" return True - + def action_cancel(self): for property in self: if property.state == "sold": raise UserError("You can not cancel a sold property") - + property.state = "cancelled" return True _exp_price_positive = models.Constraint( 'CHECK(expected_price > 0)', - 'The property expected price must be strictly positive' + 'The property expected price must be strictly positive', ) _sell_price_positive = models.Constraint( 'CHECK(selling_price >= 0)', - 'The property selling price must be positive' + 'The property selling price must be positive', ) @api.constrains("selling_price", "expected_price") def _check_selling_price(self): for property in self: - if float_is_zero(property.selling_price, precision_digits=2): continue - + if float_is_zero(property.selling_price, precision_digits=2): + continue + limit = 0.9 * property.expected_price if float_compare(property.selling_price, limit, precision_digits=2) == -1: raise ValidationError("The property selling price must be at least 90% of the expected price") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 38ea1ab4953..5df7a5f6ef9 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,5 +1,6 @@ -from odoo import api, fields, models from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models from odoo.exceptions import UserError @@ -17,10 +18,8 @@ class EstatePropertyOffer(models.Model): selection=[ ('accepted', 'Accepted'), ('refused', 'Refused'), - ] + ], ) - # Pretty sure I shouldn't be doing this but I couldn't find another way - property_state = fields.Selection(related="property_id.state") property_type_id = fields.Many2one(related="property_id.type_id", store=True) validity = fields.Integer( "Validity (days)", @@ -31,7 +30,7 @@ class EstatePropertyOffer(models.Model): "Deadline", required=True, compute="_compute_deadline", - inverse="_inverse_deadline" + inverse="_inverse_deadline", ) @api.depends("validity") @@ -45,15 +44,15 @@ def _inverse_deadline(self): offer.validity = delta.days def action_offer_accept(self): - for offer in self: - if offer.property_state in ('offer_accepted', 'sold', 'cancelled'): + for offer in self: + if offer.property_id.state in ('offer_accepted', 'sold', 'cancelled'): raise UserError("You can not accept more offers for this property") offer.status = "accepted" offer.property_id.state = "offer_accepted" offer.property_id.buyer_id = offer.partner_id offer.property_id.selling_price = offer.price - + return True def action_offer_refuse(self): @@ -64,7 +63,7 @@ def action_offer_refuse(self): _price_positive = models.Constraint( 'CHECK(price > 0)', - 'The offer price must be strictly positive' + 'The offer price must be strictly positive', ) @api.model_create_multi @@ -80,7 +79,7 @@ def create(self, values): for prop in properties: # No new offer may be lower than the best existing one - best_existing = max(prop.offer_ids.mapped('price'), default=0) + best_existing = prop.offer_ids[0] if prop_min_offer[prop.id] < best_existing: raise UserError("You can not offer less than the biggest offer") diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index b70e14e6677..28c549e1195 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -11,5 +11,5 @@ class EstatePropertyTag(models.Model): _uniq_name = models.Constraint( 'UNIQUE(name)', - 'The tag name must be unique' + 'The tag name must be unique', ) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index b01fb9348f8..9b69f669d51 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -13,12 +13,12 @@ class EstatePropertyType(models.Model): offer_ids = fields.One2many("estate.property.offer", "property_type_id") offer_count = fields.Integer(compute="_compute_offer_count", default=0) - api.depends("offer_ids") + @api.depends("offer_ids") def _compute_offer_count(self): for offer in self: offer.offer_count = len(offer.offer_ids) _uniq_name = models.Constraint( 'UNIQUE(name)', - 'The type name must be unique' + 'The type name must be unique', ) diff --git a/estate/models/res_user.py b/estate/models/res_user.py index 8f3bccb7986..80838e2454e 100644 --- a/estate/models/res_user.py +++ b/estate/models/res_user.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import fields, models class ResUser(models.Model): @@ -6,5 +6,5 @@ class ResUser(models.Model): property_ids = fields.One2many( "estate.property", "salesperson_id", - domain="[('state', 'in', ['new', 'offer_received'])]" + domain="[('state', 'in', ['new', 'offer_received'])]", ) diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 2566d25347c..21ce52a4fb6 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -24,14 +24,14 @@ name="action_offer_accept" string="Accept" icon="fa-check" - invisible="property_state in ('offer_accepted', 'sold', 'cancelled')" + invisible="status" type="object" />