From 3458fe5df828cc6060fc0bc99184f634c626cef3 Mon Sep 17 00:00:00 2001
From: djhan
Date: Tue, 18 Nov 2025 14:12:02 +0100
Subject: [PATCH 01/16] [ADD] estate: init
---
estate/__init__.py | 0
estate/__manifest__.py | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 34 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..2fe56ab9c71
--- /dev/null
+++ b/estate/__manifest__.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+
+{
+ 'name': 'Estate',
+ 'version': '1.0',
+ 'category': 'Sales/Estate',
+ 'sequence': 15,
+ 'summary': 'Track all the properties you own',
+ 'website': 'https://www.odoo.com/app/estate',
+ 'depends': [
+ 'base_setup',
+ 'sales_team',
+ 'mail',
+ 'calendar',
+ 'resource',
+ 'utm',
+ 'web_tour',
+ 'contacts',
+ 'digest',
+ 'phone_validation',
+ ],
+ 'data': [
+ ],
+ 'demo': [
+ ],
+ 'installable': True,
+ 'application': True,
+ 'assets': {
+ },
+ 'author': 'Odoo S.A.',
+ 'license': 'LGPL-3',
+}
\ No newline at end of file
From d929277073a5657e6d8018ea3e2fc67e358e6838 Mon Sep 17 00:00:00 2001
From: djhan
Date: Tue, 18 Nov 2025 15:29:51 +0100
Subject: [PATCH 02/16] [ADD] estate: add estate.property model
---
estate/__init__.py | 1 +
estate/__manifest__.py | 11 +----------
estate/models/__init__.py | 1 +
estate/models/estate_property.py | 23 +++++++++++++++++++++++
4 files changed, 26 insertions(+), 10 deletions(-)
create mode 100644 estate/models/__init__.py
create mode 100644 estate/models/estate_property.py
diff --git a/estate/__init__.py b/estate/__init__.py
index e69de29bb2d..0650744f6bc 100644
--- a/estate/__init__.py
+++ b/estate/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 2fe56ab9c71..60be7e0ff95 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -10,16 +10,7 @@
'summary': 'Track all the properties you own',
'website': 'https://www.odoo.com/app/estate',
'depends': [
- 'base_setup',
- 'sales_team',
- 'mail',
- 'calendar',
- 'resource',
- 'utm',
- 'web_tour',
- 'contacts',
- 'digest',
- 'phone_validation',
+ 'base'
],
'data': [
],
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
new file mode 100644
index 00000000000..5e1963c9d2f
--- /dev/null
+++ b/estate/models/__init__.py
@@ -0,0 +1 @@
+from . import estate_property
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
new file mode 100644
index 00000000000..f5016f1bf2c
--- /dev/null
+++ b/estate/models/estate_property.py
@@ -0,0 +1,23 @@
+from odoo import models, fields
+
+class EstateProperty(models.Model):
+ _name = "estate.property"
+ _description = "Estate properties"
+
+
+ name = fields.Char('Property Name', required=True, translate=True)
+ description = fields.Text('Property Description', required=True)
+ postcode = fields.Char('Property postcode')
+ date_availability = fields.Date('Property availability date')
+ expected_price = fields.Float('Property expected price')
+ selling_price = fields.Float('Property selling price')
+ bedrooms = fields.Integer()
+ living_area = fields.Integer()
+ facades = fields.Integer()
+ garage = fields.Boolean()
+ garden = fields.Boolean()
+ garden_area = fields.Integer()
+ garden_orientation = fields.Selection(
+ string='Type',
+ selection=[('north', 'North'), ('south', 'South'), ('east','East'), ('west', 'West')])
+
From 6b5c153c9bee8dae73a10a978b259a8919ff743f Mon Sep 17 00:00:00 2001
From: djhan
Date: Tue, 18 Nov 2025 15:44:17 +0100
Subject: [PATCH 03/16] [FIX] estate: fix lint
---
estate/__manifest__.py | 4 +---
estate/models/estate_property.py | 6 ++----
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 60be7e0ff95..31516c67368 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
{
'name': 'Estate',
'version': '1.0',
@@ -22,4 +20,4 @@
},
'author': 'Odoo S.A.',
'license': 'LGPL-3',
-}
\ No newline at end of file
+}
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index f5016f1bf2c..5982db6ab2d 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,10 +1,9 @@
from odoo import models, fields
+
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate properties"
-
-
name = fields.Char('Property Name', required=True, translate=True)
description = fields.Text('Property Description', required=True)
postcode = fields.Char('Property postcode')
@@ -19,5 +18,4 @@ class EstateProperty(models.Model):
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string='Type',
- selection=[('north', 'North'), ('south', 'South'), ('east','East'), ('west', 'West')])
-
+ selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')])
From d8510306e6e78b23752ae20b6d48120779f7f782 Mon Sep 17 00:00:00 2001
From: djhan
Date: Wed, 19 Nov 2025 10:00:25 +0100
Subject: [PATCH 04/16] [IMP] estate: add ir rules
---
estate/__manifest__.py | 1 +
estate/security/ir.model.access.csv | 2 ++
2 files changed, 3 insertions(+)
create mode 100644 estate/security/ir.model.access.csv
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 31516c67368..3532c9ab553 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -11,6 +11,7 @@
'base'
],
'data': [
+ 'security/ir.model.access.csv'
],
'demo': [
],
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
new file mode 100644
index 00000000000..32389642d4f
--- /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 2b746610ca1945d01066da19e370c133f10ec490 Mon Sep 17 00:00:00 2001
From: djhan
Date: Wed, 19 Nov 2025 11:37:39 +0100
Subject: [PATCH 05/16] [IMP] estate: add UI using default views
---
estate/__manifest__.py | 4 +++-
estate/models/estate_property.py | 15 +++++++++++----
estate/views/estate_menus.xml | 8 ++++++++
estate/views/estate_property_views.xml | 19 +++++++++++++++++++
4 files changed, 41 insertions(+), 5 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 3532c9ab553..ccf43706f57 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -11,7 +11,9 @@
'base'
],
'data': [
- 'security/ir.model.access.csv'
+ 'security/ir.model.access.csv',
+ 'views/estate_property_views.xml',
+ 'views/estate_menus.xml'
],
'demo': [
],
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 5982db6ab2d..ddb27a30d1b 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -7,15 +7,22 @@ class EstateProperty(models.Model):
name = fields.Char('Property Name', required=True, translate=True)
description = fields.Text('Property Description', required=True)
postcode = fields.Char('Property postcode')
- date_availability = fields.Date('Property availability date')
+ date_availability = fields.Date('Property availability date', copy=False, default=fields.Date.add(fields.Date.today(), months=3))
expected_price = fields.Float('Property expected price')
- selling_price = fields.Float('Property selling price')
- bedrooms = fields.Integer()
+ selling_price = fields.Float('Property selling price', readonly=True)
+ bedrooms = fields.Integer(default=2)
+ active = fields.Boolean(default=True)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(
- string='Type',
+ string='Garden Orientation',
selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')])
+ state = fields.Selection(
+ string='State',
+ required=True,
+ default='new',
+ copy=False,
+ selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')])
diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml
new file mode 100644
index 00000000000..579b3413f6f
--- /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..184e974ca19
--- /dev/null
+++ b/estate/views/estate_property_views.xml
@@ -0,0 +1,19 @@
+
+
+
+ Estate Property
+ estate.property
+ list,form
+
+
+ List all your properties
+
+
+ track thier rental state
+
+
+ track thier mentainance
+
+
+
+
\ No newline at end of file
From f8f582cd5277c83587425acf8f62204dbac0e650 Mon Sep 17 00:00:00 2001
From: djhan
Date: Wed, 19 Nov 2025 14:38:24 +0100
Subject: [PATCH 06/16] [IMP] estate: customize the view
---
estate/__manifest__.py | 5 +-
estate/models/estate_property.py | 23 ++++----
estate/views/estate_menus.xml | 2 +-
estate/views/estate_property_views.xml | 81 +++++++++++++++++++++++++-
4 files changed, 96 insertions(+), 15 deletions(-)
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index ccf43706f57..ea863f3c9de 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -5,6 +5,9 @@
'version': '1.0',
'category': 'Sales/Estate',
'sequence': 15,
+ 'description': """
+ This module is here to help you manage your real estate business.
+ """,
'summary': 'Track all the properties you own',
'website': 'https://www.odoo.com/app/estate',
'depends': [
@@ -19,8 +22,6 @@
],
'installable': True,
'application': True,
- 'assets': {
- },
'author': 'Odoo S.A.',
'license': 'LGPL-3',
}
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index ddb27a30d1b..b266c0183fc 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -4,19 +4,20 @@
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate properties"
- name = fields.Char('Property Name', required=True, translate=True)
- description = fields.Text('Property Description', required=True)
- postcode = fields.Char('Property postcode')
- date_availability = fields.Date('Property availability date', copy=False, default=fields.Date.add(fields.Date.today(), months=3))
- expected_price = fields.Float('Property expected price')
- selling_price = fields.Float('Property selling price', readonly=True)
+
+ name = fields.Char('Name', required=True, translate=True)
+ description = fields.Text('Description', required=True)
+ postcode = fields.Char('Postcode')
+ date_availability = fields.Date('Available From', copy=False, default=fields.Date.add(fields.Date.today(), months=3))
+ expected_price = fields.Float('Expected Price')
+ selling_price = fields.Float('Selling Price', readonly=True)
bedrooms = fields.Integer(default=2)
active = fields.Boolean(default=True)
- living_area = fields.Integer()
- facades = fields.Integer()
- garage = fields.Boolean()
- garden = fields.Boolean()
- garden_area = fields.Integer()
+ living_area = fields.Integer('Living Area (sqm)')
+ facades = fields.Integer('Facades')
+ garage = fields.Boolean('Garage')
+ garden = fields.Boolean('Garden')
+ garden_area = fields.Float('Garden Area (sqm)')
garden_orientation = fields.Selection(
string='Garden Orientation',
selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')])
diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml
index 579b3413f6f..cfd8dc8a797 100644
--- a/estate/views/estate_menus.xml
+++ b/estate/views/estate_menus.xml
@@ -5,4 +5,4 @@
-
\ No newline at end of file
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index 184e974ca19..b352257ccc1 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -1,5 +1,84 @@
+
+ estate.property.search
+ estate.property
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ estate.property.form
+ estate.property
+
+
+
+
+
+
+ estate.property.list
+ estate.property
+
+
+
+
+
+
+
+
+
+
+
+
+
Estate Property
estate.property
@@ -16,4 +95,4 @@
-
\ No newline at end of file
+
From 9803b98455d0741526ef5fa411f9692f87c4d365 Mon Sep 17 00:00:00 2001
From: djhan
Date: Wed, 19 Nov 2025 16:03:16 +0100
Subject: [PATCH 07/16] [IMP] estate: add property types
---
estate/__manifest__.py | 1 +
estate/models/__init__.py | 1 +
estate/models/estate_property.py | 1 +
estate/models/estate_property_type.py | 8 ++++++
estate/security/ir.model.access.csv | 2 ++
estate/views/estate_menus.xml | 5 +++-
estate/views/estate_property_type_views.xml | 29 +++++++++++++++++++++
estate/views/estate_property_views.xml | 11 ++++----
8 files changed, 52 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 ea863f3c9de..f1d36ce59a3 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -16,6 +16,7 @@
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
+ 'views/estate_property_type_views.xml',
'views/estate_menus.xml'
],
'demo': [
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index 5e1963c9d2f..97aee757823 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1 +1,2 @@
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 b266c0183fc..ed0a803bf0d 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -27,3 +27,4 @@ class EstateProperty(models.Model):
default='new',
copy=False,
selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')])
+ property_type_id = fields.Many2one("estate.property.type", "Type")
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
new file mode 100644
index 00000000000..d23aef9fd84
--- /dev/null
+++ b/estate/models/estate_property_type.py
@@ -0,0 +1,8 @@
+from odoo import models, fields
+
+
+class EstatePropertyType(models.Model):
+ _name = "estate.property.type"
+ _description = "Estate properties Types"
+
+ name = fields.Char('Name', required=True, translate=True)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 32389642d4f..eff4c41cdda 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
+
diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml
index cfd8dc8a797..21eae23dd0b 100644
--- a/estate/views/estate_menus.xml
+++ b/estate/views/estate_menus.xml
@@ -4,5 +4,8 @@
+
-
+
\ 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..adfe885fb82
--- /dev/null
+++ b/estate/views/estate_property_type_views.xml
@@ -0,0 +1,29 @@
+
+
+
+ estate.property.type.form
+ estate.property.type
+
+
+
+
+
+
+ Estate Property Types
+ estate.property.type
+ list,form
+
+
+ A property type is, for example, a house or an apartment.
+
+
+
+
\ No newline at end of file
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index b352257ccc1..fe111bffec2 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -6,6 +6,7 @@
+
@@ -14,10 +15,8 @@
-
-
-
+
@@ -35,6 +34,7 @@
+
@@ -69,6 +69,7 @@
+
@@ -95,4 +96,4 @@
-
+
\ No newline at end of file
From 4bd2716ea335b09dbe909ab3fb86f1daba3f19d6 Mon Sep 17 00:00:00 2001
From: djhan
Date: Wed, 19 Nov 2025 16:25:36 +0100
Subject: [PATCH 08/16] [FIX] estate: fix lint
---
estate/models/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index 97aee757823..40092a2d810 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1,2 +1,2 @@
from . import estate_property
-from . import estate_property_type
\ No newline at end of file
+from . import estate_property_type
From 98dc4fc4e223e726c6d9d9b72c0812644c0b37cf Mon Sep 17 00:00:00 2001
From: djhan
Date: Wed, 19 Nov 2025 17:13:34 +0100
Subject: [PATCH 09/16] [IMP] estate: add tags
---
estate/__manifest__.py | 1 +
estate/models/__init__.py | 1 +
estate/models/estate_property.py | 4 +++
estate/models/estate_property_tag.py | 8 ++++++
estate/security/ir.model.access.csv | 2 ++
estate/views/estate_menus.xml | 3 ++-
estate/views/estate_property_tag_views.xml | 29 ++++++++++++++++++++++
estate/views/estate_property_views.xml | 9 ++++++-
8 files changed, 55 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 f1d36ce59a3..0bbc7cc73e8 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -17,6 +17,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'
],
'demo': [
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index 40092a2d810..c620ac481a3 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1,2 +1,3 @@
from . import estate_property
from . import estate_property_type
+from . import estate_property_tag
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index ed0a803bf0d..b94aaa9ec2c 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -28,3 +28,7 @@ class EstateProperty(models.Model):
copy=False,
selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')])
property_type_id = fields.Many2one("estate.property.type", "Type")
+ property_tags_ids = fields.Many2many("estate.property.tag", "Tags")
+ sales_person_id = fields.Many2one('res.users', string='Salesperson', default=lambda self: self.env.user)
+ buyer_id = fields.Many2one('res.partner', string='Buyer', copy=False)
+
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
new file mode 100644
index 00000000000..2b7649b7fdd
--- /dev/null
+++ b/estate/models/estate_property_tag.py
@@ -0,0 +1,8 @@
+from odoo import models, fields
+
+
+class EstatePropertyTag(models.Model):
+ _name = "estate.property.tag"
+ _description = "Estate properties Tags"
+
+ name = fields.Char('Name', required=True, translate=True)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index eff4c41cdda..ec12e57436d 100644
--- a/estate/security/ir.model.access.csv
+++ b/estate/security/ir.model.access.csv
@@ -1,4 +1,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,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.xml b/estate/views/estate_menus.xml
index 21eae23dd0b..f0591a1d1aa 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..0f82ede750c
--- /dev/null
+++ b/estate/views/estate_property_tag_views.xml
@@ -0,0 +1,29 @@
+
+
+
+ estate.property.tag.form
+ estate.property.tag
+
+
+
+
+
+
+ Estate Property Tags
+ estate.property.tag
+ list,form
+
+
+ Property Tags.
+
+
+
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index fe111bffec2..a903283f4e0 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -31,6 +31,7 @@
+
@@ -57,6 +58,12 @@
+
+
+
+
+
+
@@ -96,4 +103,4 @@
-
\ No newline at end of file
+
From b5aaaa7e445b3f052e6e2a0f93b5048b1ac98cf7 Mon Sep 17 00:00:00 2001
From: djhan
Date: Thu, 20 Nov 2025 10:10:42 +0100
Subject: [PATCH 10/16] [IMP] estate: add offers
---
estate/__manifest__.py | 1 +
estate/models/__init__.py | 1 +
estate/models/estate_property.py | 2 +-
estate/models/estate_property_offer.py | 14 ++++++++++
estate/security/ir.model.access.csv | 3 +--
estate/views/estate_property_offer_views.xml | 28 ++++++++++++++++++++
estate/views/estate_property_type_views.xml | 2 +-
estate/views/estate_property_views.xml | 5 ++++
8 files changed, 52 insertions(+), 4 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 0bbc7cc73e8..aa92c004563 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -18,6 +18,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'
],
'demo': [
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index c620ac481a3..2f1821a39c1 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
+from . import estate_property_offer
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index b94aaa9ec2c..68f19611978 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -31,4 +31,4 @@ class EstateProperty(models.Model):
property_tags_ids = fields.Many2many("estate.property.tag", "Tags")
sales_person_id = fields.Many2one('res.users', string='Salesperson', default=lambda self: self.env.user)
buyer_id = fields.Many2one('res.partner', string='Buyer', copy=False)
-
+ 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..6fe7e5aa46d
--- /dev/null
+++ b/estate/models/estate_property_offer.py
@@ -0,0 +1,14 @@
+from odoo import models, fields
+
+
+class EstatePropertyOffer(models.Model):
+ _name = "estate.property.offer"
+ _description = "Estate properties Offers"
+
+ price = fields.Float('Price')
+ status = fields.Selection(
+ string='Status',
+ copy=False,
+ selection=[('accepted', 'Accepted'), ('refused', 'Refused')])
+ partner_id = fields.Many2one('res.partner', string='Buyer', required=True)
+ property_id = fields.Many2one('estate.property', string='Buyer', required=True)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index ec12e57436d..89f97c50842 100644
--- a/estate/security/ir.model.access.csv
+++ b/estate/security/ir.model.access.csv
@@ -2,5 +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,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..e2ee374fda1
--- /dev/null
+++ b/estate/views/estate_property_offer_views.xml
@@ -0,0 +1,28 @@
+
+
+
+ estate.property.offer.form
+ estate.property.offer
+
+
+
+
+
+
+ estate.property.offer.list
+ estate.property.offer
+
+
+
+
+
+
+
+
+
diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml
index adfe885fb82..7a191070c71 100644
--- a/estate/views/estate_property_type_views.xml
+++ b/estate/views/estate_property_type_views.xml
@@ -26,4 +26,4 @@
-
\ No newline at end of file
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index a903283f4e0..2ffc5945c4a 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -58,6 +58,11 @@
+
+
+
+
+
From 9b190ce2915963ef2bb0111332f8e0edd0fcc9f7 Mon Sep 17 00:00:00 2001
From: djhan
Date: Thu, 20 Nov 2025 13:31:41 +0100
Subject: [PATCH 11/16] [IMP] estate: add computed fields
---
estate/models/estate_property.py | 23 +++++++++++++++++++-
estate/models/estate_property_offer.py | 15 +++++++++++--
estate/views/estate_property_offer_views.xml | 6 ++++-
estate/views/estate_property_views.xml | 2 ++
4 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 68f19611978..8be2cb5d6ba 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,4 +1,4 @@
-from odoo import models, fields
+from odoo import models, fields, api
class EstateProperty(models.Model):
@@ -32,3 +32,24 @@ class EstateProperty(models.Model):
sales_person_id = fields.Many2one('res.users', string='Salesperson', default=lambda self: self.env.user)
buyer_id = fields.Many2one('res.partner', string='Buyer', copy=False)
offer_ids = fields.One2many('estate.property.offer', 'property_id', string="Offers")
+ total_area = fields.Float('Total Area', compute='_compute_total_area')
+ best_price = fields.Float('Best Offer', compute='_compute_best_price')
+
+ @api.depends('garden_area', 'living_area')
+ def _compute_total_area(self):
+ for record in self:
+ record.total_area = record.garden_area + record.living_area
+
+ @api.depends('offer_ids.price')
+ def _compute_best_price(self):
+ for record in self:
+ record.best_price = max(record.offer_ids, key=lambda p: p.price).price
+
+ @api.onchange('garden')
+ def _onchange_garden(self):
+ if not self.garden:
+ self.garden_area = None
+ self.garden_orientation = None
+ else:
+ self.garden_area = 10
+ self.garden_orientation = 'north'
\ No newline at end of file
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 6fe7e5aa46d..fd37df848fd 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -1,4 +1,4 @@
-from odoo import models, fields
+from odoo import models, fields ,api
class EstatePropertyOffer(models.Model):
@@ -11,4 +11,15 @@ class EstatePropertyOffer(models.Model):
copy=False,
selection=[('accepted', 'Accepted'), ('refused', 'Refused')])
partner_id = fields.Many2one('res.partner', string='Buyer', required=True)
- property_id = fields.Many2one('estate.property', string='Buyer', required=True)
+ property_id = fields.Many2one('estate.property', string='Estate Property', required=True)
+ validity = fields.Integer('Validity', default=7)
+ date_deadline = fields.Datetime('Deadline', compute='_compute_date_deadline', inverse='_inverse_date_deadline')
+
+ @api.depends('validity', 'create_date')
+ def _compute_date_deadline(self):
+ for record in self:
+ record.date_deadline = fields.Date.add(record.create_date, days=record.validity)
+
+ def _inverse_date_deadline(self):
+ for record in self:
+ record.validity = (record.date_deadline - record.create_date).days
diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml
index e2ee374fda1..945fde8c9fc 100644
--- a/estate/views/estate_property_offer_views.xml
+++ b/estate/views/estate_property_offer_views.xml
@@ -9,6 +9,8 @@
+
+
@@ -22,7 +24,9 @@
+
+
-
+
\ No newline at end of file
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index 2ffc5945c4a..0cb5291afca 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -41,6 +41,7 @@
+
@@ -56,6 +57,7 @@
+
From 5d4a3ff5b1958be09da307948ef8a11725b82e34 Mon Sep 17 00:00:00 2001
From: djhan
Date: Thu, 20 Nov 2025 14:37:38 +0100
Subject: [PATCH 12/16] [IMP] estate: add actions
---
estate/models/estate_property.py | 20 +++++++++++++++++--
estate/models/estate_property_offer.py | 21 ++++++++++++++++++--
estate/views/estate_property_offer_views.xml | 4 +++-
estate/views/estate_property_views.xml | 8 ++++++--
4 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 8be2cb5d6ba..301e77f1227 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,4 +1,4 @@
-from odoo import models, fields, api
+from odoo import models, fields, api, exceptions
class EstateProperty(models.Model):
@@ -52,4 +52,20 @@ def _onchange_garden(self):
self.garden_orientation = None
else:
self.garden_area = 10
- self.garden_orientation = 'north'
\ No newline at end of file
+ self.garden_orientation = 'north'
+
+ def action_cancel_property(self):
+ for record in self:
+ if record.state == 'sold':
+ raise exceptions.UserError('Sold properties cannot be cancelled')
+ else:
+ record.state = 'cancelled'
+ return True
+
+ def action_sold_property(self):
+ for record in self:
+ if record.state == 'cancelled':
+ raise exceptions.UserError('Cancelled properties cannot be sold')
+ else:
+ record.state = 'sold'
+ return True
\ No newline at end of file
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index fd37df848fd..3cb2c9753ac 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -1,4 +1,4 @@
-from odoo import models, fields ,api
+from odoo import models, fields ,api, exceptions
class EstatePropertyOffer(models.Model):
@@ -8,8 +8,9 @@ class EstatePropertyOffer(models.Model):
price = fields.Float('Price')
status = fields.Selection(
string='Status',
+ default='new',
copy=False,
- selection=[('accepted', 'Accepted'), ('refused', 'Refused')])
+ selection=[('accepted', 'Accepted'), ('refused', 'Refused'), ('new', 'New')])
partner_id = fields.Many2one('res.partner', string='Buyer', required=True)
property_id = fields.Many2one('estate.property', string='Estate Property', required=True)
validity = fields.Integer('Validity', default=7)
@@ -23,3 +24,19 @@ def _compute_date_deadline(self):
def _inverse_date_deadline(self):
for record in self:
record.validity = (record.date_deadline - record.create_date).days
+
+ def action_accept_offer(self):
+ if any(record.status == 'accepted' for record in self.property_id.offer_ids):
+ raise exceptions.UserError('An offer is already accepted for this property')
+ for record in self:
+ record.status = 'accepted'
+ record.property_id.buyer_id = record.partner_id
+ record.property_id.selling_price = record.price
+ return True
+
+ def action_refuse_offer(self):
+ for record in self:
+ if record.status == 'accepted':
+ raise exceptions.UserError('You cant refuse an already accepted offer')
+ record.status = 'refused'
+ return True
diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml
index 945fde8c9fc..4614f43d97a 100644
--- a/estate/views/estate_property_offer_views.xml
+++ b/estate/views/estate_property_offer_views.xml
@@ -22,10 +22,12 @@
-
+
+
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index 0cb5291afca..d1e5535299a 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -26,6 +26,10 @@
estate.property
@@ -26,4 +37,4 @@
-
+
\ No newline at end of file
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index d1e5535299a..ae1bb6f514d 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -29,6 +29,7 @@
@@ -52,7 +53,6 @@
-
From 3f82fdb00ac6717ecf8189a77f82c2d69b51b978 Mon Sep 17 00:00:00 2001
From: djhan
Date: Fri, 21 Nov 2025 09:31:23 +0100
Subject: [PATCH 16/16] [FIX] estate: fix comments
---
estate/models/estate_property.py | 21 ++++++++++++++------
estate/models/estate_property_offer.py | 2 +-
estate/models/estate_property_tag.py | 2 +-
estate/models/estate_property_type.py | 4 ++--
estate/views/estate_property_offer_views.xml | 8 ++++----
estate/views/estate_property_type_views.xml | 13 +++++++++++-
estate/views/estate_property_views.xml | 8 ++++----
7 files changed, 39 insertions(+), 19 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index cd016285089..b5c16f967d7 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -6,7 +6,7 @@
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate properties"
-
+ _order = "id desc"
name = fields.Char('Name', required=True, translate=True)
description = fields.Text('Description', required=True)
postcode = fields.Char('Postcode')
@@ -22,13 +22,24 @@ class EstateProperty(models.Model):
garden_area = fields.Float('Garden Area (sqm)')
garden_orientation = fields.Selection(
string='Garden Orientation',
- selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')])
+ selection=[
+ ('north', 'North'),
+ ('south', 'South'),
+ ('east', 'East'),
+ ('west', 'West')
+ ])
state = fields.Selection(
string='State',
required=True,
default='new',
copy=False,
- selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')])
+ selection=[
+ ('new', 'New'),
+ ('offer_received','Offer Received'),
+ ('offer_accepted', 'Offer Accepted'),
+ ('sold', 'Sold'),
+ ('cancelled', 'Cancelled')
+ ])
property_type_id = fields.Many2one("estate.property.type", "Type")
property_tags_ids = fields.Many2many("estate.property.tag", "Tags")
sales_person_id = fields.Many2one('res.users', string='Salesperson', default=lambda self: self.env.user)
@@ -54,9 +65,7 @@ def _compute_total_area(self):
@api.depends('offer_ids.price')
def _compute_best_price(self):
for record in self:
- if len(record.offer_ids)>0:
- record.best_price = max(record.offer_ids, key=lambda p: p.price).price
-
+ record.best_price = max(record.offer_ids.mapped('price'), default=0.0)
@api.onchange('garden')
def _onchange_garden(self):
if not self.garden:
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index c80f03648c9..c3ae1c1273e 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -4,7 +4,7 @@
class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Estate properties Offers"
-
+ _order = "price desc"
price = fields.Float('Price')
status = fields.Selection(
string='Status',
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
index c292c3735f3..f0c72ab64b7 100644
--- a/estate/models/estate_property_tag.py
+++ b/estate/models/estate_property_tag.py
@@ -4,7 +4,7 @@
class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Estate properties Tags"
-
+ _order = "name"
name = fields.Char('Name', required=True, translate=True)
_tags_uniq = models.Constraint(
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
index c7a0e944afd..71c0d94adac 100644
--- a/estate/models/estate_property_type.py
+++ b/estate/models/estate_property_type.py
@@ -4,10 +4,10 @@
class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Estate properties Types"
-
+ _order = "name"
name = fields.Char('Name', required=True, translate=True)
properties_ids = fields.One2many("estate.property", "property_type_id", "Properties")
-
+ sequence = fields.Integer('Sequence', default=1, help="Used to order stages. Lower is better.")
_types_uniq = models.Constraint(
'unique(name)',
"The type name already exists",
diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml
index 4614f43d97a..74f66a6f1de 100644
--- a/estate/views/estate_property_offer_views.xml
+++ b/estate/views/estate_property_offer_views.xml
@@ -20,15 +20,15 @@
estate.property.offer.list
estate.property.offer
-
+
-
-
+
+
-
\ No newline at end of file
+
diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml
index 969124c7e07..f848501672f 100644
--- a/estate/views/estate_property_type_views.xml
+++ b/estate/views/estate_property_type_views.xml
@@ -27,6 +27,17 @@
+
+ estate.property.type.list
+ estate.property.type
+
+
+
+
+
+
+
+
Estate Property Types
estate.property.type
@@ -37,4 +48,4 @@
-
\ No newline at end of file
+
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml
index ae1bb6f514d..42c9c3a472a 100644
--- a/estate/views/estate_property_views.xml
+++ b/estate/views/estate_property_views.xml
@@ -14,7 +14,7 @@
+ domain="[('state', 'in', ('new', 'offer_received'))]" />
@@ -29,7 +29,7 @@
@@ -85,7 +85,7 @@
estate.property.list
estate.property
-
+
@@ -114,4 +114,4 @@
-
\ No newline at end of file
+