From 11a9e659906aa92c56d3fc0bbf788101eefa7a0d Mon Sep 17 00:00:00 2001
From: odoo
Date: Tue, 21 Apr 2026 16:05:52 +0200
Subject: [PATCH 01/19] [ADD] Chapitre 3
---
estate/__init__.py | 1 +
estate/__manifest__.py | 9 +++++++++
estate/models/__init__.py | 1 +
estate/models/estate_property.py | 21 +++++++++++++++++++++
4 files changed, 32 insertions(+)
create mode 100644 estate/__init__.py
create mode 100644 estate/__manifest__.py
create mode 100644 estate/models/__init__.py
create mode 100644 estate/models/estate_property.py
diff --git a/estate/__init__.py b/estate/__init__.py
new file mode 100644
index 00000000000..9a7e03eded3
--- /dev/null
+++ b/estate/__init__.py
@@ -0,0 +1 @@
+from . import models
\ No newline at end of file
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
new file mode 100644
index 00000000000..9fe7e6c15dc
--- /dev/null
+++ b/estate/__manifest__.py
@@ -0,0 +1,9 @@
+{
+ 'name': 'estate',
+ 'depends': [
+ 'base_setup'
+ ],
+ 'installable': True,
+ 'application': True,
+
+}
\ No newline at end of file
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
new file mode 100644
index 00000000000..f4c8fd6db6d
--- /dev/null
+++ b/estate/models/__init__.py
@@ -0,0 +1 @@
+from . import estate_property
\ No newline at end of file
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
new file mode 100644
index 00000000000..4b154c93caf
--- /dev/null
+++ b/estate/models/estate_property.py
@@ -0,0 +1,21 @@
+from odoo import models, fields
+
+class Estate_property(models.Model):
+ _name = "estate_property"
+ _description = "APP super mega trop bien"
+
+ name = fields.Char(required=True)
+ description = fields.Text()
+ postcode = fields.Char()
+ date_availability = fields.Date()
+ expected_price = fields.Float(required=True)
+ 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=[('notrh', 'Notrh'), ('south', 'South'), ('east', 'East'), ('west', 'West')],
+ help="The garden orientation")
\ No newline at end of file
From e8c454226348b0229a4f256ecad185b34555b50e Mon Sep 17 00:00:00 2001
From: odoo
Date: Tue, 21 Apr 2026 16:42:27 +0200
Subject: [PATCH 02/19] [ADD] Chapitre 4
---
estate/__manifest__.py | 5 ++++-
estate/security/ir.model.access.csv | 2 ++
2 files changed, 6 insertions(+), 1 deletion(-)
create mode 100644 estate/security/ir.model.access.csv
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 9fe7e6c15dc..1e5d74031ef 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -1,9 +1,12 @@
{
'name': 'estate',
'depends': [
- 'base_setup'
+ 'base'
],
'installable': True,
'application': True,
+ 'author': 'vibad',
+ 'data': ['security/ir.model.access.csv']
+
}
\ No newline at end of file
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
new file mode 100644
index 00000000000..03f4c262708
--- /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
+acces_estate_property,acces_estate_property,model_estate_property,base.group_user,1,1,1,1
\ No newline at end of file
From ebe26b3f323d540a7b5c40b9043221e00c1ed551 Mon Sep 17 00:00:00 2001
From: odoo
Date: Tue, 21 Apr 2026 17:28:10 +0200
Subject: [PATCH 03/19] [ADD] Chapitre 5 Menus
---
estate/__manifest__.py | 2 +-
estate/view/estate_action.xml | 8 ++++++++
estate/view/estate_property_views.xml | 8 ++++++++
3 files changed, 17 insertions(+), 1 deletion(-)
create mode 100644 estate/view/estate_action.xml
create mode 100644 estate/view/estate_property_views.xml
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 1e5d74031ef..2be577eb432 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -6,7 +6,7 @@
'installable': True,
'application': True,
'author': 'vibad',
- 'data': ['security/ir.model.access.csv']
+ 'data': ['security/ir.model.access.csv', 'view/estate_property_views.xml', 'view/estate_action.xml']
}
\ No newline at end of file
diff --git a/estate/view/estate_action.xml b/estate/view/estate_action.xml
new file mode 100644
index 00000000000..3e734eb981b
--- /dev/null
+++ b/estate/view/estate_action.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/estate/view/estate_property_views.xml b/estate/view/estate_property_views.xml
new file mode 100644
index 00000000000..825ae24d7d4
--- /dev/null
+++ b/estate/view/estate_property_views.xml
@@ -0,0 +1,8 @@
+
+
+
+ Properties
+ estate_property
+ list,form
+
+
\ No newline at end of file
From 760eba1b6e1c0d9dabb87555dbf0f6a0ba60d289 Mon Sep 17 00:00:00 2001
From: vibad
Date: Wed, 22 Apr 2026 10:03:47 +0200
Subject: [PATCH 04/19] [CLN] clean style
---
estate/__init__.py | 2 +-
estate/__manifest__.py | 8 ++++++--
estate/models/__init__.py | 2 +-
estate/models/estate_property.py | 9 ++++++---
estate/security/ir.model.access.csv | 2 +-
estate/view/estate_action.xml | 4 ++--
estate/view/estate_property_views.xml | 2 +-
7 files changed, 18 insertions(+), 11 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 2be577eb432..39e60af8cc9 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -6,7 +6,11 @@
'installable': True,
'application': True,
'author': 'vibad',
- 'data': ['security/ir.model.access.csv', 'view/estate_property_views.xml', 'view/estate_action.xml']
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'view/estate_property_views.xml',
+ 'view/estate_action.xml',
+ ],
-}
\ No newline at end of file
+}
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index f4c8fd6db6d..5e1963c9d2f 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1 +1 @@
-from . import estate_property
\ No newline at end of file
+from . import estate_property
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 4b154c93caf..a7e8421a730 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,5 +1,6 @@
from odoo import models, fields
+
class Estate_property(models.Model):
_name = "estate_property"
_description = "APP super mega trop bien"
@@ -16,6 +17,8 @@ class Estate_property(models.Model):
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
- garden_orientation = fields.Selection(string='Orientation',
- selection=[('notrh', 'Notrh'), ('south', 'South'), ('east', 'East'), ('west', 'West')],
- help="The garden orientation")
\ No newline at end of file
+ garden_orientation = fields.Selection(
+ string='Orientation',
+ selection=[('notrh', 'Notrh'), ('south', 'South'), ('east', 'East'), ('west', 'West')],
+ help="The garden orientation"
+ )
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 03f4c262708..561d4ef4824 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
-acces_estate_property,acces_estate_property,model_estate_property,base.group_user,1,1,1,1
\ No newline at end of file
+acces_estate_property,acces_estate_property,model_estate_property,base.group_user,1,1,1,1
diff --git a/estate/view/estate_action.xml b/estate/view/estate_action.xml
index 3e734eb981b..c3dde6fb10a 100644
--- a/estate/view/estate_action.xml
+++ b/estate/view/estate_action.xml
@@ -1,8 +1,8 @@
\ No newline at end of file
+
diff --git a/estate/view/estate_property_views.xml b/estate/view/estate_property_views.xml
index 825ae24d7d4..c2a0fc920ac 100644
--- a/estate/view/estate_property_views.xml
+++ b/estate/view/estate_property_views.xml
@@ -5,4 +5,4 @@
estate_property
list,form
-
\ No newline at end of file
+
From 86dbba3ad41d268633fb04b8a7b0e96fe7ae386a Mon Sep 17 00:00:00 2001
From: vibad
Date: Wed, 22 Apr 2026 11:27:58 +0200
Subject: [PATCH 05/19] [ADD] Chapitre 5
---
estate/models/estate_property.py | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index a7e8421a730..5bb077f54a3 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -8,17 +8,25 @@ class Estate_property(models.Model):
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
- date_availability = fields.Date()
+ date_availability = fields.Date(copy=False, default=fields.Date.add(fields.Date.today(), months=3))
expected_price = fields.Float(required=True)
- selling_price = fields.Float()
- bedrooms = fields.Integer()
+ 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',
+ string='Orientation',
selection=[('notrh', 'Notrh'), ('south', 'South'), ('east', 'East'), ('west', 'West')],
- help="The garden orientation"
+ help="The garden orientation",
+ )
+ active = fields.Boolean(default=True)
+ state = fields.Selection(
+ string="Status",
+ selection=[('new', 'New'), ('offer Received', 'Offer Received'), ('offer Accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')],
+ require=True,
+ copy=False,
+ default="new",
)
From 842b9fb047b55ff5c23cd97998ba993a6e6a9b84 Mon Sep 17 00:00:00 2001
From: vibad
Date: Wed, 22 Apr 2026 15:12:45 +0200
Subject: [PATCH 06/19] [ADD] Chapitre 6
---
estate/security/ir.model.access.csv | 2 +-
estate/view/estate_property_views.xml | 82 +++++++++++++++++++++++++++
2 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 561d4ef4824..4492e38729a 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
-acces_estate_property,acces_estate_property,model_estate_property,base.group_user,1,1,1,1
+acces_estate_property,estate_property,model_estate_property,base.group_user,1,1,1,1
diff --git a/estate/view/estate_property_views.xml b/estate/view/estate_property_views.xml
index c2a0fc920ac..9d8bb8ed8d3 100644
--- a/estate/view/estate_property_views.xml
+++ b/estate/view/estate_property_views.xml
@@ -5,4 +5,86 @@
estate_property
list,form
+
+
+ estate.properties.list
+ estate_property
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ estate.properties.form
+ estate_property
+
+
+
+
+
+
+ estate.properties.search
+ estate_property
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From d3a2e678c9e0d8b6ac063dcc601d9a2c8d4615f6 Mon Sep 17 00:00:00 2001
From: "David Van Droogenbroeck (DROD)"
Date: Thu, 23 Apr 2026 09:51:49 +0200
Subject: [PATCH 07/19] [FIX] estate: remove unlink permission for base users
Base users were able to unlink records although they're just plebs.
---
estate/security/ir.model.access.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 4492e38729a..811072c89ea 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
-acces_estate_property,estate_property,model_estate_property,base.group_user,1,1,1,1
+acces_estate_property,estate_property,model_estate_property,base.group_user,1,1,1,0
From 0eeb0b02be7ed42412cb823622b025d9f3978bcb Mon Sep 17 00:00:00 2001
From: vibad
Date: Thu, 23 Apr 2026 10:46:39 +0200
Subject: [PATCH 08/19] [ADD] Chapitre 7 + lisence
---
estate/__manifest__.py | 4 +++
estate/models/__init__.py | 2 +-
estate/models/estate_property.py | 9 +++++--
estate/models/estate_property_offer.py | 14 ++++++++++
estate/models/estate_property_tag.py | 8 ++++++
estate/models/estate_property_type.py | 8 ++++++
estate/security/ir.model.access.csv | 3 +++
estate/view/estate_action.xml | 4 +++
estate/view/estate_property_offer_views.xml | 30 +++++++++++++++++++++
estate/view/estate_property_tag_views.xml | 8 ++++++
estate/view/estate_property_type_views.xml | 8 ++++++
estate/view/estate_property_views.xml | 14 +++++++---
12 files changed, 106 insertions(+), 6 deletions(-)
create mode 100644 estate/models/estate_property_offer.py
create mode 100644 estate/models/estate_property_tag.py
create mode 100644 estate/models/estate_property_type.py
create mode 100644 estate/view/estate_property_offer_views.xml
create mode 100644 estate/view/estate_property_tag_views.xml
create mode 100644 estate/view/estate_property_type_views.xml
diff --git a/estate/__manifest__.py b/estate/__manifest__.py
index 39e60af8cc9..7489bf1f368 100644
--- a/estate/__manifest__.py
+++ b/estate/__manifest__.py
@@ -6,9 +6,13 @@
'installable': True,
'application': True,
'author': 'vibad',
+ 'license': 'LGPL-3',
'data': [
'security/ir.model.access.csv',
+ 'view/estate_property_offer_views.xml',
'view/estate_property_views.xml',
+ 'view/estate_property_type_views.xml',
+ 'view/estate_property_tag_views.xml',
'view/estate_action.xml',
],
diff --git a/estate/models/__init__.py b/estate/models/__init__.py
index 5e1963c9d2f..93a6bd86abd 100644
--- a/estate/models/__init__.py
+++ b/estate/models/__init__.py
@@ -1 +1 @@
-from . import estate_property
+from . import estate_property, estate_property_type, estate_property_tag, estate_property_offer
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 5bb077f54a3..83c2dfe5477 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -8,7 +8,7 @@ class Estate_property(models.Model):
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
- date_availability = fields.Date(copy=False, default=fields.Date.add(fields.Date.today(), months=3))
+ date_availability = fields.Date(copy=False, default=lambda self: fields.Date.add(fields.Date.today(), months=3))
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
@@ -26,7 +26,12 @@ class Estate_property(models.Model):
state = fields.Selection(
string="Status",
selection=[('new', 'New'), ('offer Received', 'Offer Received'), ('offer Accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')],
- require=True,
+ required=True,
copy=False,
default="new",
)
+ property_type_id = fields.Many2one("estate_property_type", string="Property Type")
+ salesperson_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user)
+ buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
+ 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..331fcdfdbd3
--- /dev/null
+++ b/estate/models/estate_property_offer.py
@@ -0,0 +1,14 @@
+from odoo import models, fields
+
+
+class Estate_property_offer(models.Model):
+ _name = "estate_property_offer"
+ _description = "Offer for estate properties"
+
+ price = fields.Float(required=True)
+ partner_id = fields.Many2one("res.partner", string="Partner", required=True)
+ property_id = fields.Many2one("estate_property", string="Property", required=True)
+ state = fields.Selection([
+ ("accepted", "Accepted"),
+ ("refused", "Refused"),
+ ], string="State", copy=False)
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
new file mode 100644
index 00000000000..4bd4b9ddd54
--- /dev/null
+++ b/estate/models/estate_property_tag.py
@@ -0,0 +1,8 @@
+from odoo import models, fields
+
+
+class Estate_property_tag(models.Model):
+ _name = "estate_property_tag"
+ _description = "tag super mega trop bien"
+
+ name = fields.Char(required=True)
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
new file mode 100644
index 00000000000..2f93b1bca32
--- /dev/null
+++ b/estate/models/estate_property_type.py
@@ -0,0 +1,8 @@
+from odoo import models, fields
+
+
+class Estate_property_type(models.Model):
+ _name = "estate_property_type"
+ _description = "APP super mega trop bien"
+
+ name = fields.Char(required=True)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 4492e38729a..67e0e3f720c 100644
--- a/estate/security/ir.model.access.csv
+++ b/estate/security/ir.model.access.csv
@@ -1,2 +1,5 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
acces_estate_property,estate_property,model_estate_property,base.group_user,1,1,1,1
+acces_estate_property_type,estate_property_type,model_estate_property_type,base.group_user,1,1,1,1
+acces_estate_property_tag,estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1
+acces_estate_property_offer,estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1
diff --git a/estate/view/estate_action.xml b/estate/view/estate_action.xml
index c3dde6fb10a..cea89a5c703 100644
--- a/estate/view/estate_action.xml
+++ b/estate/view/estate_action.xml
@@ -4,5 +4,9 @@
+
diff --git a/estate/view/estate_property_offer_views.xml b/estate/view/estate_property_offer_views.xml
new file mode 100644
index 00000000000..b163f73ac45
--- /dev/null
+++ b/estate/view/estate_property_offer_views.xml
@@ -0,0 +1,30 @@
+
+
+
+ estate.property.offer.form
+ estate_property_offer
+
+
+
+
+
+
+ estate.property.offer.list
+ estate_property_offer
+
+
+
+
+
+
+
+
+
diff --git a/estate/view/estate_property_tag_views.xml b/estate/view/estate_property_tag_views.xml
new file mode 100644
index 00000000000..b5a6dbe7eeb
--- /dev/null
+++ b/estate/view/estate_property_tag_views.xml
@@ -0,0 +1,8 @@
+
+
+
+ Property Tags
+ estate_property_tag
+ list,form
+
+
diff --git a/estate/view/estate_property_type_views.xml b/estate/view/estate_property_type_views.xml
new file mode 100644
index 00000000000..6411fd2663f
--- /dev/null
+++ b/estate/view/estate_property_type_views.xml
@@ -0,0 +1,8 @@
+
+
+
+ Property Types
+ estate_property_type
+ list,form
+
+
diff --git a/estate/view/estate_property_views.xml b/estate/view/estate_property_views.xml
index 9d8bb8ed8d3..09464408da9 100644
--- a/estate/view/estate_property_views.xml
+++ b/estate/view/estate_property_views.xml
@@ -18,6 +18,7 @@
+
@@ -31,6 +32,7 @@
+
@@ -52,13 +54,20 @@
+
+
+
+
+
+
+
@@ -79,11 +88,10 @@
+
-
-
-
+
From 45320c82ec8b1e3641ecbbb6b23e752356f883a3 Mon Sep 17 00:00:00 2001
From: vibad
Date: Thu, 23 Apr 2026 10:49:42 +0200
Subject: [PATCH 09/19] [FIX] estate : remove all unlink permission for base
users
---
estate/security/ir.model.access.csv | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 67e0e3f720c..011ca4aa1a3 100644
--- a/estate/security/ir.model.access.csv
+++ b/estate/security/ir.model.access.csv
@@ -1,5 +1,5 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
-acces_estate_property,estate_property,model_estate_property,base.group_user,1,1,1,1
-acces_estate_property_type,estate_property_type,model_estate_property_type,base.group_user,1,1,1,1
-acces_estate_property_tag,estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1
-acces_estate_property_offer,estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1
+acces_estate_property,estate_property,model_estate_property,base.group_user,1,1,1,0
+acces_estate_property_type,estate_property_type,model_estate_property_type,base.group_user,1,1,1,0
+acces_estate_property_tag,estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,0
+acces_estate_property_offer,estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,0
From 7ab151598a8c2f1b3fcc86daf051d962dbdb07dc Mon Sep 17 00:00:00 2001
From: vibad
Date: Thu, 23 Apr 2026 11:55:38 +0200
Subject: [PATCH 10/19] [CLN] remove useless blanc
---
estate/models/estate_property.py | 28 +++++++++++++++++++--
estate/models/estate_property_offer.py | 21 +++++++++++++++-
estate/view/estate_property_offer_views.xml | 2 ++
estate/view/estate_property_views.xml | 7 +++---
4 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 83c2dfe5477..3a87236ae8e 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 Estate_property(models.Model):
@@ -19,7 +19,7 @@ class Estate_property(models.Model):
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string='Orientation',
- selection=[('notrh', 'Notrh'), ('south', 'South'), ('east', 'East'), ('west', 'West')],
+ selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')],
help="The garden orientation",
)
active = fields.Boolean(default=True)
@@ -35,3 +35,27 @@ class Estate_property(models.Model):
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
tag_ids = fields.Many2many("estate_property_tag", string="Tags")
offer_ids = fields.One2many("estate_property_offer", "property_id", string="Offers")
+ total_area = fields.Integer(compute="_compute_area")
+ best_price = fields.Float(compute="_compute_best_price")
+
+ @api.depends("living_area", "garden_area")
+ def _compute_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
+
+ @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
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 331fcdfdbd3..8be78443e17 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 Estate_property_offer(models.Model):
@@ -12,3 +12,22 @@ class Estate_property_offer(models.Model):
("accepted", "Accepted"),
("refused", "Refused"),
], string="State", copy=False)
+ validaty = fields.Integer(string="Offer Validity (days)", default=7)
+ date_deadline = fields.Date(string="Offer Deadline", compute="_compute_date_deadline", inverse="_inverse_date_deadline")
+
+ @api.depends("validaty", "create_date")
+ def _compute_date_deadline(self):
+ for record in self:
+ if record.validaty and record.create_date:
+
+ record.date_deadline = fields.Date.add(record.create_date, days=record.validaty)
+ else:
+ record.date_deadline = False
+
+ def _inverse_date_deadline(self):
+ for record in self:
+ if record.date_deadline and record.create_date:
+ create_date = fields.Date.to_date(record.create_date)
+ record.validaty = (record.date_deadline - create_date).days
+ else:
+ record.validaty = 0
diff --git a/estate/view/estate_property_offer_views.xml b/estate/view/estate_property_offer_views.xml
index b163f73ac45..1ea5417aa2c 100644
--- a/estate/view/estate_property_offer_views.xml
+++ b/estate/view/estate_property_offer_views.xml
@@ -10,6 +10,8 @@
+
+
diff --git a/estate/view/estate_property_views.xml b/estate/view/estate_property_views.xml
index 09464408da9..f7a0005ea59 100644
--- a/estate/view/estate_property_views.xml
+++ b/estate/view/estate_property_views.xml
@@ -37,10 +37,12 @@
+
-
+
+
@@ -56,6 +58,7 @@
+
@@ -66,8 +69,6 @@
-
-
From c4f10323ad5b6f50b1e00f758b9e6618280d8be2 Mon Sep 17 00:00:00 2001
From: vibad
Date: Thu, 23 Apr 2026 16:15:12 +0200
Subject: [PATCH 11/19] [ADD] Chapitre 10
---
estate/models/estate_property.py | 44 +++++++++++++++++++++
estate/models/estate_property_offer.py | 27 ++++++++++++-
estate/models/estate_property_tag.py | 5 +++
estate/models/estate_property_type.py | 5 +++
estate/security/ir.model.access.csv | 2 +-
estate/view/estate_property_offer_views.xml | 2 +
estate/view/estate_property_views.xml | 7 +++-
7 files changed, 89 insertions(+), 3 deletions(-)
diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py
index 3a87236ae8e..1ffd2110916 100644
--- a/estate/models/estate_property.py
+++ b/estate/models/estate_property.py
@@ -1,4 +1,5 @@
from odoo import models, fields, api
+from odoo.exceptions import UserError, ValidationError
class Estate_property(models.Model):
@@ -29,6 +30,8 @@ class Estate_property(models.Model):
required=True,
copy=False,
default="new",
+ compute="_compute_state",
+ store=True,
)
property_type_id = fields.Many2one("estate_property_type", string="Property Type")
salesperson_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user)
@@ -38,6 +41,16 @@ class Estate_property(models.Model):
total_area = fields.Integer(compute="_compute_area")
best_price = fields.Float(compute="_compute_best_price")
+ _check_expected_price = models.Constraint(
+ "CHECK(expected_price > 0)",
+ message="The expected price must be strictly positive",
+ )
+
+ _check_selling_price = models.Constraint(
+ "CHECK(selling_price >= 0)",
+ message="The selling price cannot be negative",
+ )
+
@api.depends("living_area", "garden_area")
def _compute_area(self):
for record in self:
@@ -59,3 +72,34 @@ def _onchange_garden(self):
else:
self.garden_area = 0
self.garden_orientation = False
+
+ @api.depends("offer_ids")
+ def _compute_state(self):
+ if self.offer_ids:
+ self.state = "offer Received"
+ for offer in self.offer_ids:
+ if offer.state == "accepted":
+ self.state = "offer Accepted"
+ break
+ else:
+ self.state = "new"
+
+ def action_sold(self):
+ for record in self:
+ if record.state != "cancelled" and record.state != "sold":
+ record.state = "sold"
+ else:
+ raise UserError("A property that is cancelled or already sold cannot be sold.")
+
+ def action_cancel(self):
+ for record in self:
+ if record.state != "sold" and record.state != "cancelled":
+ record.state = "cancelled"
+ else:
+ raise UserError("A property that is sold or already cancelled cannot be cancelled.")
+
+ @api.constrains("selling_price", "expected_price")
+ def _check_enough_selling_price(self):
+ for record in self:
+ if record.selling_price and record.selling_price < record.expected_price * 0.9:
+ raise ValidationError("The selling price cannot be less than 90% of the expected price.")
diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py
index 8be78443e17..ada5cae6eff 100644
--- a/estate/models/estate_property_offer.py
+++ b/estate/models/estate_property_offer.py
@@ -1,5 +1,5 @@
from odoo import models, fields, api
-
+from odoo.exceptions import UserError
class Estate_property_offer(models.Model):
_name = "estate_property_offer"
@@ -15,6 +15,11 @@ class Estate_property_offer(models.Model):
validaty = fields.Integer(string="Offer Validity (days)", default=7)
date_deadline = fields.Date(string="Offer Deadline", compute="_compute_date_deadline", inverse="_inverse_date_deadline")
+ _check_price = models.Constraint(
+ "CHECK(price > 0)",
+ message="The price must be strictly positive",
+ )
+
@api.depends("validaty", "create_date")
def _compute_date_deadline(self):
for record in self:
@@ -31,3 +36,23 @@ def _inverse_date_deadline(self):
record.validaty = (record.date_deadline - create_date).days
else:
record.validaty = 0
+
+ def accept_offer(self):
+ for record in self:
+ if record.state == "accepted" or record.state == "refused":
+ raise UserError("This offer has already been accepted or refused.")
+ for offer in record.property_id.offer_ids:
+ if offer.state == "accepted":
+ raise UserError("Another offer has already been accepted for this property.")
+ record.state = "accepted"
+ record.property_id.state = "offer Accepted"
+ record.property_id.selling_price = record.price
+ record.property_id.buyer_id = record.partner_id
+ return True
+
+ def refuse_offer(self):
+ for record in self:
+ if record.state == "accepted" or record.state == "refused":
+ raise UserError("This offer has already been accepted or refused.")
+ record.state = "refused"
+ return True
diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py
index 4bd4b9ddd54..1767c0b04fb 100644
--- a/estate/models/estate_property_tag.py
+++ b/estate/models/estate_property_tag.py
@@ -6,3 +6,8 @@ class Estate_property_tag(models.Model):
_description = "tag super mega trop bien"
name = fields.Char(required=True)
+
+ _check_name = models.Constraint(
+ "UNIQUE(name)",
+ message="The name of the tag must be unique",
+ )
diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py
index 2f93b1bca32..3b35b1eb5ef 100644
--- a/estate/models/estate_property_type.py
+++ b/estate/models/estate_property_type.py
@@ -6,3 +6,8 @@ class Estate_property_type(models.Model):
_description = "APP super mega trop bien"
name = fields.Char(required=True)
+
+ _check_name = models.Constraint(
+ "UNIQUE(name)",
+ message="The name of the property type must be unique",
+ )
diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv
index 011ca4aa1a3..c6876dae518 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
acces_estate_property,estate_property,model_estate_property,base.group_user,1,1,1,0
acces_estate_property_type,estate_property_type,model_estate_property_type,base.group_user,1,1,1,0
acces_estate_property_tag,estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,0
-acces_estate_property_offer,estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,0
+acces_estate_property_offer,estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1
diff --git a/estate/view/estate_property_offer_views.xml b/estate/view/estate_property_offer_views.xml
index 1ea5417aa2c..d70ec2aca77 100644
--- a/estate/view/estate_property_offer_views.xml
+++ b/estate/view/estate_property_offer_views.xml
@@ -25,6 +25,8 @@
+
+
diff --git a/estate/view/estate_property_views.xml b/estate/view/estate_property_views.xml
index f7a0005ea59..19470d5cb89 100644
--- a/estate/view/estate_property_views.xml
+++ b/estate/view/estate_property_views.xml
@@ -28,6 +28,10 @@
estate_property
+
+ estate.properties.kanban
+ estate.property
+
+
+
+
+
+
+
+
+
+
+
+ Expected price:
+
+
+ Best Offer:
+
+
+
+
+
+
+