From 72869bbfaa2f531f97c92e2ad2f6fdf6d3904180 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Thu, 20 Aug 2026 11:49:07 +0200 Subject: [PATCH 01/14] [IMP] git: git challenge completed! --- git-challenge.txt | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 git-challenge.txt diff --git a/git-challenge.txt b/git-challenge.txt new file mode 100644 index 00000000000..799c7e11c12 --- /dev/null +++ b/git-challenge.txt @@ -0,0 +1,29 @@ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣴⣶⡾⠿⠛⠻⠶⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡞⠉⢠⡿⠁⠀⠀⣰⡾⠓⠂⠉⢻⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡟⠀⣰⡿⠀⠀⠀⣼⠏⠀⠀⠀⢀⣠⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣧⣶⣿⣇⠀⢀⣼⡏⠀⠀⠀⣰⣿⣿⢻⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣶⣶⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣶⣾⣿⠁⠀⠀⠀⣠⣾⠏⢸⣿⣿⣿⣿⣿⣶⣶⣶⣶⣶⣶⠶⠚⠛⠛⠛⠻⠾⠿⢿⣇⡀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣿⣿⣿⡿⢷⣤⣤⣾⡿⠋⠀⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠳⢦⡀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⡀⠉⠉⠉⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢷⣄⣀⣀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣷⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⣰⣶⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⠇ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣫⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⢻⣿⣟⢹⣆⠀⠀⠀⠀⠀⠀⠀⠀⠘⣧⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣽⣿⣿⣿⣿⠿⠟⣋⣥⣾⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀⠙⠿⠮⢭⣆⠀⠀⠀⠀⠀⠀⠀⠀⢹⡀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⠟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⡇⠀⢀⣄⠀⠀⠀⠀⣠⣄⡀⠀⠀⠀⠘⡇ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⡟⠁⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀⣧⠀⠸⣿⣇⠀⠀⠀⢱⡛⠉⣿⣦⠀⢠⡇ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⠋⠀⠀⠀⠘⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠀⠀⠀⠀⠸⣷⡀⠻⣿⣷⣦⠄⠸⣿⣿⣿⡿⢀⡾⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠟⠁⠀⠀⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⡀⠀⠀⠀⠈⠛⠷⠦⠭⠥⠀⠀⠈⠉⢁⣰⠟⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⡾⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⣀⣤⠶⠋⠁⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠛⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣶⠶⠶⠛⠉⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⢠⣾⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠛⠿⣿⣿⠿⣿⣿⣿⣿⡿⣫⣾⣷⣶⣶⡖⠛⠓⡆⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⢠⣋⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⢷⣯⡍⠉⣰⣿⡿⠿⣿⠟⢳⡾⠁⢳⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⢠⡾⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣼⣿⣿⠀⠀⣹⠀⠀⢷⡀⣸⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⡰⢻⣴⢿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣤⣴⣿⣤⣤⣾⠿⠋⠀⠀⠀⠀⠀⠀ +⠀⠀⢠⠞⠁⢸⣇⣾⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡿⠿⠿⠟⠛⠋⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⣴⡏⠀⠀⠀⢿⠃⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣀⣀⣀⣤⡾⠃⠀⠀⠀⠀⠀⣀⣤⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀ +⣸⣿⣧⠀⠀⠀⠘⢷⡏⣿⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣶⣿⣿⡉⠥⠿⠛⠒⠚⣻⣄⣀⣀⠀⣠⣤⣾⣏⢉⡏⠀⠈⣿⣦⡀⠀⠀⠀⠀⠀ +⣿⣿⣿⣆⠀⠀⠀⠈⠻⢿⡍⢻⡦⣤⣀⣀⠀⠀⠀⠀⣀⣀⣠⣤⣤⠶⣾⣯⣡⠔⠋⠉⠀⢀⣠⣴⣶⣿⠟⠉⠀⣨⡿⢋⣽⣿⡿⠟⠛⠛⠶⣾⠁⠀⢱⠀⠀⠀⠀⠀ +⢻⣿⣿⣿⣦⠀⠀⠀⠀⠀⠉⠚⠷⢤⣿⣿⣉⣹⣿⣏⠙⣿⣅⣽⡷⠴⠛⠉⠀⠀⠀⢠⣾⣿⣿⣿⣿⠏⠀⠀⣼⡟⠀⣾⣿⠋⠀⠀⠀⠀⠀⠈⢳⡖⢻⠀⠀⠀⠀⠀ +⠈⢻⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⠀⠀⢠⣿⡇⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀⢸⣷⡞⠀⠀⠀⠀⠀ +⠀⠀⢹⣿⣿⣿⣿⣶⣄⡀⢀⡴⣶⣶⣶⣶⣤⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⠀⠀⢸⣿⣇⠀⢿⣧⡀⠀⠀⠀⠀⠀⢀⣾⡟⠁⠀⠀⠀⠀⠀ +⠀⠀⠀⠻⣿⣿⣿⣿⣿⠿⠋⠀⠀⠀⠀⠀⠉⠉⠙⠛⠛⠿⠷⢶⣦⣤⣀⡀⠀⠀⠀⢻⣿⣿⣿⣿⣿⣆⠀⠈⢿⣿⣦⡀⠻⢿⣦⣤⣤⣤⣶⡿⠋⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⠻⠿⠿⢿⣿⣿⣿⣿⣿⠦⠤⠤⢙⣻⣿⡦⠤⠽⠛⠛⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀ From f1c66d24d650c15970d35c6c66ee7c44de3e6f27 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Mon, 17 Aug 2026 14:13:41 +0200 Subject: [PATCH 02/14] [ADD] estate: init --- estate/__init__.py | 0 estate/__manifest__.py | 11 +++++++++++ 2 files changed, 11 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..de284a8292c --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + + +{ + "name": "estate", + "application": True, + "depends": [ + "base", + ], +} From 2f0f8cce12505c2083b0d1671621d96f2f7fb138 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Mon, 17 Aug 2026 14:56:28 +0200 Subject: [PATCH 03/14] [IMP] estate: chapter 3 --- estate/__init__.py | 4 ++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 22 ++++++++++++++++++++++ 3 files changed, 27 insertions(+) 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..dc5e6b693d1 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import models 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..066e93676a7 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,22 @@ +from odoo import fields, models + + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "An estate property" + + 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.Boolean() + garden_orientation = fields.Selection( + [("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")] + ) From c9fd1508bc66ba2d08edbced8c5750303a764572 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Mon, 17 Aug 2026 15:39:59 +0200 Subject: [PATCH 04/14] [IMP] estate: chapter 4 --- 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 de284a8292c..16615ed8997 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -8,4 +8,7 @@ "depends": [ "base", ], + "data": [ + "security/ir.model.access.csv", + ], } diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..98f4671fb0d --- /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 +estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 From fb75f1ca9d67b610af1b6aeb22253a66e5493f46 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Mon, 17 Aug 2026 16:49:52 +0200 Subject: [PATCH 05/14] [IMP] estate: chapter 5 --- estate/__manifest__.py | 2 ++ estate/models/estate_property.py | 22 +++++++++++++++++++--- estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 8 ++++++++ 4 files changed, 37 insertions(+), 3 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 16615ed8997..7fd887aab61 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,5 +10,7 @@ ], "data": [ "security/ir.model.access.csv", + "views/estate_property_views.xml", + "views/estate_menus.xml", ], } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 066e93676a7..dec86c0f4ea 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import fields, models +from odoo.tools.date_utils import add class EstateProperty(models.Model): @@ -8,10 +9,12 @@ class EstateProperty(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date( + copy=False, default=lambda _: add(fields.Date.today(), months=+3) + ) expected_price = fields.Float(required=True) - selling_price = fields.Float() - bedrooms = fields.Integer() + selling_price = fields.Float(copy=False, readonly=True) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() @@ -20,3 +23,16 @@ class EstateProperty(models.Model): garden_orientation = fields.Selection( [("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")] ) + active = fields.Boolean(default=True) + state = fields.Selection( + [ + ("new", "New"), + ("received", "Offer Received"), + ("accepted", "Offer Accepted"), + ("sold", "Sold"), + ("cancelled", "Cancelled"), + ], + required=True, + copy=False, + default="new", + ) diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..c6cd401b4a8 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..420bdc65197 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Properties + estate.property + list,form + + From 03075f51238414ab44eb556c49c3284a7c02ea83 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Tue, 18 Aug 2026 10:14:26 +0200 Subject: [PATCH 06/14] [IMP] estate: chapter 6 --- estate/__manifest__.py | 2 ++ estate/models/estate_property.py | 2 +- estate/views/estate_form.xml | 38 ++++++++++++++++++++++++++++++++ estate/views/estate_list.xml | 34 ++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 estate/views/estate_form.xml create mode 100644 estate/views/estate_list.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 7fd887aab61..312c426c783 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -11,6 +11,8 @@ "data": [ "security/ir.model.access.csv", "views/estate_property_views.xml", + "views/estate_list.xml", + "views/estate_form.xml", "views/estate_menus.xml", ], } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index dec86c0f4ea..3301229596a 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -19,7 +19,7 @@ class EstateProperty(models.Model): facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() - garden_area = fields.Boolean() + garden_area = fields.Integer() garden_orientation = fields.Selection( [("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")] ) diff --git a/estate/views/estate_form.xml b/estate/views/estate_form.xml new file mode 100644 index 00000000000..5bfca1003a0 --- /dev/null +++ b/estate/views/estate_form.xml @@ -0,0 +1,38 @@ + + + + Properties + estate.property + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
diff --git a/estate/views/estate_list.xml b/estate/views/estate_list.xml new file mode 100644 index 00000000000..aee0471fa54 --- /dev/null +++ b/estate/views/estate_list.xml @@ -0,0 +1,34 @@ + + + + Search + estate.property + + + + + + + + + + + + + + + Properties + estate.property + + + + + + + + + + + + + From 5cd1ce7a2f1796d33081a1d21ba350bee42facf2 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Tue, 18 Aug 2026 11:55:02 +0200 Subject: [PATCH 07/14] [IMP] estate: chapter 7 --- estate/__manifest__.py | 5 +- estate/models/__init__.py | 3 + estate/models/estate_offer.py | 15 +++++ estate/models/estate_property.py | 16 +++++ estate/models/estate_tag.py | 8 +++ estate/models/estate_type.py | 8 +++ estate/security/ir.model.access.csv | 3 + estate/views/estate_form.xml | 38 ------------ estate/views/estate_list.xml | 34 ----------- estate/views/estate_menus.xml | 8 ++- estate/views/estate_offer_views.xml | 31 ++++++++++ estate/views/estate_property_views.xml | 84 +++++++++++++++++++++++++- estate/views/estate_tag_views.xml | 8 +++ estate/views/estate_type_views.xml | 8 +++ 14 files changed, 192 insertions(+), 77 deletions(-) create mode 100644 estate/models/estate_offer.py create mode 100644 estate/models/estate_tag.py create mode 100644 estate/models/estate_type.py delete mode 100644 estate/views/estate_form.xml delete mode 100644 estate/views/estate_list.xml create mode 100644 estate/views/estate_offer_views.xml create mode 100644 estate/views/estate_tag_views.xml create mode 100644 estate/views/estate_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 312c426c783..73e0e310abe 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -11,8 +11,9 @@ "data": [ "security/ir.model.access.csv", "views/estate_property_views.xml", - "views/estate_list.xml", - "views/estate_form.xml", + "views/estate_type_views.xml", + "views/estate_tag_views.xml", + "views/estate_offer_views.xml", "views/estate_menus.xml", ], } diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..8d8063038f5 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,4 @@ from . import estate_property +from . import estate_type +from . import estate_tag +from . import estate_offer diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py new file mode 100644 index 00000000000..9e1194a45a9 --- /dev/null +++ b/estate/models/estate_offer.py @@ -0,0 +1,15 @@ +from odoo import fields, models + + +class EstateOffer(models.Model): + _name = "estate.offer" + _description = "An estate offer" + + price = fields.Float() + status = fields.Selection( + [("accepted", "Accepted"), ("refused", "Refused")], copy=False + ) + + # Foreign fields + partner_id = fields.Many2one("res.partner", required=True) + property_id = fields.Many2one("estate.property", required=True) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 3301229596a..2c27050a437 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -36,3 +36,19 @@ class EstateProperty(models.Model): copy=False, default="new", ) + + # Foreign fields + property_type_id = fields.Many2one( + "estate.type", required=True, ondelete="restrict" + ) + buyer_id = fields.Many2one( + "res.partner", required=False, copy=False, ondelete="restrict" + ) + seller_id = fields.Many2one( + "res.users", + required=True, + default=lambda self: self.env.user, + ondelete="restrict", + ) + tag_ids = fields.Many2many("estate.tag") + offer_ids = fields.One2many("estate.offer", "property_id") diff --git a/estate/models/estate_tag.py b/estate/models/estate_tag.py new file mode 100644 index 00000000000..5ce1c31faf6 --- /dev/null +++ b/estate/models/estate_tag.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstateTag(models.Model): + _name = "estate.tag" + _description = "An estate tag" + + name = fields.Char(required=True) diff --git a/estate/models/estate_type.py b/estate/models/estate_type.py new file mode 100644 index 00000000000..848488dfb59 --- /dev/null +++ b/estate/models/estate_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstateType(models.Model): + _name = "estate.type" + _description = "An estate type" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 98f4671fb0d..7aa3583474b 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 estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 +estate.access_estate_type,access_estate_type,estate.model_estate_type,base.group_user,1,1,1,1 +estate.access_estate_tag,access_estate_tag,estate.model_estate_tag,base.group_user,1,1,1,1 +estate.access_estate_offer,access_estate_offer,estate.model_estate_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_form.xml b/estate/views/estate_form.xml deleted file mode 100644 index 5bfca1003a0..00000000000 --- a/estate/views/estate_form.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - Properties - estate.property - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
diff --git a/estate/views/estate_list.xml b/estate/views/estate_list.xml deleted file mode 100644 index aee0471fa54..00000000000 --- a/estate/views/estate_list.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - Search - estate.property - - - - - - - - - - - - - - - Properties - estate.property - - - - - - - - - - - - - diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index c6cd401b4a8..b3f9748ea5b 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,8 +1,12 @@ - - + + + + + + diff --git a/estate/views/estate_offer_views.xml b/estate/views/estate_offer_views.xml new file mode 100644 index 00000000000..4e985114e9b --- /dev/null +++ b/estate/views/estate_offer_views.xml @@ -0,0 +1,31 @@ + + + + Offers + estate.offer + + + + + + + + + + + Offers + estate.offer + +
+ + + + + + + +
+
+
+ +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 420bdc65197..46a92a276c9 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,8 +1,90 @@ - + Properties estate.property list,form + + + Search + estate.property + + + + + + + + + + + + + + + Properties + estate.property + + + + + + + + + + + + + + + Properties + estate.property + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
diff --git a/estate/views/estate_tag_views.xml b/estate/views/estate_tag_views.xml new file mode 100644 index 00000000000..c2f9bb2d1ea --- /dev/null +++ b/estate/views/estate_tag_views.xml @@ -0,0 +1,8 @@ + + + + Tags + estate.tag + list,form + + diff --git a/estate/views/estate_type_views.xml b/estate/views/estate_type_views.xml new file mode 100644 index 00000000000..1bb09714a7c --- /dev/null +++ b/estate/views/estate_type_views.xml @@ -0,0 +1,8 @@ + + + + Types + estate.type + list,form + + From 1e20bf5d0a1b04bde5e556152a9fb475b9d4e03d Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Tue, 18 Aug 2026 14:09:32 +0200 Subject: [PATCH 08/14] [IMP] estate: chapter 8 --- estate/models/estate_offer.py | 21 ++++++++++++++++++++- estate/models/estate_property.py | 25 +++++++++++++++++++++++-- estate/views/estate_offer_views.xml | 4 ++++ estate/views/estate_property_views.xml | 2 ++ 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 9e1194a45a9..0c0ef456925 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -1,4 +1,5 @@ -from odoo import fields, models +from odoo import api, fields, models +from odoo.tools.date_utils import add class EstateOffer(models.Model): @@ -9,7 +10,25 @@ class EstateOffer(models.Model): status = fields.Selection( [("accepted", "Accepted"), ("refused", "Refused")], copy=False ) + validity = fields.Integer(default=7) # Foreign fields partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True) + + # Computed fields + date_deadline = fields.Date( + compute="_compute_date_deadline", inverse="_inverse_date_deadline" + ) + + @api.depends("validity") + def _compute_date_deadline(self): + for record in self: + record.date_deadline = add( + record.create_date if record.create_date else fields.Date.today(), + days=+record.validity, + ) + + def _inverse_date_deadline(self): + for record in self: + record.validity = (record.date_deadline - fields.Date.today()).days diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 2c27050a437..2de6ccda610 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import fields, models +from odoo.orm.models import api from odoo.tools.date_utils import add @@ -12,8 +13,8 @@ class EstateProperty(models.Model): date_availability = fields.Date( copy=False, default=lambda _: add(fields.Date.today(), months=+3) ) - expected_price = fields.Float(required=True) - selling_price = fields.Float(copy=False, readonly=True) + expected_price = fields.Integer(required=True) + selling_price = fields.Integer(copy=False, readonly=True) bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() @@ -52,3 +53,23 @@ class EstateProperty(models.Model): ) tag_ids = fields.Many2many("estate.tag") offer_ids = fields.One2many("estate.offer", "property_id") + + # Computed fields + total_area = fields.Integer(compute="_compute_total_area") + best_price = fields.Integer(compute="_compute_best_price") + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + @api.depends("offer_ids.price") + def _compute_best_price(self): + for record in self: + # Fallback to 0 if no records are present + record.best_price = max(record.offer_ids.mapped("price") + [0]) + + @api.onchange("garden") + def _onchange_garden(self): + self.garden_area = 10 if self.garden else None + self.garden_orientation = "north" if self.garden else None diff --git a/estate/views/estate_offer_views.xml b/estate/views/estate_offer_views.xml index 4e985114e9b..8437fb41646 100644 --- a/estate/views/estate_offer_views.xml +++ b/estate/views/estate_offer_views.xml @@ -7,6 +7,8 @@ + + @@ -21,6 +23,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 46a92a276c9..97daa71650f 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -56,6 +56,7 @@ + @@ -70,6 +71,7 @@ + From 6ebc63ffaac2e90d95860b7091d2316da7510495 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Tue, 18 Aug 2026 15:08:04 +0200 Subject: [PATCH 09/14] [IMP] estate: chapter 9 --- estate/models/estate_offer.py | 19 ++++++++++ estate/models/estate_property.py | 49 ++++++++++++++++++++++++-- estate/views/estate_offer_views.xml | 2 ++ estate/views/estate_property_views.xml | 7 +++- 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 0c0ef456925..fac8af09e3e 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -1,4 +1,5 @@ from odoo import api, fields, models +from odoo.exceptions import UserError from odoo.tools.date_utils import add @@ -32,3 +33,21 @@ def _compute_date_deadline(self): def _inverse_date_deadline(self): for record in self: record.validity = (record.date_deadline - fields.Date.today()).days + + # Actions + + def set_status_accepted(self): + for record in self: + for offer in self.property_id.offer_ids: + if offer.status == "accepted": + raise UserError("Only one offer can be accepted") + + record.status = "accepted" + + return True + + def set_status_refused(self): + for record in self: + record.status = "refused" + + return True diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 2de6ccda610..5d33d3712a9 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import fields, models +from odoo.exceptions import UserError from odoo.orm.models import api from odoo.tools.date_utils import add @@ -14,7 +15,9 @@ class EstateProperty(models.Model): copy=False, default=lambda _: add(fields.Date.today(), months=+3) ) expected_price = fields.Integer(required=True) - selling_price = fields.Integer(copy=False, readonly=True) + selling_price = fields.Integer( + copy=False, readonly=True, compute="_compute_selling_price" + ) bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() @@ -43,7 +46,11 @@ class EstateProperty(models.Model): "estate.type", required=True, ondelete="restrict" ) buyer_id = fields.Many2one( - "res.partner", required=False, copy=False, ondelete="restrict" + "res.partner", + required=False, + copy=False, + ondelete="restrict", + compute="_compute_buyer_id", ) seller_id = fields.Many2one( "res.users", @@ -58,6 +65,24 @@ class EstateProperty(models.Model): total_area = fields.Integer(compute="_compute_total_area") best_price = fields.Integer(compute="_compute_best_price") + @api.depends("offer_ids") + def _compute_selling_price(self): + for record in self: + record.selling_price = None + for offer in record.offer_ids: + if offer.status == "accepted": + record.selling_price = offer.price + break + + @api.depends("offer_ids") + def _compute_buyer_id(self): + for record in self: + record.buyer_id = None + for offer in record.offer_ids: + if offer.status == "accepted": + record.buyer_id = offer.partner_id + break + @api.depends("living_area", "garden_area") def _compute_total_area(self): for record in self: @@ -73,3 +98,23 @@ def _compute_best_price(self): def _onchange_garden(self): self.garden_area = 10 if self.garden else None self.garden_orientation = "north" if self.garden else None + + # Actions + + def set_state_cancel(self): + for record in self: + if record.state == "sold": + raise UserError("A sold property cannot be cancelled") + + record.state = "cancelled" + + return True + + def set_state_sold(self): + for record in self: + if record.state == "cancelled": + raise UserError("A cancelled property cannot be sold") + + record.state = "sold" + + return True diff --git a/estate/views/estate_offer_views.xml b/estate/views/estate_offer_views.xml index 8437fb41646..a51c568b5dc 100644 --- a/estate/views/estate_offer_views.xml +++ b/estate/views/estate_offer_views.xml @@ -9,6 +9,8 @@ + + + + + + + + + + + + + + + + + + + From 0c4927a28f050435d4872253e7f7d661dca0a74b Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Wed, 19 Aug 2026 14:14:20 +0200 Subject: [PATCH 12/14] [IMP] estate: chapter 12 --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_offer.py | 11 +++++++++++ estate/models/estate_property.py | 12 +++++++++++- estate/models/res_user.py | 24 ++++++++++++++++++++++++ estate/views/estate_property_views.xml | 2 +- estate/views/res_user.xml | 17 +++++++++++++++++ 7 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 estate/models/res_user.py create mode 100644 estate/views/res_user.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 5faa40a5720..63095a970c4 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -15,5 +15,6 @@ "views/estate_type_views.xml", "views/estate_tag_views.xml", "views/estate_menus.xml", + "views/res_user.xml", ], } diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 8d8063038f5..12614b21242 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -2,3 +2,4 @@ from . import estate_type from . import estate_tag from . import estate_offer +from . import res_user diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 2a2fe123ad9..26d0f28b578 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -82,3 +82,14 @@ def set_status_refused(self): record.status = "refused" return True + + # Overwrites + + @api.model + def create(self, vals_list): + for vals in vals_list: + property = self.env["estate.property"].browse(vals["property_id"]) + if property.state == "new": + property.state = "received" + + super().create(vals_list) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 1a7e00cc475..6ed0991742a 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,5 @@ from odoo import fields, models -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError from odoo.orm.models import api from odoo.tools.date_utils import add @@ -130,3 +130,13 @@ def set_state_sold(self): record.state = "sold" return True + + # Overwrites + + @api.ondelete(at_uninstall=False) + def onDelete(self): + for record in self: + if record.state not in ["new", "cancelled"]: + raise ValidationError( + "Only properties in the state new or cancelled can be deleted" + ) diff --git a/estate/models/res_user.py b/estate/models/res_user.py new file mode 100644 index 00000000000..21ec8d9b4b0 --- /dev/null +++ b/estate/models/res_user.py @@ -0,0 +1,24 @@ +from odoo import fields, models + + +class User(models.Model): + _inherit = "res.users" + + # Foreign fields + + property_ids = fields.One2many( + "estate.property", + "seller_id", + ) + + # Computed fields + + available_property_ids = fields.One2many( + "estate.property", compute="_compute_filtered_properties" + ) + + def _compute_filtered_properties(self): + for record in self: + record.available_property_ids = record.property_ids.filtered( + lambda x: x.state not in ("sold", "cancelled") + ) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index a57f9c6e6d0..0768f051b37 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -16,7 +16,7 @@ - + diff --git a/estate/views/res_user.xml b/estate/views/res_user.xml new file mode 100644 index 00000000000..485530c2e58 --- /dev/null +++ b/estate/views/res_user.xml @@ -0,0 +1,17 @@ + + + + + res.users.view.form.inherit.estate.property + res.users + + + + + + + + + + + From 85ae848d9a5c1e797b51e677be8ef7d86839c472 Mon Sep 17 00:00:00 2001 From: Vincent Vallaeys Date: Wed, 19 Aug 2026 15:29:40 +0200 Subject: [PATCH 13/14] [IMP] estate: chapter 13 --- estate/views/estate_offer_views.xml | 4 +-- estate/views/estate_property_views.xml | 2 +- estate_account/__init__.py | 1 + estate_account/__manifest__.py | 14 ++++++++++ estate_account/models/__init__.py | 1 + estate_account/models/estate_property.py | 35 ++++++++++++++++++++++++ 6 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 estate_account/__init__.py create mode 100644 estate_account/__manifest__.py create mode 100644 estate_account/models/__init__.py create mode 100644 estate_account/models/estate_property.py diff --git a/estate/views/estate_offer_views.xml b/estate/views/estate_offer_views.xml index eee1ab5a5eb..4fa3cabbe16 100644 --- a/estate/views/estate_offer_views.xml +++ b/estate/views/estate_offer_views.xml @@ -16,8 +16,8 @@ -