From 04ed3ed126594f25a72edc971a055a2d7d747e33 Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Tue, 18 Nov 2025 13:16:52 +0100
Subject: [PATCH 01/16] [ADD] estate: init
---
estate/__init__.py | 2 ++
estate/__manifest__.py | 13 +++++++++++++
2 files changed, 15 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..a0fdc10fe11
--- /dev/null
+++ b/estate/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+from . import models
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
new file mode 100644
index 00000000000..f6526396474
--- /dev/null
+++ b/estate/__manifest__.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+{
+ 'name': "Real Estate",
+ 'summary': """
+ Testing the real estate module
+ """,
+
+ 'description': """
+ Testing the description real estate module
+ """,
+ 'application': True,
+ 'depends': ['base']
+}
From 186a4916a0baaaa3482dd04ccbee535274b19b2a Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Tue, 18 Nov 2025 13:53:01 +0100
Subject: [PATCH 02/16] [FIX] estate: removed imports
---
estate/__init__.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/estate/__init__.py b/estate/__init__.py
index a0fdc10fe11..40a96afc6ff 100644
--- a/estate/__init__.py
+++ b/estate/__init__.py
@@ -1,2 +1 @@
# -*- coding: utf-8 -*-
-from . import models
From e418d9a0fddec4ba1caf96026c34044cb73aa63f Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Tue, 18 Nov 2025 13:55:52 +0100
Subject: [PATCH 03/16] [IMP] estate: change to installable
---
estate/__manifest__.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index f6526396474..e955b3ad937 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -8,6 +8,7 @@
'description': """
Testing the description real estate module
""",
+ 'depends': ['base'],
'application': True,
- 'depends': ['base']
+ 'installable': True,
}
From d4cf70e49c9ea8323e6ccde8b5b66c5b1bb5a4e1 Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Tue, 18 Nov 2025 14:39:18 +0100
Subject: [PATCH 04/16] [IMP] estate: Models and basic fields
---
estate/__init__.py | 1 +
estate/models/__init__.py | 1 +
estate/models/estate.py | 19 +++++++++++++++++++
3 files changed, 21 insertions(+)
create mode 100644 estate/models/__init__.py
create mode 100644 estate/models/estate.py
diff --git a/estate/__init__.py b/estate/__init__.py
index 40a96afc6ff..f5ba686bc21 100644
--- a/estate/__init__.py
+++ b/estate/__init__.py
@@ -1 +1,2 @@
# -*- coding: utf-8 -*-
+from . import models
\ No newline at end of file
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
new file mode 100644
index 00000000000..912e8d6a61f
--- /dev/null
+++ b/estate/models/__init__.py
@@ -0,0 +1 @@
+from . import estate
\ No newline at end of file
diff --git a/estate/models/estate.py b/estate/models/estate.py
new file mode 100644
index 00000000000..878f2b960eb
--- /dev/null
+++ b/estate/models/estate.py
@@ -0,0 +1,19 @@
+from odoo import models, fields
+
+class EstateProperty(models.Model):
+ _name = "estate.property"
+
+ _description = "Estate property model"
+ name = fields.Char('Property name', required=True,)
+ description = fields.Text('Property description')
+ postcode = fields.Char()
+ expected_price = fields.Float('Property expected price', required = True)
+ date_availability = fields.Date()
+ selling_price = fields.Float()
+ bedrooms = fields.Integer()
+ living_area = fields.Integer()
+ facades = fields.Integer()
+ garage = fields.Boolean()
+ garden = fields.Boolean()
+ garden_area = fields.Integer()
+ garden_orientation = fields.Selection(string = 'Orientation',selection=[("north", "N"), ("south", "S"), ("east", "E"), ("west", "W")], help = 'Orientation of the garden')
From a05b607aae937e61997645c2f5f4d4084e217d0d Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Tue, 18 Nov 2025 15:02:35 +0100
Subject: [PATCH 05/16] [IMP] estate: access rights and security
---
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 e955b3ad937..c58df372df2 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -9,6 +9,9 @@
Testing the description real estate module
""",
'depends': ['base'],
+ 'data': [
+ 'security/ir.model.access.csv',
+ ],
'application': True,
'installable': True,
}
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
new file mode 100644
index 00000000000..0e11f47e58d
--- /dev/null
+++ b/estate/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
\ No newline at end of file
From 4f494dc8b11d798480094a14f669e02548d06fe7 Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Tue, 18 Nov 2025 16:32:56 +0100
Subject: [PATCH 06/16] [IMP] estate: UI activated and more fields added
---
estate/__init__.py | 3 +--
estate/__manifest__.py | 3 ++-
estate/models/__init__.py | 2 +-
estate/models/estate.py | 19 ++++++++++++-------
estate/views/estate_menus.xml | 8 ++++++++
estate/views/estate_property_view.xml | 13 +++++++++++++
6 files changed, 37 insertions(+), 11 deletions(-)
create mode 100644 estate/views/estate_menus.xml
create mode 100644 estate/views/estate_property_view.xml
diff --git a/estate/__init__.py b/estate/__init__.py
index f5ba686bc21..0650744f6bc 100644
--- a/estate/__init__.py
+++ b/estate/__init__.py
@@ -1,2 +1 @@
-# -*- coding: utf-8 -*-
-from . import models
\ No newline at end of file
+from . import models
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index c58df372df2..325a0d676f4 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
{
'name': "Real Estate",
'summary': """
@@ -11,6 +10,8 @@
'depends': ['base'],
'data': [
'security/ir.model.access.csv',
+ 'views/estate_property_view.xml',
+ 'views/estate_menus.xml'
],
'application': True,
'installable': True,
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index 912e8d6a61f..e4f59229d23 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1 +1 @@
-from . import estate
\ No newline at end of file
+from . import estate
diff --git a/estate/models/estate.py b/estate/models/estate.py
index 878f2b960eb..2d7ff1737c5 100644
--- a/estate/models/estate.py
+++ b/estate/models/estate.py
@@ -1,19 +1,24 @@
from odoo import models, fields
+from dateutil.relativedelta import relativedelta
+
class EstateProperty(models.Model):
_name = "estate.property"
-
_description = "Estate property model"
- name = fields.Char('Property name', required=True,)
+
+ name = fields.Char('Property name', required=True, default="Unknown")
description = fields.Text('Property description')
postcode = fields.Char()
- expected_price = fields.Float('Property expected price', required = True)
- date_availability = fields.Date()
- selling_price = fields.Float()
- bedrooms = fields.Integer()
+ expected_price = fields.Float('Property expected price', required=True)
+ date_availability = fields.Date(default=fields.Date.today() + relativedelta(months=3), copy=False)
+ selling_price = fields.Float(readonly=True, copy=False)
+ bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
- garden_orientation = fields.Selection(string = 'Orientation',selection=[("north", "N"), ("south", "S"), ("east", "E"), ("west", "W")], help = 'Orientation of the garden')
+ garden_orientation = fields.Selection(string='Orientation', selection=[("north", "N"), ("south", "S"), ("east", "E"), ("west", "W")], help='Orientation of the garden')
+ last_seen = fields.Datetime("Last Seen", default=fields.Datetime.now)
+ active = fields.Boolean(default=True)
+ state = fields.Selection(string='State', selection=[("new", "New"), ("offer_received", "Offer Received"), ("offer_accepted", "Offer Accepted"), ("sold", "Sold"), ("cancelled", "Cancelled")], default='new')
diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml
new file mode 100644
index 00000000000..5c198a7b812
--- /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_view.xml b/estate/views/estate_property_view.xml
new file mode 100644
index 00000000000..a78899e2d50
--- /dev/null
+++ b/estate/views/estate_property_view.xml
@@ -0,0 +1,13 @@
+
+
+
+ Estate Property
+ estate.property
+ list,form
+
+
+ Welcome to the help of estate property
+
+
+
+
\ No newline at end of file
From 66b5efaf645b25ba955e3fd6ff90103c3bcd877b Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Tue, 18 Nov 2025 16:55:07 +0100
Subject: [PATCH 07/16] [FIX] estate: CI compliance
---
estate/__manifest__.py | 2 ++
estate/models/estate.py | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 325a0d676f4..d72224bb21e 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -15,4 +15,6 @@
],
'application': True,
'installable': True,
+ 'author': "Odoo",
+ 'license': 'AGPL-3'
}
diff --git a/estate/models/estate.py b/estate/models/estate.py
index 2d7ff1737c5..9d3b95c2b80 100644
--- a/estate/models/estate.py
+++ b/estate/models/estate.py
@@ -5,7 +5,7 @@
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate property model"
-
+
name = fields.Char('Property name', required=True, default="Unknown")
description = fields.Text('Property description')
postcode = fields.Char()
From a97811c74240c8647f2cc8c45a7ea568b5a12f01 Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Wed, 19 Nov 2025 11:02:42 +0100
Subject: [PATCH 08/16] [LINT] estate: adapting to git and code guideline
---
estate/__manifest__.py | 7 ++----
estate/models/estate.py | 36 +++++++++++++++++++++------
estate/security/ir.model.access.csv | 2 +-
estate/views/estate_menus.xml | 2 +-
estate/views/estate_property_view.xml | 2 +-
5 files changed, 33 insertions(+), 16 deletions(-)
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index d72224bb21e..b073bd4e253 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -1,8 +1,6 @@
{
'name': "Real Estate",
- 'summary': """
- Testing the real estate module
- """,
+ 'summary': "Testing the real estate module",
'description': """
Testing the description real estate module
@@ -14,7 +12,6 @@
'views/estate_menus.xml'
],
'application': True,
- 'installable': True,
'author': "Odoo",
- 'license': 'AGPL-3'
+ 'license': 'LGPL-3',
}
diff --git a/estate/models/estate.py b/estate/models/estate.py
index 9d3b95c2b80..43941c34949 100644
--- a/estate/models/estate.py
+++ b/estate/models/estate.py
@@ -1,16 +1,17 @@
-from odoo import models, fields
-from dateutil.relativedelta import relativedelta
+from odoo import fields, models
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate property model"
- name = fields.Char('Property name', required=True, default="Unknown")
- description = fields.Text('Property description')
+ name = fields.Char("Property name", required=True, default="Unknown")
+ description = fields.Text("Property description")
postcode = fields.Char()
- expected_price = fields.Float('Property expected price', required=True)
- date_availability = fields.Date(default=fields.Date.today() + relativedelta(months=3), copy=False)
+ expected_price = fields.Float("Property expected price", required=True)
+ date_availability = fields.Date(
+ default=fields.Date.add(fields.Date.today(), months=3), copy=False
+ )
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
@@ -18,7 +19,26 @@ class EstateProperty(models.Model):
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
- garden_orientation = fields.Selection(string='Orientation', selection=[("north", "N"), ("south", "S"), ("east", "E"), ("west", "W")], help='Orientation of the garden')
+ garden_orientation = fields.Selection(
+ string="Orientation",
+ selection=[
+ ("north", "N"),
+ ("south", "S"),
+ ("east", "E"),
+ ("west", "W")
+ ],
+ help="Orientation of the garden",
+ )
last_seen = fields.Datetime("Last Seen", default=fields.Datetime.now)
active = fields.Boolean(default=True)
- state = fields.Selection(string='State', selection=[("new", "New"), ("offer_received", "Offer Received"), ("offer_accepted", "Offer Accepted"), ("sold", "Sold"), ("cancelled", "Cancelled")], default='new')
+ state = fields.Selection(
+ string="State",
+ selection=[
+ ("new", "New"),
+ ("offer_received", "Offer Received"),
+ ("offer_accepted", "Offer Accepted"),
+ ("sold", "Sold"),
+ ("cancelled", "Cancelled"),
+ ],
+ default="new",
+ )
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 0e11f47e58d..32389642d4f 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_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
\ No newline at end of file
+access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml
index 5c198a7b812..8b30a644b7d 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_view.xml b/estate/views/estate_property_view.xml
index a78899e2d50..e53322f5376 100644
--- a/estate/views/estate_property_view.xml
+++ b/estate/views/estate_property_view.xml
@@ -10,4 +10,4 @@
-
\ No newline at end of file
+
From b011fb1f36442519892d6b263fa8fe832feb27bb Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Wed, 19 Nov 2025 14:44:20 +0100
Subject: [PATCH 09/16] [IMP] estate: adjusted access rights improved views
and search filters
---
estate/models/estate.py | 20 ++++---
estate/security/ir.model.access.csv | 3 +-
estate/views/estate_property_view.xml | 79 +++++++++++++++++++++++++++
3 files changed, 92 insertions(+), 10 deletions(-)
diff --git a/estate/models/estate.py b/estate/models/estate.py
index 43941c34949..fd2b7d27575 100644
--- a/estate/models/estate.py
+++ b/estate/models/estate.py
@@ -5,16 +5,18 @@ class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate property model"
- name = fields.Char("Property name", required=True, default="Unknown")
- description = fields.Text("Property description")
- postcode = fields.Char()
- expected_price = fields.Float("Property expected price", required=True)
+ name = fields.Char("Title", required=True, default="Unknown")
+ description = fields.Text("Description")
+ postcode = fields.Char("Postcode")
+ expected_price = fields.Float("Expected Price", required=True)
date_availability = fields.Date(
- default=fields.Date.add(fields.Date.today(), months=3), copy=False
+ "Available From",
+ default=fields.Date.add(fields.Date.today(), months=3),
+ copy=False,
)
- selling_price = fields.Float(readonly=True, copy=False)
- bedrooms = fields.Integer(default=2)
- living_area = fields.Integer()
+ selling_price = fields.Float("Selling Price", readonly=True, copy=False)
+ bedrooms = fields.Integer("Bedrooms", default=2)
+ living_area = fields.Integer("Living Area (sqm)")
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
@@ -25,7 +27,7 @@ class EstateProperty(models.Model):
("north", "N"),
("south", "S"),
("east", "E"),
- ("west", "W")
+ ("west", "W"),
],
help="Orientation of the garden",
)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 32389642d4f..8d817d69903 100644
--- a/estate/security/ir.model.access.csv
+++ b/estate/security/ir.model.access.csv
@@ -1,2 +1,3 @@
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,access_estate_property,model_estate_property,base.group_system,1,1,1,1
+view_estate_property,view_estate_property,model_estate_property,base.group_user,1,0,0,0
diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml
index e53322f5376..00cea38d618 100644
--- a/estate/views/estate_property_view.xml
+++ b/estate/views/estate_property_view.xml
@@ -1,5 +1,6 @@
+
Estate Property
estate.property
@@ -10,4 +11,82 @@
+
+
+ estate.property.form
+ estate.property
+
+
+
+
+
+
+ estate.property.list
+ estate.property
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ estate.property.search
+ estate.property
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 4bf0325bf71abd029d3efcdd575b28064117f58c Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Wed, 19 Nov 2025 14:45:56 +0100
Subject: [PATCH 10/16] [LINT] estate: remove whitespace
---
estate/models/estate.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/estate/models/estate.py b/estate/models/estate.py
index fd2b7d27575..0d4be8249e2 100644
--- a/estate/models/estate.py
+++ b/estate/models/estate.py
@@ -25,8 +25,8 @@ class EstateProperty(models.Model):
string="Orientation",
selection=[
("north", "N"),
- ("south", "S"),
- ("east", "E"),
+ ("south", "S"),
+ ("east", "E"),
("west", "W"),
],
help="Orientation of the garden",
From d749764a6bbb2b48a01b7e488a4abf112f797dd4 Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Wed, 19 Nov 2025 15:31:45 +0100
Subject: [PATCH 11/16] [FIX] estate: xml views error fix
---
estate/views/estate_property_view.xml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml
index 00cea38d618..7ddcc0a6c56 100644
--- a/estate/views/estate_property_view.xml
+++ b/estate/views/estate_property_view.xml
@@ -77,13 +77,13 @@
-
+
-
-
-
-
-
+
+
+
+
+
From 577573a7cf88447f808477a1e4686aaa16466540 Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Wed, 19 Nov 2025 17:39:37 +0100
Subject: [PATCH 12/16] [IMP] estate : adding new foreign fields for tags,
types and offers to property model
---
estate/__manifest__.py | 5 ++-
estate/models/__init__.py | 7 +++-
.../models/{estate.py => estate_property.py} | 26 ++++++++++----
estate/models/estate_property_offer.py | 23 +++++++++++++
estate/models/estate_property_tag.py | 8 +++++
estate/models/estate_property_type.py | 8 +++++
estate/security/ir.model.access.csv | 3 ++
estate/views/estate_menus.xml | 4 +++
estate/views/estate_property_offer_view.xml | 34 +++++++++++++++++++
estate/views/estate_property_tag_view.xml | 8 +++++
estate/views/estate_property_type_view.xml | 8 +++++
estate/views/estate_property_view.xml | 15 +++++++-
12 files changed, 140 insertions(+), 9 deletions(-)
rename estate/models/{estate.py => estate_property.py} (67%)
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_view.xml
create mode 100644 estate/views/estate_property_tag_view.xml
create mode 100644 estate/views/estate_property_type_view.xml
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index b073bd4e253..56f73fb8c83 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -8,8 +8,11 @@
'depends': ['base'],
'data': [
'security/ir.model.access.csv',
+ 'views/estate_property_offer_view.xml',
'views/estate_property_view.xml',
- 'views/estate_menus.xml'
+ 'views/estate_property_type_view.xml',
+ 'views/estate_property_tag_view.xml',
+ 'views/estate_menus.xml',
],
'application': True,
'author': "Odoo",
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index e4f59229d23..3683ff97b61 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1 +1,6 @@
-from . import estate
+from . import (
+ estate_property,
+ estate_property_offer,
+ estate_property_tag,
+ estate_property_type,
+)
diff --git a/estate/models/estate.py b/estate/models/estate_property.py
similarity index 67%
rename from estate/models/estate.py
rename to estate/models/estate_property.py
index 0d4be8249e2..550883f26b0 100644
--- a/estate/models/estate.py
+++ b/estate/models/estate_property.py
@@ -23,12 +23,7 @@ class EstateProperty(models.Model):
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string="Orientation",
- selection=[
- ("north", "N"),
- ("south", "S"),
- ("east", "E"),
- ("west", "W"),
- ],
+ selection=[("north", "N"), ("south", "S"), ("east", "E"), ("west", "W")],
help="Orientation of the garden",
)
last_seen = fields.Datetime("Last Seen", default=fields.Datetime.now)
@@ -44,3 +39,22 @@ class EstateProperty(models.Model):
],
default="new",
)
+
+ property_type_id = fields.Many2one(
+ 'estate.property.type', string='Property Type', ondelete='restrict'
+ )
+
+ salesperson_id = fields.Many2one(
+ 'res.partner',
+ string='Salesman',
+ ondelete='restrict',
+ default=lambda self: self.env.user.partner_id,
+ )
+
+ buyer_id = fields.Many2one(
+ 'res.users', string='Buyer', ondelete='restrict', copy=False
+ )
+
+ property_tags_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..a93bbadfe86
--- /dev/null
+++ b/estate/models/estate_property_offer.py
@@ -0,0 +1,23 @@
+from odoo import fields, models
+
+
+class EstatePropertyOffer(models.Model):
+ _name = "estate.property.offer"
+ _description = "Estate property offer"
+
+ price = fields.Float("Expected Price")
+ status = fields.Selection(
+ string="Status", selection=[("yes", "Accepted"), ("no", "Refused")], copy=False
+ )
+
+ partner_id = fields.Many2one(
+ 'res.partner',
+ string='Salesman',
+ ondelete='restrict',
+ default=lambda self: self.env.user.partner_id,
+ required=True,
+ )
+
+ property_id = fields.Many2one(
+ 'estate.property', string='Property', ondelete='restrict', required=True
+ )
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
new file mode 100644
index 00000000000..7bf04d1a55a
--- /dev/null
+++ b/estate/models/estate_property_tag.py
@@ -0,0 +1,8 @@
+from odoo import fields, models
+
+
+class EstatePropertyTag(models.Model):
+ _name = "estate.property.tag"
+ _description = "Estate property tag"
+
+ name = fields.Char('Property Tag', required=True)
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
new file mode 100644
index 00000000000..6c5cc5602de
--- /dev/null
+++ b/estate/models/estate_property_type.py
@@ -0,0 +1,8 @@
+from odoo import fields, models
+
+
+class EstatePropertyType(models.Model):
+ _name = "estate.property.type"
+ _description = "Estate property type"
+
+ name = fields.Char('Property Type', required=True)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 8d817d69903..d9bc9fdd546 100644
--- a/estate/security/ir.model.access.csv
+++ b/estate/security/ir.model.access.csv
@@ -1,3 +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_system,1,1,1,1
+access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_system,1,1,1,1
+access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_system,1,1,1,1
+access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_system,1,1,1,1
view_estate_property,view_estate_property,model_estate_property,base.group_user,1,0,0,0
diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml
index 8b30a644b7d..93a193780d3 100644
--- a/estate/views/estate_menus.xml
+++ b/estate/views/estate_menus.xml
@@ -4,5 +4,9 @@
+
diff --git a/estate/views/estate_property_offer_view.xml b/estate/views/estate_property_offer_view.xml
new file mode 100644
index 00000000000..2d135694a8a
--- /dev/null
+++ b/estate/views/estate_property_offer_view.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ estate.property.offer.form
+ estate.property.offer
+
+
+
+
+
+
+ estate.property.offer.list
+ estate.property.offer
+
+
+
+
+
+
+
+
+
+
diff --git a/estate/views/estate_property_tag_view.xml b/estate/views/estate_property_tag_view.xml
new file mode 100644
index 00000000000..c45a547f151
--- /dev/null
+++ b/estate/views/estate_property_tag_view.xml
@@ -0,0 +1,8 @@
+
+
+
+ Property Tags
+ estate.property.tag
+ list
+
+
diff --git a/estate/views/estate_property_type_view.xml b/estate/views/estate_property_type_view.xml
new file mode 100644
index 00000000000..ab77adaf4ab
--- /dev/null
+++ b/estate/views/estate_property_type_view.xml
@@ -0,0 +1,8 @@
+
+
+
+ Property Types
+ estate.property.type
+ list
+
+
diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml
index 7ddcc0a6c56..00943004de8 100644
--- a/estate/views/estate_property_view.xml
+++ b/estate/views/estate_property_view.xml
@@ -2,7 +2,7 @@
- Estate Property
+ Real Estate Properties
estate.property
list,form
@@ -27,6 +27,8 @@
+
+
@@ -49,6 +51,17 @@
+
+
+
+
+
+
+
+
+
+
+
From b28ab2fc8c8c1d4da119a99b4c3ef31a70aa00bb Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Thu, 20 Nov 2025 10:53:07 +0100
Subject: [PATCH 13/16] [IMP] estate: computed properties for garden
description and offer validiy
---
estate/models/estate_property.py | 23 ++++++++++++++++++++-
estate/models/estate_property_offer.py | 19 +++++++++++++++--
estate/views/estate_property_offer_view.xml | 4 ++++
estate/views/estate_property_view.xml | 2 ++
4 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 550883f26b0..c4880174da9 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,4 +1,4 @@
-from odoo import fields, models
+from odoo import api, fields, models
class EstateProperty(models.Model):
@@ -58,3 +58,24 @@ class EstateProperty(models.Model):
property_tags_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
+ total_area = fields.Float(compute="_compute_total_area")
+ best_offer = fields.Float(compute="_compute_best_offer")
+
+ @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")
+ def _compute_best_offer(self):
+ for record in self:
+ record.best_offer = 0
+ for offer in record.offer_ids:
+ record.best_offer = max(record.best_offer, offer.price)
+
+ @api.onchange("garden")
+ def _onchange_garden(self):
+ if self.garden:
+ self.garden_area, self.garden_orientation = 10, "north"
+ else:
+ self.garden_area, self.garden_orientation = 0, None
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index a93bbadfe86..e3644a0e740 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -1,4 +1,4 @@
-from odoo import fields, models
+from odoo import api, fields, models
class EstatePropertyOffer(models.Model):
@@ -9,6 +9,11 @@ class EstatePropertyOffer(models.Model):
status = fields.Selection(
string="Status", selection=[("yes", "Accepted"), ("no", "Refused")], copy=False
)
+ validity = fields.Integer(default=7)
+ offer_creation_date = fields.Date(default=fields.Date.today())
+ deadline = fields.Date(
+ "Offer Deadline", compute="_compute_deadline", inverse="_inverse_deadline"
+ )
partner_id = fields.Many2one(
'res.partner',
@@ -17,7 +22,17 @@ class EstatePropertyOffer(models.Model):
default=lambda self: self.env.user.partner_id,
required=True,
)
-
property_id = fields.Many2one(
'estate.property', string='Property', ondelete='restrict', required=True
)
+
+ @api.depends("validity", "offer_creation_date")
+ def _compute_deadline(self):
+ for record in self:
+ record.deadline = fields.Date.add(
+ record.offer_creation_date, days=record.validity
+ )
+
+ def _inverse_deadline(self):
+ for record in self:
+ record.validity = (record.deadline - record.offer_creation_date).days
diff --git a/estate/views/estate_property_offer_view.xml b/estate/views/estate_property_offer_view.xml
index 2d135694a8a..88fa0f96717 100644
--- a/estate/views/estate_property_offer_view.xml
+++ b/estate/views/estate_property_offer_view.xml
@@ -11,6 +11,8 @@
+
+
@@ -26,6 +28,8 @@
+
+
diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml
index 00943004de8..8a473957070 100644
--- a/estate/views/estate_property_view.xml
+++ b/estate/views/estate_property_view.xml
@@ -34,6 +34,7 @@
+
@@ -49,6 +50,7 @@
+
From 2e71ec26c369c529b3065340bfd460a060bc1fdb Mon Sep 17 00:00:00 2001
From: "Hazem (haabo)"
Date: Thu, 20 Nov 2025 13:48:45 +0100
Subject: [PATCH 14/16] [IMP] estate: ch9 actions to modify property status and
accept offers added
---
estate/models/estate_property.py | 21 ++++++++++++--
estate/models/estate_property_offer.py | 31 +++++++++++++++++++--
estate/views/estate_property_offer_view.xml | 2 ++
estate/views/estate_property_view.xml | 4 ++-
4 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index c4880174da9..60c0e8d3af3 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,4 +1,5 @@
-from odoo import api, fields, models
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
class EstateProperty(models.Model):
@@ -45,14 +46,14 @@ class EstateProperty(models.Model):
)
salesperson_id = fields.Many2one(
- 'res.partner',
+ 'res.users',
string='Salesman',
ondelete='restrict',
default=lambda self: self.env.user.partner_id,
)
buyer_id = fields.Many2one(
- 'res.users', string='Buyer', ondelete='restrict', copy=False
+ 'res.partner', string='Buyer', ondelete='restrict', copy=False, readonly=True
)
property_tags_ids = fields.Many2many("estate.property.tag", string="Tags")
@@ -79,3 +80,17 @@ def _onchange_garden(self):
self.garden_area, self.garden_orientation = 10, "north"
else:
self.garden_area, self.garden_orientation = 0, None
+
+ def action_mark_as_sold(self):
+ for record in self:
+ if record.state == "cancelled":
+ raise UserError(_("Canceled properties cannot be sold."))
+ record.state = "sold"
+ return True
+
+ def action_mark_as_cancelled(self):
+ for record in self:
+ if record.state == "sold":
+ raise UserError(_("Sold properties cannot be cancelled."))
+ record.state = "cancelled"
+ return True
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index e3644a0e740..d5b9981716d 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -1,10 +1,19 @@
-from odoo import api, fields, models
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Estate property offer"
+ @api.model_create_multi
+ def create(self, vals_list):
+ records = super().create(vals_list)
+ for record in records:
+ if record.property_id.state == "new":
+ record.property_id.state = "offer_received"
+ return records
+
price = fields.Float("Expected Price")
status = fields.Selection(
string="Status", selection=[("yes", "Accepted"), ("no", "Refused")], copy=False
@@ -17,7 +26,7 @@ class EstatePropertyOffer(models.Model):
partner_id = fields.Many2one(
'res.partner',
- string='Salesman',
+ string='Buyer',
ondelete='restrict',
default=lambda self: self.env.user.partner_id,
required=True,
@@ -36,3 +45,21 @@ def _compute_deadline(self):
def _inverse_deadline(self):
for record in self:
record.validity = (record.deadline - record.offer_creation_date).days
+
+ def action_accept_offer(self):
+ for record in self:
+ for offer in record.property_id.offer_ids:
+ if offer.status == "yes":
+ raise UserError(_("Already accepted an offer for this property."))
+ record.status = "yes"
+ record.property_id.buyer_id = record.partner_id
+ record.property_id.selling_price = record.price
+ record.property_id.state = "offer_accepted"
+ return True
+
+ def action_reject_offer(self):
+ for record in self:
+ record.property_id.buyer_id = None
+ record.property_id.selling_price = 0
+ record.status = "no"
+ return True
diff --git a/estate/views/estate_property_offer_view.xml b/estate/views/estate_property_offer_view.xml
index 88fa0f96717..1921bf60d85 100644
--- a/estate/views/estate_property_offer_view.xml
+++ b/estate/views/estate_property_offer_view.xml
@@ -31,6 +31,8 @@
+
+
diff --git a/estate/views/estate_property_view.xml b/estate/views/estate_property_view.xml
index 8a473957070..d3e34df8aae 100644
--- a/estate/views/estate_property_view.xml
+++ b/estate/views/estate_property_view.xml
@@ -18,7 +18,9 @@