From c8637b4bd45b1391f2b03cea27789813c1f5d0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Fran=C3=A7ois=20=28stfra=29?= Date: Mon, 15 Dec 2025 16:25:14 +0100 Subject: [PATCH 1/6] [ADD] estate: Create a new module for real estate advertisment with a model set --- estate/__init__.py | 1 + estate/__manifest__.py | 8 ++++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 19 +++++++++++++++++++ 4 files changed, 29 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 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..02e0a36f06f --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,8 @@ +{ + 'name': 'Estate', + 'depends': [ + 'base_setup', + ], + 'installable': True, + 'application': 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..0ac4e554d26 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,19 @@ +from odoo import fields, models + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Real Estate Advertisement module" + + name = fields.Char('Name', required=True, translate=True) + description = fields.Text('Description') + postcode = fields.Char('Poscode', default='1000') + date_availability = fields.Date('Date availability') + expected_price = fields.Float('Expected price') + selling_price = fields.Float('Selling price') + bedrooms = fields.Integer('Bedrooms') + living_area = fields.Integer('Living Area') + facades = fields.Integer('Facades') + garage = fields.Boolean('Garage') + garden = fields.Boolean('Garden') + garden_area = fields.Integer('Garden Area') + garden_orientation = fields.Selection('Gordden orientation', selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]) \ No newline at end of file From 7dc2f6eba5b3233f1733b9945eb4cbc791497385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Fran=C3=A7ois=20=28stfra=29?= Date: Mon, 15 Dec 2025 17:25:25 +0100 Subject: [PATCH 2/6] [FIX] estate: Fix required field needed in expected price and a typo --- estate/models/estate_property.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0ac4e554d26..70e1ad7622f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -8,7 +8,7 @@ class EstateProperty(models.Model): description = fields.Text('Description') postcode = fields.Char('Poscode', default='1000') date_availability = fields.Date('Date availability') - expected_price = fields.Float('Expected price') + expected_price = fields.Float('Expected price', required=True) selling_price = fields.Float('Selling price') bedrooms = fields.Integer('Bedrooms') living_area = fields.Integer('Living Area') @@ -16,4 +16,9 @@ class EstateProperty(models.Model): garage = fields.Boolean('Garage') garden = fields.Boolean('Garden') garden_area = fields.Integer('Garden Area') - garden_orientation = fields.Selection('Gordden orientation', selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]) \ No newline at end of file + garden_orientation = fields.Selection(selection=[ + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West') + ], string='Garden orientation') \ No newline at end of file From 7bcf43b67460f8979457518a932f780cee9a48f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Fran=C3=A7ois=20=28stfra=29?= Date: Mon, 15 Dec 2025 17:33:21 +0100 Subject: [PATCH 3/6] [ADD] estate: Add some access rights to the db --- 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 02e0a36f06f..148ed547f7d 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,4 +5,7 @@ ], 'installable': True, 'application': 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..1a28315d94f --- /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_real_estate_platform,estate.property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file From ab1280b8a9ebe6fccf9446bde1341b71fda358a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Fran=C3=A7ois=20=28stfra=29?= Date: Tue, 16 Dec 2025 14:37:26 +0100 Subject: [PATCH 4/6] [IMP] estate: enable UI interaction trough menus, display property data in list view and create/edit property data in form view The model was created and access rights provided but there was no interaction with the model. This is implemented through xml as its is mre convinient for complex data. There is one menu to acces the list view of the properties and a form views is displayed whend creating or editing a property. Some fields have now a default value, some are read-only, some are requiered and some cannot be copied when duplicated. The fields active and state were added for properties display in the list view by default and for it to have a different state depending on the stage respectively. --- estate/__manifest__.py | 3 +++ estate/models/estate_property.py | 24 ++++++++++++++++++------ estate/security/ir.model.access.csv | 2 +- estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 5 files changed, 38 insertions(+), 7 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 148ed547f7d..9551d7334ce 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,9 @@ 'installable': True, '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/estate_property.py b/estate/models/estate_property.py index 70e1ad7622f..4a8f593762c 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,16 +1,20 @@ from odoo import fields, models +from datetime import timedelta + +def default_availability_date(recordset): + return fields.Datetime.today() + timedelta(days=90) class EstateProperty(models.Model): _name = "estate.property" _description = "Real Estate Advertisement module" - name = fields.Char('Name', required=True, translate=True) + name = fields.Char('Name', required=True) description = fields.Text('Description') - postcode = fields.Char('Poscode', default='1000') - date_availability = fields.Date('Date availability') + postcode = fields.Char('Postcode') + date_availability = fields.Date('Date availability', copy=False, default=default_availability_date) expected_price = fields.Float('Expected price', required=True) - selling_price = fields.Float('Selling price') - bedrooms = fields.Integer('Bedrooms') + selling_price = fields.Float('Selling price', readonly=True, copy=False) + bedrooms = fields.Integer('Bedrooms', default=2) living_area = fields.Integer('Living Area') facades = fields.Integer('Facades') garage = fields.Boolean('Garage') @@ -21,4 +25,12 @@ class EstateProperty(models.Model): ('south', 'South'), ('east', 'East'), ('west', 'West') - ], string='Garden orientation') \ No newline at end of file + ], string='Garden orientation') + active = fields.Boolean('Active' ,default=True) + state = fields.Selection(selection=[ + ('new', 'New'), + ('offer_received', 'Offer Received'), + ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled') + ], string="State", default='new', required=True, copy=False) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 1a28315d94f..50e2e32682c 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 -access_real_estate_platform,estate.property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +access_real_estate_platform,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..698b4b952a9 --- /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 a76b9fe6e07e67e8e22e6fa571f0cee4b0df47f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Fran=C3=A7ois=20=28stfra=29?= Date: Wed, 17 Dec 2025 13:41:52 +0100 Subject: [PATCH 5/6] [IMP] estate: enrich the different views (list, form, and search) of the estate module. The views created were basic one and need some custumization to be use in a business application. List view: the following fields have been added: Title, Postcode, bedrooms, Living Area (sqm), Expected Price, Selling Price, Available From Form view: It is now composed of groups and a notebook with a page description Search view: Search has now other predifened shorcuts (Title, Postcode, expectedPrice, Bedrooms, Living Area, Facades), a predefined filter "Available" (that looks for properties with the state New or Offer received) and a Group by filter on the postcodes --- estate/views/estate_property_views.xml | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index affda3b8abc..b77681e6c1b 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,5 +1,82 @@ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+ + + estate.property.list + estate.property + + + + + + + + + + + + + Properties estate.property From 5b6bde87c07936e0be10b8080b7c59aa894e1b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Fran=C3=A7ois=20=28stfra=29?= Date: Thu, 18 Dec 2025 16:39:35 +0100 Subject: [PATCH 6/6] [IMP] estate: introduce various new data in the estate property model: buyer, salesperson, property type, list of tags and list of offers. Buyer Can be any individual (res.partner) Salesperson Has to be an employe (res.user) Property type New model with the name of the type (estate.property.type) Tags New model with the name of the tag (estate.property.tag) Offers New model with the buyer, price, status and property id (estate.property.offer) All those new models have list and form view but the offer model is only accessible from the porperty form (as each offer is linked to a property) A new menu setting has been added to allow the creation of new tags and types. --- estate/__manifest__.py | 3 ++ estate/models/__init__.py | 3 ++ estate/models/estate_property.py | 7 ++++- estate/models/estate_property_offer.py | 13 +++++++++ estate/models/estate_property_tag.py | 7 +++++ estate/models/estate_property_type.py | 7 +++++ estate/security/ir.model.access.csv | 5 +++- estate/views/estate_menus.xml | 4 +++ estate/views/estate_property_offer_views.xml | 30 ++++++++++++++++++++ estate/views/estate_property_tag_views.xml | 22 ++++++++++++++ estate/views/estate_property_type_views.xml | 22 ++++++++++++++ estate/views/estate_property_views.xml | 18 +++++++++++- 12 files changed, 138 insertions(+), 3 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 9551d7334ce..c880174a961 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,9 @@ 'installable': True, 'application': True, 'data': [ + 'views/estate_property_offer_views.xml', + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', 'views/estate_property_views.xml', 'views/estate_menus.xml', diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..aa4dade4194 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ +from . import estate_property_type +from . import estate_property_tag +from . import estate_property_offer from . import estate_property \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 4a8f593762c..b18deadd059 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -33,4 +33,9 @@ class EstateProperty(models.Model): ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled') - ], string="State", default='new', required=True, copy=False) \ No newline at end of file + ], string="State", default='new', required=True, copy=False) + property_type_id = fields.Many2one('estate.property.type', string="Property type") + property_buyer_id = fields.Many2one('res.partner', string='Buyer', copy=False) + property_salesperson_id = fields.Many2one('res.users', string="Salesperson", default=lambda self: self.env.user) + property_tag_ids = fields.Many2many('estate.property.tag', string="Property tags") + property_offer_ids = fields.One2many('estate.property.offer', 'property_id', string='Offers') \ 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..e002ab18712 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,13 @@ +from odoo import fields, models + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "A property offer" + + price = fields.Float('Price') + status = fields.Selection(selection=[ + ('accepted', 'Accepted'), + ('refused', 'Refused') + ], copy=False, string='Status') + property_buyer_id = fields.Many2one('res.partner', string="Buyer", required=True) + property_id = fields.Many2one('estate.property', string="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..e64cddda5bb --- /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 = "A property tag" + + name = fields.Char('Property tag', 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..032a4f3ef1c --- /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 = "A property type is, for example, a house or an apartment. It is a standard business need to categorize properties according to their type, especially to refine filtering." + + name = fields.Char('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 50e2e32682c..ebd47619cc5 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_real_estate_platform,estate_property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +access_real_estate_platform,estate_property,model_estate_property,base.group_user,1,1,1,1 +access_real_estate_type,estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 +access_real_estate_tag,estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 +access_real_estate_offer,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 698b4b952a9..d2db06f2618 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -4,5 +4,9 @@ + + + +
\ 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..9fceac67fbb --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,30 @@ + + + + estate.property.offer.form + estate.property.offer + +
+ + + + + + + +
+
+
+ + + 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..74e4501f5cb --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,22 @@ + + + + estate.property.tag.form + estate.property.tag + +
+ +

+ +

+
+
+
+
+ + + Property Tags + estate.property.tag + list,form + +
\ 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..c72829b86d6 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,22 @@ + + + + estate.property.type.form + estate.property.type + +
+ +

+ +

+
+
+
+
+ + + Property Types + estate.property.type + list,form + +
\ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index b77681e6c1b..90b75b77937 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,6 +6,7 @@ + @@ -28,9 +29,14 @@

+ + + + + @@ -39,6 +45,7 @@ + @@ -52,7 +59,15 @@ - + + + + + + + + + @@ -67,6 +82,7 @@ +