From df8d5248c19862523c713f73b2963fe115e76c36 Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Mon, 20 Oct 2025 13:48:29 +0200 Subject: [PATCH 01/24] [ADD] estate: create the app --- estate/__init__.py | 0 estate/__manifest__.py | 6 ++++++ 2 files changed, 6 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..b16a0f57a88 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,6 @@ +{ + 'name': "Estate", + 'version': '1.0', + 'depends': ['base'], + 'author': "GACI Jugurtha (jugac)", +} \ No newline at end of file From b84e9166ecece6c306a0004e78125cfb74231638 Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Mon, 20 Oct 2025 14:46:11 +0200 Subject: [PATCH 02/24] [ADD] estate: chapter 3 --- estate/__manifest__.py | 1 + estate/models/estate_property.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 estate/models/estate_property.py diff --git a/estate/__manifest__.py b/estate/__manifest__.py index b16a0f57a88..9f91523e2c7 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,4 +3,5 @@ 'version': '1.0', 'depends': ['base'], 'author': "GACI Jugurtha (jugac)", + 'application': True } \ 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..c562d26b07f --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,27 @@ +from odoo import models, fields + +class EstateProperty(models.model): + _name = "estate_property" + _description = "Table that stores the estate properties" + + name = fields.Char(string="Title", required=True) + description = fields.Text(string="Description") + postcode = fields.Char(string="Postcode") + + date_availability = fields.Date(string="Available From") + expected_price = fields.Float(string="Expected price", required=True) + selling_price = fields.Float(string="Selling price") + bedrooms = fields.Integer(string="Bedrooms") + living_area = fields.Integer(string="Living Arez (sqm)") + facades = fields.Integer(string="Facades") + garage = fields.Boolean(string="Garage") + garden = fields.Boolean(string="Garden") + garden_area = fields.Integer() + garden_orientation = fields.Selection( + selection=[ + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West') + ] + ) From c3c8c66ead3efbd2c55d15d5ff9dd037b1b487a7 Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Mon, 20 Oct 2025 15:14:52 +0200 Subject: [PATCH 03/24] [ADD] estate: define access rights (chapter 4) --- estate/__manifest__.py | 5 ++++- estate/data/ir.model.access.csv | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 estate/data/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 9f91523e2c7..c4f4d770cdb 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,5 +3,8 @@ 'version': '1.0', 'depends': ['base'], 'author': "GACI Jugurtha (jugac)", - 'application': True + 'application': True, + 'data': [ + 'data/ir.model.access.csv' + ] } \ No newline at end of file diff --git a/estate/data/ir.model.access.csv b/estate/data/ir.model.access.csv new file mode 100644 index 00000000000..3a3ca3b3040 --- /dev/null +++ b/estate/data/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_user,access.estate.property.user,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file From 223bbb61f55ab5a06379a37c89eeee31c39e6a8f Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Tue, 21 Oct 2025 10:24:36 +0200 Subject: [PATCH 04/24] [ADD] estate: create the actions and views (chapter 5) && [FIX] fix PR reviews --- estate/__init__.py | 1 + estate/__manifest__.py | 6 ++-- estate/data/ir.model.access.csv | 2 -- estate/models/__init__.py | 1 + estate/models/estate_property.py | 46 ++++++++++++++++++-------- estate/security/ir.model.access.csv | 2 ++ estate/views/estate_menus.xml | 8 +++++ estate/views/estate_property_views.xml | 8 +++++ 8 files changed, 56 insertions(+), 18 deletions(-) delete mode 100644 estate/data/ir.model.access.csv create mode 100644 estate/models/__init__.py create mode 100644 estate/security/ir.model.access.csv create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/estate/__init__.py b/estate/__init__.py index e69de29bb2d..9a7e03eded3 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py index c4f4d770cdb..8fc1ca1c53b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,6 +5,8 @@ 'author': "GACI Jugurtha (jugac)", 'application': True, 'data': [ - 'data/ir.model.access.csv' + 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml', ] -} \ No newline at end of file +} diff --git a/estate/data/ir.model.access.csv b/estate/data/ir.model.access.csv deleted file mode 100644 index 3a3ca3b3040..00000000000 --- a/estate/data/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink -access_estate_property_user,access.estate.property.user,model_estate_property,base.group_user,1,1,1,1 \ 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 index c562d26b07f..544577a6d56 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,21 +1,29 @@ from odoo import models, fields -class EstateProperty(models.model): - _name = "estate_property" +class EstateProperty(models.Model): + _name = "estate.property" _description = "Table that stores the estate properties" - name = fields.Char(string="Title", required=True) - description = fields.Text(string="Description") - postcode = fields.Char(string="Postcode") - - date_availability = fields.Date(string="Available From") - expected_price = fields.Float(string="Expected price", required=True) - selling_price = fields.Float(string="Selling price") - bedrooms = fields.Integer(string="Bedrooms") - living_area = fields.Integer(string="Living Arez (sqm)") - facades = fields.Integer(string="Facades") - garage = fields.Boolean(string="Garage") - garden = fields.Boolean(string="Garden") + + name = fields.Char(required=True) + description = fields.Text() + postcode = fields.Char() + + date_availability = fields.Date( + string="Available From", + copy=False, + default=lambda self: fields.Date.add(fields.Date.today(), months=3) + ) + + expected_price = fields.Float(required=True) + selling_price = fields.Float(readonly=False, copy=False) + + bedrooms = fields.Integer(default=2) + living_area = fields.Integer(string="Living Area (sqm)") + facades = fields.Integer() + garage = fields.Boolean() + + garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( selection=[ @@ -25,3 +33,13 @@ class EstateProperty(models.model): ('west', 'West') ] ) + + active = fields.Boolean(default=True) + state = fields.Selection( + selection=[ + ('new', 'New'), + ('offer', 'Offer'), + ('received', 'Received'), + ('accepted', 'Accepted') + ] + ) \ 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..bc409891f2e --- /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_group_user,estate.group.user,model_estate_property,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..53e18090998 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..c3f9003cd20 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,8 @@ + + + + Create new estate + estate.property + list,form + + \ No newline at end of file From c7523f707faf1cd02bc9e68d0182726bbfe14af1 Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Tue, 21 Oct 2025 13:20:24 +0200 Subject: [PATCH 05/24] [ADD] estate: customize the view (chapter 6) --- estate/models/estate_property.py | 1 - estate/views/estate_property_views.xml | 71 ++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 544577a6d56..a0b8ce5eaec 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -38,7 +38,6 @@ class EstateProperty(models.Model): state = fields.Selection( selection=[ ('new', 'New'), - ('offer', 'Offer'), ('received', 'Received'), ('accepted', 'Accepted') ] diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c3f9003cd20..65b97cbbb26 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,4 +5,75 @@ estate.property list,form + + + + estate.property.list + estate.property + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +

+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + \ No newline at end of file From 094f1395efd0c15e7e3a96c2794bda12bd56c526 Mon Sep 17 00:00:00 2001 From: Mathilde Pascal Date: Tue, 21 Oct 2025 14:30:35 +0200 Subject: [PATCH 06/24] [IMP] estate: Add notes. --- estate/models/estate_property.py | 1 + estate/views/estate_property_views.xml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a0b8ce5eaec..eef70ed31ac 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -7,6 +7,7 @@ class EstateProperty(models.Model): name = fields.Char(required=True) description = fields.Text() + notes = fields.Html() postcode = fields.Char() date_availability = fields.Date( diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 65b97cbbb26..c213a00e7ab 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -51,6 +51,9 @@ + + + From a9e95fd0ac0bc534271b5940f6aabadfb96d27f8 Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Wed, 22 Oct 2025 09:13:06 +0200 Subject: [PATCH 07/24] [ADD] estate: create relationship tables : offers, tags, types - (chapter 7) --- estate/__manifest__.py | 3 ++ estate/models/__init__.py | 6 +++- estate/models/estate_offer.py | 19 +++++++++++++ estate/models/estate_property.py | 9 +++++- estate/models/estate_tags.py | 8 ++++++ estate/models/estate_type.py | 8 ++++++ estate/security/ir.model.access.csv | 3 ++ estate/views/estate_menus.xml | 17 ++++++++--- estate/views/estate_property_offer_views.xml | 28 ++++++++++++++++++ estate/views/estate_property_tag_views.xml | 30 ++++++++++++++++++++ estate/views/estate_property_type_views.xml | 30 ++++++++++++++++++++ estate/views/estate_property_views.xml | 30 +++++++++++++++++--- 12 files changed, 181 insertions(+), 10 deletions(-) create mode 100644 estate/models/estate_offer.py create mode 100644 estate/models/estate_tags.py create mode 100644 estate/models/estate_type.py create mode 100644 estate/views/estate_property_offer_views.xml create mode 100644 estate/views/estate_property_tag_views.xml create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 8fc1ca1c53b..507f32b15a6 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,6 +7,9 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_menus.xml', ] } diff --git a/estate/models/__init__.py b/estate/models/__init__.py index f4c8fd6db6d..fb51d9b31fe 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,5 @@ -from . import estate_property \ No newline at end of file +from . import estate_property +from . import estate_type +from . import estate_tags +from . import estate_offer + diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py new file mode 100644 index 00000000000..cc09419e47d --- /dev/null +++ b/estate/models/estate_offer.py @@ -0,0 +1,19 @@ +from odoo import models, fields + +class EstateProperty(models.Model): + _name = "estate.property.offer" + _description = "Estate property offers" + + + price = fields.Float(required=True) + status = fields.Selection( + selection=[ + ('refused', 'Refused'), + ('accepted', 'Accepted') + ], + copy=False + ) + + # relations + 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 eef70ed31ac..d2afe3cb862 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -42,4 +42,11 @@ class EstateProperty(models.Model): ('received', 'Received'), ('accepted', 'Accepted') ] - ) \ No newline at end of file + ) + + # relations + property_type_id = fields.Many2one("estate.property.type") + buyer_id = fields.Many2one("res.partner") + salesman_id = fields.Many2one("res.users") + tag_ids = fields.Many2many("estate.property.tag") + offer_ids = fields.One2many("estate.property.offer", "partner_id") \ No newline at end of file diff --git a/estate/models/estate_tags.py b/estate/models/estate_tags.py new file mode 100644 index 00000000000..c4654c4e9d5 --- /dev/null +++ b/estate/models/estate_tags.py @@ -0,0 +1,8 @@ +from odoo import models, fields + +class EstateProperty(models.Model): + _name = "estate.property.tag" + _description = "Estate property tags" + + + 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..43845803ea3 --- /dev/null +++ b/estate/models/estate_type.py @@ -0,0 +1,8 @@ +from odoo import models, fields + +class EstateProperty(models.Model): + _name = "estate.property.type" + _description = "Estate property types" + + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index bc409891f2e..9eb29855a32 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 access_estate_group_user,estate.group.user,model_estate_property,base.group_user,1,1,1,1 +access_estate_type_group_user,estate.type.group.user,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_tag_group_user,estate.tag.group.user,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_offer_group_user,estate.offer.group.user,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 53e18090998..d44f4f07710 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,8 +1,17 @@ - - - + + + + + + + + + + + + - + \ No newline at end of file diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..9818eb08533 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,28 @@ + + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + + estate.property.offer.form + estate.property.offer + +
+ + + + + +
+
+
\ No newline at end of file diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..a6b66ecc0ba --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,30 @@ + + + + + Property tags + estate.property.tag + list,form + + + + estate.property.tag.list + estate.property.tag + + + + + + + + + estate.property.tag.form + estate.property.tag + +
+ + +
+
+
+
\ No newline at end of file diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..3e667bcaaee --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,30 @@ + + + + + Property types + estate.property.type + list,form + + + + estate.property.type.list + estate.property.type + + + + + + + + + estate.property.type.form + estate.property.type + +
+ + +
+
+
+
\ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c213a00e7ab..a5c92a4465f 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,13 +1,12 @@ - + Create new estate estate.property list,form - - + estate.property.list estate.property @@ -32,6 +31,7 @@

+ @@ -54,12 +54,35 @@ + + + + + + + + + + + + + + + + + + + + +
+ + estate.property.search estate.property @@ -77,6 +100,5 @@ -
\ No newline at end of file From ed6ba5689c353f8b35d7b34243beebc15d0e4a79 Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Wed, 22 Oct 2025 13:54:26 +0200 Subject: [PATCH 08/24] [ADD] estate: implement compute, inverse, and onchange methods && [IMP] estate: attempt to resolve runbot error messages --- estate/__init__.py | 2 +- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 - estate/models/estate_offer.py | 26 ++++++++--- estate/models/estate_property.py | 48 ++++++++++++++------ estate/views/estate_menus.xml | 2 +- estate/views/estate_property_offer_views.xml | 12 +++-- estate/views/estate_property_tag_views.xml | 2 +- estate/views/estate_property_type_views.xml | 2 +- estate/views/estate_property_views.xml | 22 ++++----- 10 files changed, 76 insertions(+), 42 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 507f32b15a6..e93b3e65618 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -4,6 +4,7 @@ 'depends': ['base'], 'author': "GACI Jugurtha (jugac)", 'application': True, + 'licence': 'LGPL-3', 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', diff --git a/estate/models/__init__.py b/estate/models/__init__.py index fb51d9b31fe..eebacbb7932 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -2,4 +2,3 @@ from . import estate_type from . import estate_tags from . import estate_offer - diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index cc09419e47d..6af03f5601d 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -1,19 +1,33 @@ -from odoo import models, fields +from odoo import models, fields, api + class EstateProperty(models.Model): _name = "estate.property.offer" _description = "Estate property offers" - price = fields.Float(required=True) status = fields.Selection( selection=[ - ('refused', 'Refused'), - ('accepted', 'Accepted') - ], + ('refused','Refused'), + ('accepted','Accepted') + ], copy=False ) - + validity = fields.Integer(default=7) + create_date = fields.Date(default=lambda self: fields.Date.today(), readonly=True) + date_deadline = fields.Date(compute="_compute_date_deadline", inverse="_inverse_validity") # relations partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True) + + + @api.depends("validity") + def _compute_date_deadline(self): + for record in self: + record.date_deadline = fields.Date.add(record.create_date, days=record.validity) + + + @api.depends("date_deadline") + def _inverse_validity(self): + for record in self: + record.validity = (record.date_deadline - record.create_date).days \ No newline at end of file diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index d2afe3cb862..92356faf88f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,40 +1,36 @@ -from odoo import models, fields +from odoo import models, fields, api + class EstateProperty(models.Model): _name = "estate.property" _description = "Table that stores the estate properties" - name = fields.Char(required=True) description = fields.Text() notes = fields.Html() postcode = fields.Char() - date_availability = fields.Date( string="Available From", copy=False, default=lambda self: fields.Date.add(fields.Date.today(), months=3) ) - expected_price = fields.Float(required=True) - selling_price = fields.Float(readonly=False, copy=False) - + selling_price = fields.Float(copy=False) + best_offer = fields.Float(copy=False, compute="_compute_best_offer") bedrooms = fields.Integer(default=2) living_area = fields.Integer(string="Living Area (sqm)") facades = fields.Integer() garage = fields.Boolean() - garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( selection=[ - ('north', 'North'), - ('south', 'South'), - ('east', 'East'), - ('west', 'West') + ('north','North'), + ('south','South'), + ('east','East'), + ('west','West') ] ) - active = fields.Boolean(default=True) state = fields.Selection( selection=[ @@ -43,10 +39,34 @@ class EstateProperty(models.Model): ('accepted', 'Accepted') ] ) - + total_area = fields.Integer( + compute="_compute_total_area", + store=True, + string="Total Area (sqm)", + ) # relations property_type_id = fields.Many2one("estate.property.type") buyer_id = fields.Many2one("res.partner") salesman_id = fields.Many2one("res.users") tag_ids = fields.Many2many("estate.property.tag") - offer_ids = fields.One2many("estate.property.offer", "partner_id") \ No newline at end of file + offer_ids = fields.One2many("estate.property.offer", "property_id") + + + @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_offer(self): + for record in self: + record.best_offer = max((offer.price for offer in record.offer_ids), default=0) + + + @api.onchange("garden") + def _onchange_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = "north" + \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index d44f4f07710..6f7cb3588e9 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 9818eb08533..82d6cb29cbe 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -18,11 +18,15 @@ estate.property.offer
- - - + + + + + + + - \ No newline at end of file + diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index a6b66ecc0ba..f5c52195e04 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -27,4 +27,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index 3e667bcaaee..3537a9382cf 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -27,4 +27,4 @@
- \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index a5c92a4465f..c81739c6a80 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -37,6 +37,7 @@ + @@ -50,30 +51,25 @@ + - + + + - - - - - - - - - - + + + - @@ -101,4 +97,4 @@
- \ No newline at end of file + From 7b1a0913e1332543ad3eae120893bd9d64fa624d Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Wed, 22 Oct 2025 15:46:27 +0200 Subject: [PATCH 09/24] [ADD] estate: add actions (chapter 9) && [FIX] estate: fix runbot errors --- estate/models/estate_offer.py | 27 +++++++---- estate/models/estate_property.py | 66 ++++++++++++++++++-------- estate/models/estate_tags.py | 6 +-- estate/models/estate_type.py | 4 +- estate/views/estate_property_views.xml | 34 +++++++++---- 5 files changed, 93 insertions(+), 44 deletions(-) diff --git a/estate/models/estate_offer.py b/estate/models/estate_offer.py index 6af03f5601d..b6653a8d9ce 100644 --- a/estate/models/estate_offer.py +++ b/estate/models/estate_offer.py @@ -1,33 +1,44 @@ from odoo import models, fields, api -class EstateProperty(models.Model): +class EstatePropertyOffer(models.Model): _name = "estate.property.offer" - _description = "Estate property offers" + _description = "Estate offers" price = fields.Float(required=True) status = fields.Selection( selection=[ - ('refused','Refused'), - ('accepted','Accepted') + ('refused', 'Refused'), + ('accepted', 'Accepted') ], copy=False ) validity = fields.Integer(default=7) create_date = fields.Date(default=lambda self: fields.Date.today(), readonly=True) date_deadline = fields.Date(compute="_compute_date_deadline", inverse="_inverse_validity") + # relations partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True) - - @api.depends("validity") + @api.depends("validity", "create_date") def _compute_date_deadline(self): for record in self: record.date_deadline = fields.Date.add(record.create_date, days=record.validity) - @api.depends("date_deadline") def _inverse_validity(self): for record in self: - record.validity = (record.date_deadline - record.create_date).days \ No newline at end of file + record.validity = (record.date_deadline - record.create_date).days + + def action_accept(self): + for record in self: + record.status = 'accepted' + record.property_id.buyer_id = record.partner_id + record.property_id.selling_price = record.price + return True + + def action_refuse(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 92356faf88f..a751663e2ad 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,72 +1,96 @@ -from odoo import models, fields, api +from odoo import models, fields, api, exceptions class EstateProperty(models.Model): _name = "estate.property" - _description = "Table that stores the estate properties" + _description = "Estate Property" name = fields.Char(required=True) description = fields.Text() notes = fields.Html() postcode = fields.Char() + date_availability = fields.Date( - string="Available From", - copy=False, + string="Available From", + copy=False, default=lambda self: fields.Date.add(fields.Date.today(), months=3) ) + expected_price = fields.Float(required=True) selling_price = fields.Float(copy=False) best_offer = fields.Float(copy=False, compute="_compute_best_offer") + bedrooms = fields.Integer(default=2) living_area = fields.Integer(string="Living Area (sqm)") + total_area = fields.Integer( + compute="_compute_total_area", + store=True, + string="Total Area (sqm)", + ) facades = fields.Integer() garage = fields.Boolean() + garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( selection=[ - ('north','North'), - ('south','South'), - ('east','East'), - ('west','West') + ('north', 'North'), + ('south', 'South'), + ('east', 'East'), + ('west', 'West') ] ) + active = fields.Boolean(default=True) state = fields.Selection( selection=[ - ('new', 'New'), - ('received', 'Received'), + ('new', 'New'), + ('received', 'Received'), ('accepted', 'Accepted') - ] + ], + default="new" ) - total_area = fields.Integer( - compute="_compute_total_area", - store=True, - string="Total Area (sqm)", + status = fields.Selection( + selection=[('new', 'New'), ('sold', 'Sold'), ('canceled', 'Canceled')], + default="new" ) + # relations property_type_id = fields.Many2one("estate.property.type") buyer_id = fields.Many2one("res.partner") - salesman_id = fields.Many2one("res.users") + salesman_id = fields.Many2one("res.users", default=lambda self: self.env.user) tag_ids = fields.Many2many("estate.property.tag") offer_ids = fields.One2many("estate.property.offer", "property_id") - @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_offer(self): for record in self: record.best_offer = max((offer.price for offer in record.offer_ids), default=0) - @api.onchange("garden") def _onchange_garden(self): if self.garden: self.garden_area = 10 - self.garden_orientation = "north" - \ No newline at end of file + self.garden_orientation = "north" + + def action_set_sold(self): + for record in self: + if record.status == 'canceled': + raise exceptions.UserError('A canceled property cannot be sold') + else: + record.status = 'sold' + return True + + def action_set_canceled(self): + for record in self: + if record.status == 'sold': + raise exceptions.UserError('A sold property cannot be canceled') + else: + record.status = 'canceled' + return True + diff --git a/estate/models/estate_tags.py b/estate/models/estate_tags.py index c4654c4e9d5..7a01dadcf55 100644 --- a/estate/models/estate_tags.py +++ b/estate/models/estate_tags.py @@ -1,8 +1,8 @@ from odoo import models, fields -class EstateProperty(models.Model): - _name = "estate.property.tag" - _description = "Estate property tags" +class EstatePropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Estate tags" name = fields.Char(required=True) diff --git a/estate/models/estate_type.py b/estate/models/estate_type.py index 43845803ea3..e48f5f33708 100644 --- a/estate/models/estate_type.py +++ b/estate/models/estate_type.py @@ -1,8 +1,8 @@ from odoo import models, fields -class EstateProperty(models.Model): + +class EstatePropertyType(models.Model): _name = "estate.property.type" _description = "Estate property types" - name = fields.Char(required=True) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c81739c6a80..8b018b1176c 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -27,10 +27,17 @@ estate.property
+
+

+ + + @@ -44,14 +51,15 @@ - - - - - - - - + + + + + + + + + @@ -63,13 +71,19 @@ + + + +

+ +

+ + + + + + + + + + + + +
+ diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 683ea0334fd..10557a88b7c 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -4,20 +4,21 @@ Create new estate estate.property list,form + {'search_default_available': True} estate.property.list estate.property - + - + @@ -28,17 +29,17 @@
-

- - - - + + + @@ -57,8 +58,8 @@ - - + + @@ -68,15 +69,14 @@ - - + + - + + diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..c91122ac668 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,7 @@ import { Component } from "@odoo/owl"; +import { Counter } from "./counter/counter" export class Playground extends Component { static template = "awesome_owl.playground"; + static components = {Counter} } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..ab03885247b 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -3,7 +3,9 @@
- hello world + hello world
+ This is my counter component :
+
diff --git a/estate/models/res_users.py b/estate/models/res_users.py index 3fef230ec7d..8b747f48d76 100644 --- a/estate/models/res_users.py +++ b/estate/models/res_users.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import models from odoo.fields import One2many diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py index 0286e393ecc..861da6ebbca 100644 --- a/estate_account/models/estate_property.py +++ b/estate_account/models/estate_property.py @@ -8,7 +8,7 @@ def action_set_sold(self): journal = self.env['account.journal'].search([('type', '=', 'sale')], limit=1) - created_invoice = self.env["account.move"].create({ + self.env["account.move"].create({ 'partner_id': self.buyer_id.id, 'move_type': 'out_invoice', 'journal_id': journal.id, From ae39b49e82e950f73708adf78e4c583a2bd35c32 Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Tue, 28 Oct 2025 15:12:17 +0100 Subject: [PATCH 23/24] [ADD] awesome_owl: finish all the chapters --- awesome_owl/static/src/card/card.js | 20 +++++++++++++ awesome_owl/static/src/card/card.xml | 17 +++++++++++ awesome_owl/static/src/counter/counter.js | 8 ++++-- awesome_owl/static/src/counter/counter.xml | 14 +++++++-- awesome_owl/static/src/main.js | 12 ++++---- awesome_owl/static/src/playground.js | 14 +++++++-- awesome_owl/static/src/playground.xml | 23 ++++++++++++--- awesome_owl/static/src/todo/item.js | 7 +++++ awesome_owl/static/src/todo/item.xml | 10 +++++++ awesome_owl/static/src/todo/list.js | 33 ++++++++++++++++++++++ awesome_owl/static/src/todo/list.xml | 20 +++++++++++++ awesome_owl/static/src/utils.js | 12 ++++++++ 12 files changed, 174 insertions(+), 16 deletions(-) create mode 100644 awesome_owl/static/src/card/card.js create mode 100644 awesome_owl/static/src/card/card.xml create mode 100644 awesome_owl/static/src/todo/item.js create mode 100644 awesome_owl/static/src/todo/item.xml create mode 100644 awesome_owl/static/src/todo/list.js create mode 100644 awesome_owl/static/src/todo/list.xml create mode 100644 awesome_owl/static/src/utils.js diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js new file mode 100644 index 00000000000..7a4001509e7 --- /dev/null +++ b/awesome_owl/static/src/card/card.js @@ -0,0 +1,20 @@ +import { Component, useState } from "@odoo/owl"; + + +export class Card extends Component { + static template = "awesome_owl.card.card"; + static props = { + title: {type: String}, + slots: { + type: Object, + shape: { + default: {} + } + } + } + + setup() { + this.state = useState({visible: true}) + } + +} diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml new file mode 100644 index 00000000000..54c5dad547a --- /dev/null +++ b/awesome_owl/static/src/card/card.xml @@ -0,0 +1,17 @@ + + + + + +
+ +
+

+ +
+
+
+
+
diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js index ab8737cf2d3..199d80ac1d6 100644 --- a/awesome_owl/static/src/counter/counter.js +++ b/awesome_owl/static/src/counter/counter.js @@ -1,7 +1,9 @@ -import { Component, useState } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl" + export class Counter extends Component { - static template = "awesome_owl.counter.counter"; + static template = "awesome_owl.counter.counter" + static props = ['onchange?'] setup() { this.counter = useState({ value: 0 }); @@ -9,5 +11,7 @@ export class Counter extends Component { increment() { this.counter.value++; + if(this.props.onchange != null && this.props.onchange != undefined) + this.props.onchange() } } diff --git a/awesome_owl/static/src/counter/counter.xml b/awesome_owl/static/src/counter/counter.xml index 9859c1cd3ee..4e8f007ad01 100644 --- a/awesome_owl/static/src/counter/counter.xml +++ b/awesome_owl/static/src/counter/counter.xml @@ -1,7 +1,17 @@ -

Counter:

- +
+ +

Counter:

+ + + +
diff --git a/awesome_owl/static/src/main.js b/awesome_owl/static/src/main.js index 1aaea902b55..686037c08ec 100644 --- a/awesome_owl/static/src/main.js +++ b/awesome_owl/static/src/main.js @@ -1,12 +1,12 @@ -import { whenReady } from "@odoo/owl"; -import { mountComponent } from "@web/env"; -import { Playground } from "./playground"; +import { whenReady } from "@odoo/owl" +import { mountComponent } from "@web/env" +import { Playground } from "./playground" + const config = { dev: true, name: "Owl Tutorial" -}; +} // Mount the Playground component when the document.body is ready -whenReady(() => mountComponent(Playground, document.body, config)); - +whenReady(() => mountComponent(Playground, document.body, config)) diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index c91122ac668..8c25b264355 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,7 +1,17 @@ -import { Component } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; import { Counter } from "./counter/counter" +import { Card } from "./card/card" +import { TodoList } from "./todo/list"; + export class Playground extends Component { static template = "awesome_owl.playground"; - static components = {Counter} + static components = {Counter, Card, TodoList} + + setup() { + this.sum = useState({value: 2}) + } + incrementSum() { + this.sum.value += 1 + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index ab03885247b..a49b6947032 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -1,11 +1,26 @@ -
- hello world
- This is my counter component :
- +
+ + +

SUM is

+
+ +
+ + + + +

hello

+
+
+ +
+

My todo list for this week

+ +
diff --git a/awesome_owl/static/src/todo/item.js b/awesome_owl/static/src/todo/item.js new file mode 100644 index 00000000000..951687b43a3 --- /dev/null +++ b/awesome_owl/static/src/todo/item.js @@ -0,0 +1,7 @@ +import { Component } from "@odoo/owl" + + +export class TodoItem extends Component { + static template = "awesome_owl.todo.item" + static props = ['todo', 'toggleState', 'deleteTodo'] +} diff --git a/awesome_owl/static/src/todo/item.xml b/awesome_owl/static/src/todo/item.xml new file mode 100644 index 00000000000..d0794b90b0e --- /dev/null +++ b/awesome_owl/static/src/todo/item.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/awesome_owl/static/src/todo/list.js b/awesome_owl/static/src/todo/list.js new file mode 100644 index 00000000000..50aa57f348f --- /dev/null +++ b/awesome_owl/static/src/todo/list.js @@ -0,0 +1,33 @@ +import { Component, useState, useRef, onMounted } from "@odoo/owl" +import { TodoItem } from "./item" +import { useAutofocus } from './../utils' + + +export class TodoList extends Component { + static template = "awesome_owl.todo.list" + static components = {TodoItem} + + setup() { + this.todos = useState([]) + useAutofocus('todoInputRef') + } + addTodo(ev) { + if(ev.keyCode == '13' && ev.target.value != '') { + this.todos.push({ + id: this.todos.length + 1, + description: ev.target.value, + isCompleted: false + }) + ev.target.value = '' + } + } + toggleState(id) { + const [res] = this.todos.filter(todo => todo.id == id) + res.isCompleted = true + + } + deleteTodo(id) { + const [res] = this.todos.filter(todo => todo.id == id) + this.todos.splice(this.todos.indexOf(res), 1) + } +} diff --git a/awesome_owl/static/src/todo/list.xml b/awesome_owl/static/src/todo/list.xml new file mode 100644 index 00000000000..aeaf3e0e71c --- /dev/null +++ b/awesome_owl/static/src/todo/list.xml @@ -0,0 +1,20 @@ + + + + + +
    +
  1. + +
  2. +
+

+ You have tasks to do +

+
+ +
diff --git a/awesome_owl/static/src/utils.js b/awesome_owl/static/src/utils.js new file mode 100644 index 00000000000..9ae78255305 --- /dev/null +++ b/awesome_owl/static/src/utils.js @@ -0,0 +1,12 @@ +import { useRef, onMounted } from "@odoo/owl"; + + +const useAutofocus = (refName) => { + const ref = useRef(refName) + onMounted(() => ref.el.focus()) +} + + +export { + useAutofocus +} \ No newline at end of file From 5342ef16ae674ba7dde45733dc0211aeb57bda65 Mon Sep 17 00:00:00 2001 From: Jugurtha Date: Tue, 28 Oct 2025 16:31:29 +0100 Subject: [PATCH 24/24] [FIX] awesome_owl: fix toggle state --- awesome_owl/static/src/todo/item.xml | 2 +- awesome_owl/static/src/todo/list.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awesome_owl/static/src/todo/item.xml b/awesome_owl/static/src/todo/item.xml index d0794b90b0e..38f50ae7f23 100644 --- a/awesome_owl/static/src/todo/item.xml +++ b/awesome_owl/static/src/todo/item.xml @@ -2,7 +2,7 @@ - + diff --git a/awesome_owl/static/src/todo/list.js b/awesome_owl/static/src/todo/list.js index 50aa57f348f..9848cbd2d6c 100644 --- a/awesome_owl/static/src/todo/list.js +++ b/awesome_owl/static/src/todo/list.js @@ -23,7 +23,7 @@ export class TodoList extends Component { } toggleState(id) { const [res] = this.todos.filter(todo => todo.id == id) - res.isCompleted = true + res.isCompleted = !res.isCompleted } deleteTodo(id) {