From 7e844b7bf638408fd5a32370211d598c062033b1 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Mon, 15 Dec 2025 14:49:41 +0100 Subject: [PATCH 01/12] [ADD] real_estate: init real estate addon --- estate/__init__.py | 0 estate/__manifest__.py | 13 +++++++++++++ 2 files changed, 13 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..50d404c7608 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Real Estate", + + 'summary': """ + Starting module for "Real Estate" + """, + + 'version': '0.1', + + # any module necessary for this one to work correctly + 'depends': ['base',], +} From f5d86c51da42711c4a094a03f0b42debfc298562 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Mon, 15 Dec 2025 16:42:59 +0100 Subject: [PATCH 02/12] [ADD] real_estate: add estate property model and its props --- estate/__manifest__.py | 13 +++---------- estate/models/__init__.py | 1 + estate/models/estate_property.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 50d404c7608..e1bd7b5508a 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,13 +1,6 @@ -# -*- coding: utf-8 -*- { 'name': "Real Estate", - - 'summary': """ - Starting module for "Real Estate" - """, - - 'version': '0.1', - - # any module necessary for this one to work correctly - 'depends': ['base',], + 'application': True, + 'installable': True, + 'depends': ['base'] } 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..d67c3b7dd57 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,28 @@ +from odoo import fields, models + + +class EstateProperty(models.Model): + _name = "public.estate_property" + _description = "Estate property" + + name = fields.Char('Property Name', required=True) + description = fields.Text('Property Description') + postcode = fields.Char('Postcode') + date_availability = fields.Date('Availability Date') + expected_price = fields.Float('Expected Price', required=True) + selling_price = fields.Float('Selling Price') + bedrooms = fields.Integer('# Bedrooms') + living_area = fields.Integer('# Living Area') + facades = fields.Integer('# Facades') + garage = fields.Boolean('Has Garage') + garden = fields.Boolean('Has Garden') + garden_area = fields.Integer('Garden area') + garden_orientation = fields.Selection( + string='type', + selection=[ + ('North', 'North Garden Orientation'), + ('South', 'South Garden Orientation'), + ('East', 'East Garden Orientation'), + ('West', 'West Garden Orientation') + ], string='North Garden Orientation', default='North') + From fe4548015225223af851c4d2b9c8ce517f8c7909 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Tue, 16 Dec 2025 14:58:08 +0100 Subject: [PATCH 03/12] [ADD] real_estate: add estate model security rules --- estate/__init__.py | 1 + estate/__manifest__.py | 5 ++++- estate/models/estate_property.py | 4 ++-- estate/security/ir.model.access.csv | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 estate/security/ir.model.access.csv 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 e1bd7b5508a..d3bcf7b920d 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,5 +2,8 @@ 'name': "Real Estate", 'application': True, 'installable': True, - 'depends': ['base'] + 'depends': ['base'], + 'data': [ + 'security/ir.model.access.csv', + ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d67c3b7dd57..3f7913fd34d 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -2,7 +2,7 @@ class EstateProperty(models.Model): - _name = "public.estate_property" + _name = "estate_property" _description = "Estate property" name = fields.Char('Property Name', required=True) @@ -24,5 +24,5 @@ class EstateProperty(models.Model): ('South', 'South Garden Orientation'), ('East', 'East Garden Orientation'), ('West', 'West Garden Orientation') - ], string='North Garden Orientation', default='North') + ]) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..29affc2d1b4 --- /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 From ac62922bc1b4ac12a41cee610d5bd3f9208dfd8a Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Tue, 16 Dec 2025 17:08:28 +0100 Subject: [PATCH 04/12] [ADD] real_estate: changes in the views of the model to add menu and actions --- .vscode/settings.json | 3 +++ estate/__init__.py | 3 ++- estate/__manifest__.py | 2 ++ estate/models/estate_property.py | 22 ++++++++++++++++++---- estate/views/estate_menus_views.xml | 8 ++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 6 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 estate/views/estate_menus_views.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..f163449d183 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["odoo"] +} diff --git a/estate/__init__.py b/estate/__init__.py index 9a7e03eded3..a82c623203c 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1 +1,2 @@ -from . import models \ No newline at end of file +from . import models +from . import views \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py index d3bcf7b920d..eb62bc30ba2 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,5 +5,7 @@ 'depends': ['base'], 'data': [ 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus_views.xml', ] } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 3f7913fd34d..751177a7ba5 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,17 +1,19 @@ from odoo import fields, models +from datetime import timedelta class EstateProperty(models.Model): - _name = "estate_property" + _name = "estate.property" _description = "Estate property" name = fields.Char('Property Name', required=True) description = fields.Text('Property Description') + active = fields.Boolean(default=True) postcode = fields.Char('Postcode') - date_availability = fields.Date('Availability Date') + date_availability = fields.Date(default=lambda self: fields.Date.today() + timedelta(days=90), copy=False) 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('Has Garage') @@ -25,4 +27,16 @@ class EstateProperty(models.Model): ('East', 'East Garden Orientation'), ('West', 'West Garden Orientation') ]) + state = fields.Selection( + [ + ('new', 'New'), + ('offer_received', 'Offer Received'), + ('offer_accepted', 'Offer Accepted'), + ('sold', 'Sold'), + ('cancelled', 'Cancelled'), + ], + required=True, + copy=False, + default='new', + ) diff --git a/estate/views/estate_menus_views.xml b/estate/views/estate_menus_views.xml new file mode 100644 index 00000000000..61d6a951b68 --- /dev/null +++ b/estate/views/estate_menus_views.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..9fa576c2618 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Property + estate.property + list,form + + \ No newline at end of file From 7ec510153807f5992c45aaa14080199be0f52e44 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Wed, 17 Dec 2025 11:47:40 +0100 Subject: [PATCH 05/12] [IMP] real_estate: add list columns and form fields --- estate/__init__.py | 3 +- estate/models/estate_property.py | 24 +++---- estate/views/estate_property_views.xml | 93 ++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 20 deletions(-) diff --git a/estate/__init__.py b/estate/__init__.py index a82c623203c..9a7e03eded3 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -1,2 +1 @@ -from . import models -from . import views \ No newline at end of file +from . import models \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 751177a7ba5..21b6e46a491 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -6,26 +6,26 @@ class EstateProperty(models.Model): _name = "estate.property" _description = "Estate property" - name = fields.Char('Property Name', required=True) - description = fields.Text('Property Description') + name = fields.Char('Name', required=True, default='My new house') + description = fields.Text('Description') active = fields.Boolean(default=True) postcode = fields.Char('Postcode') - date_availability = fields.Date(default=lambda self: fields.Date.today() + timedelta(days=90), copy=False) + date_availability = fields.Date('Available From',default=lambda self: fields.Date.today() + timedelta(days=90), copy=False) expected_price = fields.Float('Expected Price', required=True) 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('Has Garage') - garden = fields.Boolean('Has Garden') + bedrooms = fields.Integer('Bedrooms', default=2) + living_area = fields.Integer('Living Area (sqm)') + facades = fields.Integer('#Facades') + garage = fields.Boolean('Garage') + garden = fields.Boolean('Garden') garden_area = fields.Integer('Garden area') garden_orientation = fields.Selection( string='type', selection=[ - ('North', 'North Garden Orientation'), - ('South', 'South Garden Orientation'), - ('East', 'East Garden Orientation'), - ('West', 'West Garden Orientation') + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West') ]) state = fields.Selection( [ diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 9fa576c2618..d59320cc315 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,8 +1,89 @@ - - Property - estate.property - list,form - - \ No newline at end of file + + Property + estate.property + list,form + + + + estate.property.list + estate.property + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ From 90cf058bec21af9934c1226be31a3f6a4142a4e4 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Wed, 17 Dec 2025 13:42:14 +0100 Subject: [PATCH 06/12] [IMP] real_estate: add search and filters in estate properties list --- estate/views/estate_property_views.xml | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index d59320cc315..d4c9ff13a7b 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -86,4 +86,34 @@ + + + estate.property.view.search + estate.property + + + + + + + + + + + + + + + + From 580267f20a2e047832473d58c1a37a8ab9239333 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Thu, 18 Dec 2025 17:27:22 +0100 Subject: [PATCH 07/12] [IMP] real_estate: add property type and tags models --- estate/models/__init__.py | 4 ++- estate/models/estate_property.py | 23 +++++++++++++++++ estate/models/estate_property_tag.py | 7 ++++++ estate/models/estate_property_type.py | 9 +++++++ estate/security/ir.model.access.csv | 2 ++ estate/views/estate_menus_views.xml | 25 ++++++++++++++---- estate/views/estate_property_views.xml | 35 +++++++++++++++++++++++--- 7 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/models/estate_property_type.py diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..fdab6f99948 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,3 @@ -from . import estate_property \ No newline at end of file +from . import estate_property +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 21b6e46a491..93ac6a8732b 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -27,6 +27,7 @@ class EstateProperty(models.Model): ('east', 'East'), ('west', 'West') ]) + state = fields.Selection( [ ('new', 'New'), @@ -39,4 +40,26 @@ class EstateProperty(models.Model): copy=False, default='new', ) + + buyer_id = fields.Many2one( + "res.partner", + string="Buyer", + copy=False + ) + + salesperson_id = fields.Many2one( + "res.users", + string="Salesperson", + default=lambda self: self.env.user + ) + + property_type_id = fields.Many2one( + "estate.property.type", + string="Property Type" + ) + + tag_ids = fields.Many2many( + "estate.property.tag", + string="Tags" + ) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..911961b424c --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,7 @@ +from odoo import models, fields + +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Estate Property Tag" + + name = fields.Char(required=True) \ No newline at end of file diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..42b4fac0e16 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,9 @@ +from odoo import fields, models + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Estate property type" + + name = fields.Char('Name', 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 29affc2d1b4..2c80d272e19 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,4 @@ 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,access.estate.property.type,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag,access.estate.property.tag,model_estate_property_tag,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus_views.xml b/estate/views/estate_menus_views.xml index 61d6a951b68..1c7498ccb42 100644 --- a/estate/views/estate_menus_views.xml +++ b/estate/views/estate_menus_views.xml @@ -1,8 +1,23 @@ - - - - + + + + + + + + - \ No newline at end of file + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index d4c9ff13a7b..027b370ab97 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,6 +6,18 @@ list,form + + Settings + estate.property.type + list,form + + + + Settings + estate.property.type + list,form + + estate.property.list estate.property @@ -51,7 +63,16 @@ + + + + + + + + + @@ -81,6 +102,12 @@ + + + + + + @@ -88,7 +115,7 @@ - estate.property.view.search + estate.property.search estate.property @@ -99,16 +126,16 @@ - + From 9fea90025bc7a01e0e53b81a23695164c0a23738 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Fri, 19 Dec 2025 15:18:20 +0100 Subject: [PATCH 08/12] [IMP] real_estate: implement property offers, types, and tags with corresponding views --- estate/__manifest__.py | 3 ++ estate/models/__init__.py | 3 +- estate/models/estate_property.py | 23 ++++++++----- estate/models/estate_property_offer.py | 23 +++++++++++++ estate/models/estate_property_tag.py | 3 +- estate/models/estate_property_type.py | 3 +- estate/security/ir.model.access.csv | 1 + estate/views/estate_menus_views.xml | 8 ++--- estate/views/estate_property_offer_views.xml | 30 +++++++++++++++++ estate/views/estate_property_tag_views.xml | 32 ++++++++++++++++++ estate/views/estate_property_type_views.xml | 35 ++++++++++++++++++++ estate/views/estate_property_views.xml | 34 +++++++------------ 12 files changed, 159 insertions(+), 39 deletions(-) create mode 100644 estate/models/estate_property_offer.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 eb62bc30ba2..535d2f59b94 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,9 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_menus_views.xml', ] } 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 93ac6a8732b..5e8b6a03627 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -19,6 +19,7 @@ class EstateProperty(models.Model): garage = fields.Boolean('Garage') garden = fields.Boolean('Garden') garden_area = fields.Integer('Garden area') + garden_orientation = fields.Selection( string='type', selection=[ @@ -27,7 +28,7 @@ class EstateProperty(models.Model): ('east', 'East'), ('west', 'West') ]) - + state = fields.Selection( [ ('new', 'New'), @@ -40,6 +41,10 @@ class EstateProperty(models.Model): copy=False, default='new', ) + property_type_id = fields.Many2one( + "estate.property.type", + string="Property Type" + ) buyer_id = fields.Many2one( "res.partner", @@ -49,17 +54,17 @@ class EstateProperty(models.Model): salesperson_id = fields.Many2one( "res.users", - string="Salesperson", + string="Salesman", default=lambda self: self.env.user ) - property_type_id = fields.Many2one( - "estate.property.type", - string="Property Type" + tag_ids = fields.Many2many( + "estate.property.tag", + string="Tags" ) - - tag_ids = fields.Many2many( - "estate.property.tag", - string="Tags" + offer_ids = fields.One2many( + "estate.property.offer", + "property_id", + string="Offers", ) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..4e9c347a667 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,23 @@ +from odoo import models, fields + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Estate Property Offer" + + price = fields.Float() + status = fields.Selection( + [ + ('accepted', 'Accepted'), + ('refused', 'Refused'), + ], + copy=False, + ) + partner_id = fields.Many2one( + "res.partner", + required=True, + ) + property_id = fields.Many2one( + "estate.property", + required=True, + ) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 911961b424c..78ce298bce9 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -1,7 +1,8 @@ from odoo import models, fields + class EstatePropertyTag(models.Model): _name = "estate.property.tag" _description = "Estate Property Tag" - name = fields.Char(required=True) \ No newline at end of file + name = fields.Char(required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 42b4fac0e16..b4a57cefb7d 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -1,9 +1,8 @@ from odoo import fields, models + class EstatePropertyType(models.Model): _name = "estate.property.type" _description = "Estate property type" name = fields.Char('Name', 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 2c80d272e19..8e49c2c5593 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -2,3 +2,4 @@ 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,access.estate.property.type,model_estate_property_type,base.group_user,1,1,1,1 access_estate_property_tag,access.estate.property.tag,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer,estate.property.offer,model_estate_property_offer,,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_menus_views.xml b/estate/views/estate_menus_views.xml index 1c7498ccb42..10b4fc9a6a2 100644 --- a/estate/views/estate_menus_views.xml +++ b/estate/views/estate_menus_views.xml @@ -1,6 +1,6 @@ - + - - + /> + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..398b444b8cc --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,30 @@ + + + + estate.property.offer.tree + estate.property.offer + + + + + + + + + + + estate.property.offer.form + estate.property.offer + +
+ + + + + + + +
+
+
+
diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..07b417bdc01 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,32 @@ + + + + Tags + estate.property.tag + list,form + + + + estate.property.tag.list + estate.property.tag + + + + + + + + + estate.property.tag.form + estate.property.tag + +
+ + + + + +
+
+
+
diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..d2f1b87c56f --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,35 @@ + + + + Property type + estate.property.type + list,form + + + + estate.property.type.list + estate.property.type + + + + + + + + + estate.property.type.form + estate.property.type + +
+ +
+ +

+ +

+
+
+
+
+
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 027b370ab97..3afff431521 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,18 +6,6 @@ list,form
- - Settings - estate.property.type - list,form - - - - Settings - estate.property.type - list,form - - estate.property.list estate.property @@ -30,6 +18,8 @@ + + @@ -46,10 +36,15 @@ + + + + + + - @@ -63,14 +58,6 @@ - - - - - - - - @@ -102,12 +89,15 @@ - + + + + From a9406d3888c1f75b4212f74497826db393e277c4 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Mon, 22 Dec 2025 10:05:16 +0100 Subject: [PATCH 09/12] [IMP] real_estate: add computed values and onchange to estate property and offer models --- .vscode/settings.json | 3 - estate/models/estate_property.py | 65 +++++++++++++++++--- estate/models/estate_property_offer.py | 42 ++++++++++++- estate/views/estate_property_offer_views.xml | 4 ++ estate/views/estate_property_views.xml | 44 ++++--------- 5 files changed, 112 insertions(+), 46 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index f163449d183..00000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "cSpell.words": ["odoo"] -} diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5e8b6a03627..6d520bfd2eb 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 datetime import timedelta @@ -10,7 +10,7 @@ class EstateProperty(models.Model): description = fields.Text('Description') active = fields.Boolean(default=True) postcode = fields.Char('Postcode') - date_availability = fields.Date('Available From',default=lambda self: fields.Date.today() + timedelta(days=90), copy=False) + date_availability = fields.Date('Available From', default=lambda self: fields.Date.context_today(self) + timedelta(days=90), copy=False) expected_price = fields.Float('Expected Price', required=True) selling_price = fields.Float('Selling Price', readonly=True, copy=False) bedrooms = fields.Integer('Bedrooms', default=2) @@ -20,13 +20,11 @@ class EstateProperty(models.Model): garden = fields.Boolean('Garden') garden_area = fields.Integer('Garden area') - garden_orientation = fields.Selection( - string='type', - selection=[ - ('north', 'North'), - ('south', 'South'), - ('east', 'East'), - ('west', 'West') + garden_orientation = fields.Selection([ + ("north", "North"), + ("south", "South"), + ("east", "East"), + ("west", "West"), ]) state = fields.Selection( @@ -68,3 +66,52 @@ class EstateProperty(models.Model): "property_id", string="Offers", ) + + total_area = fields.Float( + compute="_compute_total_area", + string="Total Area", + ) + + best_price = fields.Float( + compute="_compute_best_price", + string="Best Offer", + ) + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + @api.depends("offer_ids.price") + def _compute_best_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 + + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for record in self: + if record.create_date: + record.date_deadline = ( + record.create_date.date() + timedelta(days=record.validity) + ) + else: + record.date_deadline = fields.Date.context_today(self) + + def _inverse_date_deadline(self): + for record in self: + if record.create_date and record.date_deadline: + record.validity = ( + record.date_deadline - record.create_date.date() + ).days diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 4e9c347a667..fd095359728 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,5 @@ -from odoo import models, fields +from odoo import models, fields, api +from datetime import timedelta class EstatePropertyOffer(models.Model): @@ -13,11 +14,50 @@ class EstatePropertyOffer(models.Model): ], copy=False, ) + partner_id = fields.Many2one( "res.partner", required=True, ) + property_id = fields.Many2one( "estate.property", required=True, ) + + validity = fields.Integer( + default=7, + ) + + date_deadline = fields.Date( + compute="_compute_date_deadline", + inverse="_inverse_date_deadline", + ) + + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for record in self: + if record.create_date: + record.date_deadline = ( + record.create_date.date() + + timedelta(days=record.validity) + ) + else: + record.date_deadline = False + + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for record in self: + if record.create_date: + record.date_deadline = ( + record.create_date.date() + timedelta(days=record.validity) + ) + else: + record.date_deadline = fields.Date.context_today(self) + + def _inverse_date_deadline(self): + for record in self: + if record.create_date and record.date_deadline: + record.validity = ( + record.date_deadline - record.create_date.date() + ).days diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 398b444b8cc..ee4c2520cc4 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -8,6 +8,8 @@ + + @@ -21,6 +23,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 3afff431521..c0479c815fc 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -35,28 +35,19 @@ - + + + + - - - - - - - - - - - - - - + + @@ -65,39 +56,26 @@ - - - - - - - - - - - - - - + + + + - - - @@ -111,7 +89,7 @@ - + From db55157b577e52ca778a14a45dec3653201b5bda Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Mon, 22 Dec 2025 10:28:38 +0100 Subject: [PATCH 10/12] [FIX] real_estate: ensure proper formatting in manifest and model initialization files --- estate/__init__.py | 2 +- estate/__manifest__.py | 4 +++- estate/models/__init__.py | 2 +- 3 files changed, 5 insertions(+), 3 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 535d2f59b94..c05d9330061 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,5 +10,7 @@ 'views/estate_property_tag_views.xml', 'views/estate_property_offer_views.xml', 'views/estate_menus_views.xml', - ] + ], + 'author': 'Odoo S.A.', + 'license': 'LGPL-3' } 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 From 99386a71f7af8d9445d9d0244eb1bcdedef8e505 Mon Sep 17 00:00:00 2001 From: Oussema Heni Date: Mon, 22 Dec 2025 11:43:13 +0100 Subject: [PATCH 11/12] [IMP] real_estate: add action methods and models constraints --- estate/models/estate_property.py | 43 +++++++++ estate/models/estate_property_offer.py | 25 ++++++ estate/models/estate_property_tag.py | 5 ++ estate/models/estate_property_type.py | 5 ++ estate/security/ir.model.access.csv | 2 +- estate/views/estate_property_offer_views.xml | 91 +++++++++++++------- estate/views/estate_property_views.xml | 22 +++++ 7 files changed, 162 insertions(+), 31 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 6d520bfd2eb..03a6216dc94 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,6 @@ from odoo import fields, models, api +from odoo.exceptions import UserError, ValidationError +from odoo.tools.float_utils import float_compare, float_is_zero from datetime import timedelta @@ -6,6 +8,16 @@ class EstateProperty(models.Model): _name = "estate.property" _description = "Estate property" + _check_expected_price = models.Constraint( + "CHECK(expected_price > 0)", + "The expected price must be strictly positive.", + ) + + _check_selling_price = models.Constraint( + "CHECK(selling_price >= 0)", + "The selling price must be positive.", + ) + name = fields.Char('Name', required=True, default='My new house') description = fields.Text('Description') active = fields.Boolean(default=True) @@ -115,3 +127,34 @@ def _inverse_date_deadline(self): record.validity = ( record.date_deadline - record.create_date.date() ).days + + def action_cancel(self): + for record in self: + if record.state == "sold": + raise UserError("A sold property cannot be cancelled.") + record.state = "cancelled" + return True + + def action_sold(self): + for record in self: + if record.state == "cancelled": + raise UserError("A cancelled property cannot be sold.") + record.state = "sold" + return True + + @api.constrains("selling_price", "expected_price") + def _check_selling_price(self): + for record in self: + if float_is_zero(record.selling_price, precision_rounding=0.01): + continue + + min_price = record.expected_price * 0.9 + + if float_compare( + record.selling_price, + min_price, + precision_rounding=0.01 + ) < 0: + raise ValidationError( + "The selling price cannot be lower than 90% of the expected price." + ) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index fd095359728..edc46c33784 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -6,7 +6,13 @@ class EstatePropertyOffer(models.Model): _name = "estate.property.offer" _description = "Estate Property Offer" + _check_offer_price = models.Constraint( + "CHECK(price > 0)", + "The offer price must be strictly positive.", + ) + price = fields.Float() + status = fields.Selection( [ ('accepted', 'Accepted'), @@ -61,3 +67,22 @@ def _inverse_date_deadline(self): record.validity = ( record.date_deadline - record.create_date.date() ).days + + def action_accept(self): + for record in self: + other_offers = record.property_id.offer_ids - record + other_offers.write({"status": "refused"}) + + record.status = "accepted" + + record.property_id.write({ + "buyer_id": record.partner_id.id, + "selling_price": record.price, + "state": "offer_accepted", + }) + return True + + def action_refuse(self): + for record in self: + record.status = "refused" + return True diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 78ce298bce9..e7fcc2b0f58 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -5,4 +5,9 @@ class EstatePropertyTag(models.Model): _name = "estate.property.tag" _description = "Estate Property Tag" + _unique_tag_name = models.Constraint( + "UNIQUE(name)", + "The property tag name must be unique.", + ) + name = fields.Char(required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index b4a57cefb7d..4e8a933b0ee 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -5,4 +5,9 @@ class EstatePropertyType(models.Model): _name = "estate.property.type" _description = "Estate property type" + _unique_type_name = models.Constraint( + "UNIQUE(name)", + "The property type name must be unique.", + ) + name = fields.Char('Name', required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 8e49c2c5593..74f61371874 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,model_estate_property,base.group_user,1,1,1,1 access_estate_property_type,access.estate.property.type,model_estate_property_type,base.group_user,1,1,1,1 access_estate_property_tag,access.estate.property.tag,model_estate_property_tag,base.group_user,1,1,1,1 -access_estate_property_offer,estate.property.offer,model_estate_property_offer,,1,1,1,1 \ No newline at end of file +access_estate_property_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_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index ee4c2520cc4..53606b42a2c 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -1,34 +1,65 @@ - - estate.property.offer.tree - estate.property.offer - - - - - - - - - - + + estate.property.offer.tree + estate.property.offer + + + + + + +