From 5e4d31ec4c30e37ae27df0cc60402dc41ba057c5 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Tue, 17 Jun 2025 16:03:39 +0200 Subject: [PATCH 01/42] [ADD] estate: Creating apps and models --- estate/__init__.py | 1 + estate/__manifest__.py | 16 ++++++++++++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 24 ++++++++++++++++++++++++ estate/views/estate_menus.xml | 7 +++++++ estate/views/estate_property_views.xml | 7 +++++++ 6 files changed, 56 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 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 new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..edf59be781b --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,16 @@ +{ + 'name': "estate", + 'version': '0.1', + 'depends': ['base'], + 'data':[ + 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml', + ], + 'author': "baje", + 'category': 'Uncategorized', + 'description': """ + An app to manage a Real Estate Agency + """, + '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..ddab98621ac --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,24 @@ +from odoo import fields, models +from dateutil.relativedelta import relativedelta + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "estate properties" + _order = "id" + + name = fields.Char("Title", required=True) + description = fields.Text("Description") + postcode = fields.Char("Postcode") + date_availability = fields.Date("Available From", default=(fields.Date.today()+relativedelta(months=3))) + expected_price = fields.Float("Expected Price", required=True) + selling_price = fields.Float("Selling Price", readonly=True, copy=False) + bedrooms = fields.Integer("Bedrooms", default=2) + living_area = fields.Integer("Living Area (sqm)") + facades = fields.Integer("Facades") + garage = fields.Boolean("Garage") + garden = fields.Boolean("Garden") + garden_area = fields.Integer("Garden Area (sqm)") + garden_orientation = fields.Selection( + string="Garden Orientation", + selection=[('north',"North"),('south',"South"),('east',"East"),('west',"West")] + ) \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..692f3b63f12 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,7 @@ + + + + + + + \ 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..a81214e39f4 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,7 @@ + + + Properties + estate.property + list,form + + \ No newline at end of file From 23ad338eb1b3f935684dbfcb8f2abbdfa374e93c Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Tue, 17 Jun 2025 16:36:59 +0200 Subject: [PATCH 02/42] [IMP] estate: Add Security rules --- estate/__manifest__.py | 4 ++++ estate/security/ir.model.access.csv | 2 ++ 2 files changed, 6 insertions(+) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index edf59be781b..6ebcc594105 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,9 +3,13 @@ 'version': '0.1', 'depends': ['base'], 'data':[ +<<<<<<< HEAD 'security/ir.model.access.csv', 'views/estate_property_views.xml', 'views/estate_menus.xml', +======= + 'security/ir.model.access.csv' +>>>>>>> 6f53f08 (Security) ], 'author': "baje", 'category': 'Uncategorized', diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..976b61e8cb3 --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file From 902379ac30aceee79383957042cff2ce5a0591cd Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 08:51:01 +0200 Subject: [PATCH 03/42] [IMP] estate: Add fields and parameters to models estate_property --- estate/models/estate_property.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index ddab98621ac..3b14dc4f7f0 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -21,4 +21,20 @@ class EstateProperty(models.Model): garden_orientation = fields.Selection( string="Garden Orientation", selection=[('north',"North"),('south',"South"),('east',"East"),('west',"West")] + ) + + active= fields.Boolean("Active", default=True) + + state = fields.Selection( + selection=[ + ('new', "New"), + ('offer_received', "Offer Received"), + ('offer_accepted', "Offer Accepted"), + ('sold', "Sold"), + ('canceled', "Canceled"), + ], + string="Status", + required=True, + copy=False, + default="new", ) \ No newline at end of file From 9420b3aa4a8ad74565edacfc3db9492b2017b220 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 09:31:16 +0200 Subject: [PATCH 04/42] [IMP] estate: Add list view --- estate/views/estate_property_views.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index a81214e39f4..ba356a1ed03 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -1,7 +1,23 @@ + Properties estate.property list,form + + estate.property.list + estate.property + + + + + + + + + + + + \ No newline at end of file From ef18c3f16b887d3a42167c13f79be92635c7fa2c Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 09:39:08 +0200 Subject: [PATCH 05/42] [FIX] estate: pipeline fix --- estate/__manifest__.py | 8 ++------ estate/models/__init__.py | 2 +- estate/models/estate_property.py | 9 +++++---- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 6ebcc594105..0424d7da305 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,14 +2,10 @@ 'name': "estate", 'version': '0.1', 'depends': ['base'], - 'data':[ -<<<<<<< HEAD + 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', 'views/estate_menus.xml', -======= - 'security/ir.model.access.csv' ->>>>>>> 6f53f08 (Security) ], 'author': "baje", 'category': 'Uncategorized', @@ -17,4 +13,4 @@ An app to manage a Real Estate Agency """, 'application': True, -} \ 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 3b14dc4f7f0..2bdac212142 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -2,6 +2,7 @@ from dateutil.relativedelta import relativedelta class EstateProperty(models.Model): + _name = "estate.property" _description = "estate properties" _order = "id" @@ -9,7 +10,7 @@ class EstateProperty(models.Model): name = fields.Char("Title", required=True) description = fields.Text("Description") postcode = fields.Char("Postcode") - date_availability = fields.Date("Available From", default=(fields.Date.today()+relativedelta(months=3))) + date_availability = fields.Date("Available From", default=(fields.Date.today() + relativedelta(months=3))) expected_price = fields.Float("Expected Price", required=True) selling_price = fields.Float("Selling Price", readonly=True, copy=False) bedrooms = fields.Integer("Bedrooms", default=2) @@ -20,10 +21,10 @@ class EstateProperty(models.Model): garden_area = fields.Integer("Garden Area (sqm)") garden_orientation = fields.Selection( string="Garden Orientation", - selection=[('north',"North"),('south',"South"),('east',"East"),('west',"West")] + selection=[('north',"North"), ('south',"South"), ('east',"East"), ('west',"West")] ) - active= fields.Boolean("Active", default=True) + active = fields.Boolean("Active", default=True) state = fields.Selection( selection=[ @@ -37,4 +38,4 @@ class EstateProperty(models.Model): required=True, copy=False, default="new", - ) \ No newline at end of file + ) From 1be0e7a2fa195da1ab8857b5538dfa8fe17d2f32 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 10:29:56 +0200 Subject: [PATCH 06/42] [IMP] estate: Add form view --- estate/models/estate_property.py | 12 +++++--- estate/views/estate_property_views.xml | 42 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 2bdac212142..4bcb6fe9d30 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,7 @@ from odoo import fields, models from dateutil.relativedelta import relativedelta + class EstateProperty(models.Model): _name = "estate.property" @@ -21,12 +22,16 @@ class EstateProperty(models.Model): garden_area = fields.Integer("Garden Area (sqm)") garden_orientation = fields.Selection( string="Garden Orientation", - selection=[('north',"North"), ('south',"South"), ('east',"East"), ('west',"West")] + selection=[ + ('north', "North"), + ('south', "South"), + ('east', "East"), + ('west', "West") + ], ) - active = fields.Boolean("Active", default=True) - state = fields.Selection( + string="Status", selection=[ ('new', "New"), ('offer_received', "Offer Received"), @@ -34,7 +39,6 @@ class EstateProperty(models.Model): ('sold', "Sold"), ('canceled', "Canceled"), ], - string="Status", required=True, copy=False, default="new", diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index ba356a1ed03..371d6b93efe 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,6 +5,48 @@ estate.property list,form + + estate.property.form + estate.property + +
+ +
+

+ +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
estate.property.list estate.property From 8ec4cb4bfcd64b3a4f21b3a6ce66edc135314f02 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 11:24:09 +0200 Subject: [PATCH 07/42] [IMP] estate: Add Searchbar view --- estate/views/estate_property_views.xml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 371d6b93efe..c0481c7e41c 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -9,13 +9,12 @@ estate.property.form estate.property -
+

-
@@ -62,4 +61,23 @@ + + estate.property.search + estate.property + + + + + + + + + + + + + + + + \ No newline at end of file From 9182210a24aab204f0be3c958fc0f1607900eec7 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 13:11:34 +0200 Subject: [PATCH 08/42] [IMP] estate: Add many2one relations --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 3 +++ estate/models/estate_property_type.py | 10 ++++++++++ estate/security/ir.model.access.csv | 3 ++- estate/views/estate_menus.xml | 3 +++ estate/views/estate_property_type_views.xml | 7 +++++++ estate/views/estate_property_views.xml | 9 +++++++++ 8 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 0424d7da305..8fd74723f9a 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,6 +5,7 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', 'views/estate_menus.xml', ], 'author': "baje", diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..40092a2d810 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +1,2 @@ from . import estate_property +from . import estate_property_type diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 4bcb6fe9d30..a8facf2bd70 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -43,3 +43,6 @@ class EstateProperty(models.Model): copy=False, default="new", ) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") + user_id = fields.Many2one("res.users", string="Salesman", default=lambda self: self.env.user) + buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..225c58fa5b2 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class EstatePropertyType(models.Model): + + _name = "estate.property.type" + _description = "estate property type" + _order = "id" + + name = fields.Char("Name", required=True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 976b61e8cb3..ac245eddbb7 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,3 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink -access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 692f3b63f12..2cb432daec7 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -3,5 +3,8 @@ + + + \ No newline at end of file diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..6cbd93a23cf --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,7 @@ + + + Property Types + estate.property.type + list,form + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c0481c7e41c..d8ff46cc078 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -18,6 +18,7 @@ + @@ -41,6 +42,14 @@ + + + + + + + +
From c316dd2f0fc42cd5c2b4051a3f5ea5f889928237 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 13:31:21 +0200 Subject: [PATCH 09/42] [IMP] estate: Add many2many relations --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 1 + estate/models/estate_property_tag.py | 10 ++++++++++ estate/security/ir.model.access.csv | 3 ++- estate/views/estate_menus.xml | 1 + estate/views/estate_property_tag_views.xml | 7 +++++++ estate/views/estate_property_views.xml | 1 + 8 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/views/estate_property_tag_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 8fd74723f9a..3d18f0af827 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,6 +5,7 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_tag_views.xml', 'views/estate_property_type_views.xml', 'views/estate_menus.xml', ], diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 40092a2d810..c620ac481a3 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,2 +1,3 @@ from . import estate_property from . import estate_property_type +from . import estate_property_tag diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a8facf2bd70..824cdc9e2af 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -46,3 +46,4 @@ class EstateProperty(models.Model): property_type_id = fields.Many2one("estate.property.type", string="Property Type") user_id = fields.Many2one("res.users", string="Salesman", 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") diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..ea1c16e0cb4 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class EstatePropertyTag(models.Model): + + _name = "estate.property.tag" + _description = "estate property tag" + _order = "id" + + name = fields.Char("Name", required=True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index ac245eddbb7..849c40dcc5b 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,3 +1,4 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 -access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 2cb432daec7..19b8bcb398d 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -5,6 +5,7 @@ + \ No newline at end of file diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..3920037c137 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,7 @@ + + + Property Tags + estate.property.tag + list,form + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index d8ff46cc078..28d0ce13060 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -16,6 +16,7 @@ + From b398197a49f442b180ca5144c1ec7eacf99b407d Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 14:17:52 +0200 Subject: [PATCH 10/42] [IMP] estate: Add one2many relations --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 1 + estate/models/estate_property_offer.py | 20 ++++++++++++++++++++ estate/security/ir.model.access.csv | 3 ++- estate/views/estate_property_offer_views.xml | 13 +++++++++++++ estate/views/estate_property_views.xml | 3 +++ 7 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/views/estate_property_offer_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 3d18f0af827..52f697be444 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -5,6 +5,7 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', + 'views/estate_property_offer_views.xml', 'views/estate_property_tag_views.xml', 'views/estate_property_type_views.xml', 'views/estate_menus.xml', diff --git a/estate/models/__init__.py b/estate/models/__init__.py index c620ac481a3..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1,3 +1,4 @@ from . import estate_property from . import estate_property_type from . import estate_property_tag +from . import estate_property_offer diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 824cdc9e2af..54b99713195 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -47,3 +47,4 @@ class EstateProperty(models.Model): user_id = fields.Many2one("res.users", string="Salesman", 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..7dc9b70adc9 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,20 @@ +from odoo import fields, models + + +class EstatePropertyOffer(models.Model): + + _name = "estate.property.offer" + _description = "estate property offer" + _order = "id" + + price = fields.Float(string="Price") + status = fields.Selection( + string="Status", + selection=[ + ('accepted', "Accepted"), + ('refused', "Refused"), + ], + copy=False, + ) + partner_id = fields.Many2one("res.partner", string="Partner", required=True) + property_id = fields.Many2one("estate.property", string="Property", required=True) \ No newline at end of file diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 849c40dcc5b..4c593ed42e4 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,4 +1,5 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 -access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 \ 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..aff3d01acb2 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,13 @@ + + + estate.property.offer.list + estate.property.offer + + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 28d0ce13060..31894413296 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -43,6 +43,9 @@ + + + From 5786a5920f896369d04537295e944b0b4fe0692c Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 15:24:07 +0200 Subject: [PATCH 11/42] [IMP] estate: Add compute and inverse --- estate/models/estate_property.py | 17 ++++++++++++++++- estate/models/estate_property_offer.py | 17 +++++++++++++++-- estate/views/estate_property_views.xml | 2 ++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 54b99713195..b5b233ff48b 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import fields, models, api from dateutil.relativedelta import relativedelta @@ -48,3 +48,18 @@ class EstateProperty(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("Total Area (sqm)", compute="_compute_total_area") + best_price = fields.Float("Best Offer", 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: + record.best_price = max(record.offer_ids.mapped("price")) if record.offer_ids else 0.0 + + diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 7dc9b70adc9..4e2e246796b 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,4 +1,5 @@ -from odoo import fields, models +from odoo import fields, models, api +from dateutil.relativedelta import relativedelta class EstatePropertyOffer(models.Model): @@ -17,4 +18,16 @@ class EstatePropertyOffer(models.Model): copy=False, ) partner_id = fields.Many2one("res.partner", string="Partner", required=True) - property_id = fields.Many2one("estate.property", string="Property", required=True) \ No newline at end of file + property_id = fields.Many2one("estate.property", string="Property", required=True) + validity = fields.Integer("Validity (days)") + deadline = fields.Date("Deadline", compute="_compute_deadline", inverse="_inverse_deadline") + + + @api.depends("validity") + def _compute_deadline(self): + for record in self: + record.deadline = fields.Date.today() + relativedelta(days=record.validity) + + def _inverse_deadline(self): + for record in self: + record.validity = (record.deadline - fields.Date.today()).days diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 31894413296..e88cac7ea6b 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -25,6 +25,7 @@ + @@ -40,6 +41,7 @@ + From 042287811b7915e1380162f69afa2adee25ddb77 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 16:28:46 +0200 Subject: [PATCH 12/42] [IMP] estate: Add onchange method --- estate/__manifest__.py | 1 + estate/models/estate_property.py | 11 +++++++++-- estate/models/estate_property_offer.py | 1 - estate/models/estate_property_tag.py | 3 ++- estate/models/estate_property_type.py | 3 ++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 52f697be444..02c1c5e4e31 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -16,4 +16,5 @@ An app to manage a Real Estate Agency """, 'application': True, + 'license': 'LGPL-3', } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index b5b233ff48b..188744fd880 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -51,7 +51,6 @@ class EstateProperty(models.Model): total_area = fields.Integer("Total Area (sqm)", compute="_compute_total_area") best_price = fields.Float("Best Offer", compute="_compute_best_price") - @api.depends("living_area", "garden_area") def _compute_total_area(self): for record in self: @@ -62,4 +61,12 @@ def _compute_best_price(self): for record in self: record.best_price = max(record.offer_ids.mapped("price")) if record.offer_ids else 0.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 + \ No newline at end of file diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 4e2e246796b..63cb68364d6 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -22,7 +22,6 @@ class EstatePropertyOffer(models.Model): validity = fields.Integer("Validity (days)") deadline = fields.Date("Deadline", compute="_compute_deadline", inverse="_inverse_deadline") - @api.depends("validity") def _compute_deadline(self): for record in self: diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index ea1c16e0cb4..031392502b2 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -7,4 +7,5 @@ class EstatePropertyTag(models.Model): _description = "estate property tag" _order = "id" - name = fields.Char("Name", required=True) \ No newline at end of file + name = fields.Char("Name", required=True) + \ No newline at end of file diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 225c58fa5b2..4b62d468cfc 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -7,4 +7,5 @@ class EstatePropertyType(models.Model): _description = "estate property type" _order = "id" - name = fields.Char("Name", required=True) \ No newline at end of file + name = fields.Char("Name", required=True) + \ No newline at end of file From 294945831d489fc2cc6980929e544a78381831e5 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 18 Jun 2025 17:26:38 +0200 Subject: [PATCH 13/42] [IMP] estate: Add buttons --- estate/models/estate_property.py | 16 +++++++++++++++- estate/models/estate_property_offer.py | 15 +++++++++++++++ estate/models/estate_property_tag.py | 1 - estate/models/estate_property_type.py | 1 - estate/views/estate_property_offer_views.xml | 2 ++ estate/views/estate_property_views.xml | 5 +++++ 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 188744fd880..38ea4af5828 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,5 +1,6 @@ from odoo import fields, models, api from dateutil.relativedelta import relativedelta +from odoo.exceptions import UserError class EstateProperty(models.Model): @@ -69,4 +70,17 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = False - \ No newline at end of file + + def action_sold(self): + for record in self: + if record.state == 'canceled': + raise UserError("Canceled properties cannot be sold.") + else: + record.state = 'sold' + + def action_cancel(self): + for record in self: + if record.state == 'sold': + raise UserError("Sold properties cannot be canceled.") + else: + record.state = 'canceled' diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 63cb68364d6..7a1a75886e4 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,5 +1,6 @@ from odoo import fields, models, api from dateutil.relativedelta import relativedelta +from odoo.exceptions import UserError class EstatePropertyOffer(models.Model): @@ -30,3 +31,17 @@ def _compute_deadline(self): def _inverse_deadline(self): for record in self: record.validity = (record.deadline - fields.Date.today()).days + + def action_accept(self): + if 'accepted' in self.mapped("property_id.offer_ids.status"): + raise UserError("An offer is already accepted") + else: + for record in self: + record.status = 'accepted' + record.property_id.state = 'offer_accepted' + record.property_id.buyer_id = record.partner_id + record.property_id.selling_price = record.price + + def action_refuse(self): + for record in self: + record.status = 'refused' diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 031392502b2..becfa4ad17f 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -8,4 +8,3 @@ class EstatePropertyTag(models.Model): _order = "id" name = fields.Char("Name", required=True) - \ No newline at end of file diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index 4b62d468cfc..5010f9a75db 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -8,4 +8,3 @@ class EstatePropertyType(models.Model): _order = "id" name = fields.Char("Name", required=True) - \ 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 aff3d01acb2..3b63b67d9fd 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -6,6 +6,8 @@ + + From 2a00cff5f9db0275c4cd65f7b9cb1c080d53f53d Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Thu, 19 Jun 2025 17:30:12 +0200 Subject: [PATCH 17/42] [IMP] estate: Add create and delete conditions - user inheritance --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 5 +++++ estate/models/estate_property_offer.py | 13 +++++++++++++ estate/models/res_users.py | 10 ++++++++++ estate/security/ir.model.access.csv | 2 +- estate/views/res_users_views.xml | 14 ++++++++++++++ 7 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 estate/models/res_users.py create mode 100644 estate/views/res_users_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 02c1c5e4e31..73f2379d538 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -8,6 +8,7 @@ 'views/estate_property_offer_views.xml', 'views/estate_property_tag_views.xml', 'views/estate_property_type_views.xml', + 'views/res_users_views.xml', 'views/estate_menus.xml', ], 'author': "baje", diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 2f1821a39c1..9a2189b6382 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -2,3 +2,4 @@ from . import estate_property_type from . import estate_property_tag from . import estate_property_offer +from . import res_users diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a582fcc64eb..3d6629d0cc6 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -95,3 +95,8 @@ def _check_selling_price(self): for record in self: if not float_is_zero(record.selling_price, precision_rounding=0.01) and float_compare(record.selling_price, record.expected_price * 0.9, precision_rounding=0.01) < 0: raise ValidationError("the selling price cannot be lower than 90% of the expected price") + + @api.ondelete(at_uninstall=False) + def _unlink_if_state_is_new_or_cancelled(self): + if any(record.state not in ('new', 'canceled') for record in self): + raise UserError("Only new and canceled properties can be deleted.") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 9ecbb07be83..0734cfe3e76 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,6 +1,7 @@ from odoo import fields, models, api from dateutil.relativedelta import relativedelta from odoo.exceptions import UserError +from odoo.tools.float_utils import float_compare class EstatePropertyOffer(models.Model): @@ -50,3 +51,15 @@ def action_accept(self): def action_refuse(self): for record in self: record.status = 'refused' + + @api.model_create_multi + def create(self, values): + for vals in values: + if vals.get("property_id") and vals.get("price"): + prop = self.env["estate.property"].browse(vals["property_id"]) + if prop.offer_ids: + max_offer = max(prop.mapped("offer_ids.price")) + if float_compare(vals["price"], max_offer, precision_rounding=0.01) <= 0: + raise UserError(f"The offer must be higher than {max_offer}") + prop.state = "offer_received" + return super().create(values) diff --git a/estate/models/res_users.py b/estate/models/res_users.py new file mode 100644 index 00000000000..f54f78760c1 --- /dev/null +++ b/estate/models/res_users.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class ResUsers(models.Model): + + _inherit = "res.users" + + property_ids = fields.One2many( + "estate.property", "user_id", string="Properties", domain=[("state", "in", ["new", "offer_received"])] + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 4c593ed42e4..49bca99cac8 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 access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1 access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1 access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1 -access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file +access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/res_users_views.xml b/estate/views/res_users_views.xml new file mode 100644 index 00000000000..50553fb15f0 --- /dev/null +++ b/estate/views/res_users_views.xml @@ -0,0 +1,14 @@ + + + res.users.form.inherit.estate + res.users + + + + + + + + + + \ No newline at end of file From bfdb4ba66da6fc6ea029a78542dad1b1d5657f48 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Fri, 20 Jun 2025 09:50:44 +0200 Subject: [PATCH 18/42] [ADD] estate_account: Add link between estate and account --- estate_account/__init__.py | 1 + estate_account/__manifest__.py | 12 ++++++++ estate_account/models/__init__.py | 1 + estate_account/models/estate_property.py | 39 ++++++++++++++++++++++++ 4 files changed, 53 insertions(+) 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_account/__init__.py b/estate_account/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/estate_account/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py new file mode 100644 index 00000000000..42c2057abf2 --- /dev/null +++ b/estate_account/__manifest__.py @@ -0,0 +1,12 @@ +{ + 'name': "estate_account", + 'version': '0.1', + 'depends': ['estate', 'account'], + 'author': "baje", + 'category': 'Uncategorized', + 'description': """ + Link between estate and accounting apps + """, + 'auto_install': True, + 'license': 'LGPL-3', +} diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/estate_account/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py new file mode 100644 index 00000000000..65a78af4ff9 --- /dev/null +++ b/estate_account/models/estate_property.py @@ -0,0 +1,39 @@ +from odoo import models + + +class EstateProperty(models.Model): + + _inherit = "estate.property" + + def action_sold(self): + res = super().action_sold() + journal = self.env["account.journal"].search([("type", "=", "sale")], limit=1) + for prop in self: + self.env["account.move"].create( + { + "partner_id": prop.buyer_id.id, + "move_type": "out_invoice", + "journal_id": journal.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": prop.name, + "quantity": 1.0, + "price_unit": prop.selling_price * 6.0 / 100.0, + }, + ), + ( + 0, + 0, + { + "name": "Administrative fees", + "quantity": 1.0, + "price_unit": 100.0, + }, + ), + ], + } + ) + return res From 9a0df1af5041d840f1b0a9f7dd88f16f2e5473df Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Fri, 20 Jun 2025 10:07:44 +0200 Subject: [PATCH 19/42] [IMP] estate: Add kanban view --- estate/views/estate_property_views.xml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index f3b497d081e..c2d607e30c9 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -3,9 +3,24 @@ Properties estate.property - list,form + list,form,kanban {'search_default_available': True} + + estate.property.kanban + estate.property + + + + +
+ +
+
+
+
+
+
estate.property.form estate.property From 199b3c2bdefc8f4057e8f0c610459b257d5797c4 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Fri, 20 Jun 2025 10:41:42 +0200 Subject: [PATCH 20/42] [IMP] estate: Add more informations on cards --- estate/views/estate_property_views.xml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c2d607e30c9..290574c116a 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -10,11 +10,26 @@ estate.property.kanban estate.property - + + -
- +
+
+ + + +
+
+ Expected Price: +
+
+ Best Offer: +
+
+ Selling Price: +
+
From d5a3567703593da37089154f46bfa01eabe36208 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Fri, 20 Jun 2025 11:55:02 +0200 Subject: [PATCH 21/42] [REF] estate: Refactoring to the coding guidelines --- .gitignore | 2 +- .vscode/settings.json | 3 + estate/__manifest__.py | 2 +- estate/models/estate_property.py | 30 +++---- estate/models/estate_property_offer.py | 24 +++--- estate/models/estate_property_tag.py | 5 +- estate/models/estate_property_type.py | 5 +- estate/models/res_users.py | 3 +- estate/views/estate_property_offer_views.xml | 4 +- estate/views/estate_property_tag_views.xml | 4 +- estate/views/estate_property_type_views.xml | 8 +- estate/views/estate_property_views.xml | 16 ++-- estate/views/res_users_views.xml | 4 +- estate_account/__manifest__.py | 2 +- estate_account/models/estate_property.py | 1 + ruff.toml | 84 ++++++++++++++++++++ 16 files changed, 145 insertions(+), 52 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 ruff.toml diff --git a/.gitignore b/.gitignore index b6e47617de1..9fe17bcebb3 100644 --- a/.gitignore +++ b/.gitignore @@ -126,4 +126,4 @@ venv.bak/ dmypy.json # Pyre type checker -.pyre/ +.pyre/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..ff5300ef481 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.languageServer": "None" +} \ No newline at end of file diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 73f2379d538..b02cdfea387 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,6 +1,6 @@ { 'name': "estate", - 'version': '0.1', + 'version': '18.0.1.0.0', 'depends': ['base'], 'data': [ 'security/ir.model.access.csv', diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 3d6629d0cc6..5de60e30dde 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,23 +1,27 @@ -from odoo import fields, models, api +from odoo import fields, models, api, _ from dateutil.relativedelta import relativedelta from odoo.exceptions import UserError, ValidationError from odoo.tools.float_utils import float_compare, float_is_zero class EstateProperty(models.Model): + """Model representing a real estate property.""" _name = "estate.property" - _description = "estate properties" + _description = "Real Estate Properties" _order = "id desc" _sql_constraints = [ - ('check_expected_price', 'CHECK(expected_price > 0 )', 'expected price must be strictly positive'), - ('check_selling_price', 'CHECK(selling_price >= 0 )', 'selling price must be positive'), + ('check_expected_price', 'CHECK(expected_price > 0 )', 'Expected price must be strictly positive.'), + ('check_selling_price', 'CHECK(selling_price >= 0 )', 'Selling price must be positive.'), ] + def _default_date_availability(self): + return fields.Date.today() + relativedelta(months=3) + name = fields.Char("Title", required=True) description = fields.Text("Description") postcode = fields.Char("Postcode") - date_availability = fields.Date("Available From", default=(fields.Date.today() + relativedelta(months=3))) + date_availability = fields.Date("Available From", default=_default_date_availability) expected_price = fields.Float("Expected Price", required=True) selling_price = fields.Float("Selling Price", readonly=True, copy=False) bedrooms = fields.Integer("Bedrooms", default=2) @@ -32,7 +36,7 @@ class EstateProperty(models.Model): ('north', "North"), ('south', "South"), ('east', "East"), - ('west', "West") + ('west', "West"), ], ) active = fields.Boolean("Active", default=True) @@ -79,24 +83,22 @@ def _onchange_garden(self): def action_sold(self): for record in self: if record.state == 'canceled': - raise UserError("Canceled properties cannot be sold.") - else: - record.state = 'sold' + raise UserError(_("Canceled properties cannot be sold.")) + record.state = 'sold' def action_cancel(self): for record in self: if record.state == 'sold': - raise UserError("Sold properties cannot be canceled.") - else: - record.state = 'canceled' + raise UserError(_("Sold properties cannot be canceled.")) + record.state = 'canceled' @api.constrains('expected_price', 'selling_price') def _check_selling_price(self): for record in self: if not float_is_zero(record.selling_price, precision_rounding=0.01) and float_compare(record.selling_price, record.expected_price * 0.9, precision_rounding=0.01) < 0: - raise ValidationError("the selling price cannot be lower than 90% of the expected price") + raise ValidationError(_("The selling price cannot be lower than 90% of the expected price.")) @api.ondelete(at_uninstall=False) def _unlink_if_state_is_new_or_cancelled(self): if any(record.state not in ('new', 'canceled') for record in self): - raise UserError("Only new and canceled properties can be deleted.") + raise UserError(_("Only new and canceled properties can be deleted.")) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 0734cfe3e76..8b08fdc0e71 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,16 +1,17 @@ -from odoo import fields, models, api +from odoo import fields, models, api, _ from dateutil.relativedelta import relativedelta from odoo.exceptions import UserError from odoo.tools.float_utils import float_compare class EstatePropertyOffer(models.Model): + """Model representing an offer for a real estate property.""" _name = "estate.property.offer" - _description = "estate property offer" + _description = "Real Estate Property Offers" _order = "price desc" _sql_constraints = [ - ('check_offer_price', 'CHECK(price > 0)', 'An offer price must be strictly positive') + ('check_offer_price', 'CHECK(price > 0)', 'An offer price must be strictly positive.'), ] price = fields.Float(string="Price") @@ -24,7 +25,7 @@ class EstatePropertyOffer(models.Model): ) partner_id = fields.Many2one("res.partner", string="Partner", required=True) property_id = fields.Many2one("estate.property", string="Property", required=True) - validity = fields.Integer("Validity (days)") + validity = fields.Integer("Validity (days)", default=7) deadline = fields.Date("Deadline", compute="_compute_deadline", inverse="_inverse_deadline") property_type_id = fields.Many2one( "estate.property.type", related="property_id.property_type_id", string="Property Type") @@ -40,13 +41,12 @@ def _inverse_deadline(self): def action_accept(self): if 'accepted' in self.mapped("property_id.offer_ids.status"): - raise UserError("An offer is already accepted") - else: - for record in self: - record.status = 'accepted' - record.property_id.state = 'offer_accepted' - record.property_id.buyer_id = record.partner_id - record.property_id.selling_price = record.price + raise UserError(_("An offer is already accepted.")) + for record in self: + record.status = 'accepted' + record.property_id.state = 'offer_accepted' + record.property_id.buyer_id = record.partner_id + record.property_id.selling_price = record.price def action_refuse(self): for record in self: @@ -60,6 +60,6 @@ def create(self, values): if prop.offer_ids: max_offer = max(prop.mapped("offer_ids.price")) if float_compare(vals["price"], max_offer, precision_rounding=0.01) <= 0: - raise UserError(f"The offer must be higher than {max_offer}") + raise UserError(_("The offer must be higher than %.2f."), max_offer) prop.state = "offer_received" return super().create(values) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py index 037bb2ac83c..667d8281ded 100644 --- a/estate/models/estate_property_tag.py +++ b/estate/models/estate_property_tag.py @@ -2,12 +2,13 @@ class EstatePropertyTag(models.Model): + """Model representing a property tag.""" _name = "estate.property.tag" - _description = "estate property tag" + _description = "Real Estate Property Tag" _order = "name" _sql_constraints = [ - ('unique_name', 'UNIQUE(name)', 'the tag name must be unique') + ('unique_name', 'UNIQUE(name)', 'The tag name must be unique.'), ] name = fields.Char("Name", required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py index b6d65f16ed9..9475f0d5d13 100644 --- a/estate/models/estate_property_type.py +++ b/estate/models/estate_property_type.py @@ -2,12 +2,13 @@ class EstatePropertyType(models.Model): + """Model representing a property type.""" _name = "estate.property.type" - _description = "estate property type" + _description = "Real Estate Property Type" _order = "name" _sql_constraints = [ - ('unique_name', 'UNIQUE(name)', 'the type name must be unique') + ('unique_name', 'UNIQUE(name)', 'The type name must be unique.'), ] name = fields.Char("Name", required=True) diff --git a/estate/models/res_users.py b/estate/models/res_users.py index f54f78760c1..149e75823ab 100644 --- a/estate/models/res_users.py +++ b/estate/models/res_users.py @@ -2,9 +2,10 @@ class ResUsers(models.Model): + """Model extending users""" _inherit = "res.users" property_ids = fields.One2many( - "estate.property", "user_id", string="Properties", domain=[("state", "in", ["new", "offer_received"])] + "estate.property", "user_id", string="Properties", domain=[("state", "in", ["new", "offer_received"])], ) diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index d77ffc7c037..beceb00415f 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -4,8 +4,8 @@ estate.property.offer list,form - - estate.property.offer.list + + estate.property.offer.view.list estate.property.offer diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml index a5267f8ad8f..2d3a20591e4 100644 --- a/estate/views/estate_property_tag_views.xml +++ b/estate/views/estate_property_tag_views.xml @@ -4,8 +4,8 @@ estate.property.tag list,form - - estate.property.tag.list + + estate.property.tag.view.list estate.property.tag diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index f962f6fdc5f..82ec5f8bc7a 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -4,8 +4,8 @@ estate.property.type list,form - - estate.property.type.form + + estate.property.type.view.form estate.property.type @@ -38,8 +38,8 @@ - - estate.property.type.list + + estate.property.type.view.list estate.property.type diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 290574c116a..2192030ac33 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,8 +6,8 @@ list,form,kanban {'search_default_available': True} - - estate.property.kanban + + estate.property.view.kanban estate.property @@ -36,8 +36,8 @@ - - estate.property.form + + estate.property.view.form estate.property
@@ -99,8 +99,8 @@
- - estate.property.list + + estate.property.view.list estate.property @@ -114,8 +114,8 @@ - - estate.property.search + + estate.property.view.search estate.property diff --git a/estate/views/res_users_views.xml b/estate/views/res_users_views.xml index 50553fb15f0..050b9273457 100644 --- a/estate/views/res_users_views.xml +++ b/estate/views/res_users_views.xml @@ -1,6 +1,6 @@ - - res.users.form.inherit.estate + + res.users.view.form.inherit.estate res.users diff --git a/estate_account/__manifest__.py b/estate_account/__manifest__.py index 42c2057abf2..05cdf78d6de 100644 --- a/estate_account/__manifest__.py +++ b/estate_account/__manifest__.py @@ -1,6 +1,6 @@ { 'name': "estate_account", - 'version': '0.1', + 'version': '18.0.1.0.0', 'depends': ['estate', 'account'], 'author': "baje", 'category': 'Uncategorized', diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py index 65a78af4ff9..233fbad2bdb 100644 --- a/estate_account/models/estate_property.py +++ b/estate_account/models/estate_property.py @@ -2,6 +2,7 @@ class EstateProperty(models.Model): + """Extend estate property to create an invoice on sold.""" _inherit = "estate.property" diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000000..bdb80c921a5 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,84 @@ +# automatically generated file by the runbot nightly ruff checks, do not modify +# for ruff version 0.11.4 (or higher) +# note: 'E241', 'E272', 'E201', 'E221' are ignored on runbot in test files when formating a table like structure (more than two space) +# some rules present here are not enabled on runbot (yet) but are still advised to follow when possible : ["B904", "COM812", "E741", "EM101", "I001", "RET", "RUF021", "TRY002", "UP006", "UP007"] + + +target-version = "py310" + +[lint] +preview = true +select = [ + "BLE", # flake8-blind-except + "C", # flake8-comprehensions + "COM", # flake8-commas + "E", # pycodestyle Error + "EM", # flake8-errmsg + "EXE", # flake8-executable + "F", # Pyflakes + "FA", # flake8-future-annotations + "FLY", # flynt + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PLC", # Pylint Convention + "PLE", # Pylint Error + "PLW", # Pylint Warning + "PYI", # flake8-pyi + "RET", # flake8-return + "RUF", # Ruff-specific rules + "SIM", # flake8-simplify + "SLOT", # flake8-slots + "T", # flake8-print + "TC", # flake8-type-checking + "TID", # flake8-tidy-imports + "TRY", # tryceratops + "UP", # pyupgrade + "W", # pycodestyle Warning + "YTT", # flake8-2020 +] +ignore = [ + "C408", # unnecessary-collection-call + "C420", # unnecessary-dict-comprehension-for-iterable + "C901", # complex-structure + "E266", # multiple-leading-hashes-for-block-comment + "E501", # line-too-long + "E713", # not-in-test + "EM102", # f-string-in-exception + "FA100", # future-rewritable-type-annotation + "I001", # import sorting + "PGH003", # blanket-type-ignore + "PIE790", # unnecessary-placeholder + "PIE808", # unnecessary-range-start + "PLC2701", # import-private-name + "PLW2901", # redefined-loop-name + "RUF001", # ambiguous-unicode-character-string + "RUF005", # collection-literal-concatenation + "RUF012", # mutable-class-default + "RUF100", # unused-noqa + "SIM102", # collapsible-if + "SIM108", # if-else-block-instead-of-if-exp + "SIM117", # multiple-with-statements + "TID252", # relative-imports + "TRY003", # raise-vanilla-args + "TRY300", # try-consider-else + "TRY400", # error-instead-of-exception + "UP031", # printf-string-formatting + "UP038", # non-pep604-isinstance +] + +[lint.per-file-ignores] +"**/__init__.py" = [ + "F401", # unused-import +] + +[lint.isort] +# https://www.odoo.com/documentation/18.0/contributing/development/coding_guidelines.html#imports +section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"] +known-first-party = ["odoo"] +known-local-folder = ["odoo.addons"] \ No newline at end of file From 2c139ac158f92f653f4a897470c954893434d67c Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Mon, 23 Jun 2025 11:39:43 +0200 Subject: [PATCH 22/42] [ADD] awesome_owl: implement counter and card --- awesome_owl/__manifest__.py | 2 +- awesome_owl/static/src/card/card.js | 5 +++++ awesome_owl/static/src/card/card.xml | 11 +++++++++++ awesome_owl/static/src/counter/counter.js | 13 +++++++++++++ awesome_owl/static/src/counter/counter.xml | 9 +++++++++ awesome_owl/static/src/playground.js | 3 +++ awesome_owl/static/src/playground.xml | 7 ++++++- awesome_owl/views/templates.xml | 1 + 8 files changed, 49 insertions(+), 2 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/counter/counter.js create mode 100644 awesome_owl/static/src/counter/counter.xml diff --git a/awesome_owl/__manifest__.py b/awesome_owl/__manifest__.py index 77abad510ef..c37a1ab8609 100644 --- a/awesome_owl/__manifest__.py +++ b/awesome_owl/__manifest__.py @@ -38,5 +38,5 @@ 'awesome_owl/static/src/**/*', ], }, - 'license': 'AGPL-3' + 'license': 'AGPL-3', } diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js new file mode 100644 index 00000000000..ceb4fb065c9 --- /dev/null +++ b/awesome_owl/static/src/card/card.js @@ -0,0 +1,5 @@ +import { Component } from "@odoo/owl"; + +export class Card extends Component { + static template = "awesome_owl.Card"; +} diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml new file mode 100644 index 00000000000..4d6797962aa --- /dev/null +++ b/awesome_owl/static/src/card/card.xml @@ -0,0 +1,11 @@ + + + +
+
+
+

+
+
+
+
diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js new file mode 100644 index 00000000000..bf295e5c4d9 --- /dev/null +++ b/awesome_owl/static/src/counter/counter.js @@ -0,0 +1,13 @@ +import { Component, useState } from "@odoo/owl"; + +export class Counter extends Component { + static template = "awesome_owl.Counter"; + + setup(){ + this.state = useState({ value: 0 }); + } + + increment(){ + this.state.value++; + } +} diff --git a/awesome_owl/static/src/counter/counter.xml b/awesome_owl/static/src/counter/counter.xml new file mode 100644 index 00000000000..4791fb6cd42 --- /dev/null +++ b/awesome_owl/static/src/counter/counter.xml @@ -0,0 +1,9 @@ + + + +
+ Counter: + +
+
+
diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 657fb8b07bb..a9035a74009 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,7 +1,10 @@ /** @odoo-module **/ import { Component } from "@odoo/owl"; +import { Counter } from "./counter/counter"; +import { Card } from "./card/card"; export class Playground extends Component { static template = "awesome_owl.playground"; + static components = { Counter, Card }; } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..27292532aec 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -3,7 +3,12 @@
- hello world + + +
+
+ +
diff --git a/awesome_owl/views/templates.xml b/awesome_owl/views/templates.xml index aa54c1a7241..3df6b44bd5b 100644 --- a/awesome_owl/views/templates.xml +++ b/awesome_owl/views/templates.xml @@ -5,6 +5,7 @@ + From 290ffe141571946c06410faa40d9dfd0b9157402 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Mon, 23 Jun 2025 13:00:04 +0200 Subject: [PATCH 23/42] [IMP] awesome_owl: Add Markup use to card --- awesome_owl/static/src/card/card.xml | 4 ++-- awesome_owl/static/src/playground.js | 7 ++++++- awesome_owl/static/src/playground.xml | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml index 4d6797962aa..23878b0723a 100644 --- a/awesome_owl/static/src/card/card.xml +++ b/awesome_owl/static/src/card/card.xml @@ -3,8 +3,8 @@
-
-

+
+

diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index a9035a74009..1c80a6c3b5d 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,10 +1,15 @@ /** @odoo-module **/ -import { Component } from "@odoo/owl"; +import { Component , markup} from "@odoo/owl"; import { Counter } from "./counter/counter"; import { Card } from "./card/card"; export class Playground extends Component { static template = "awesome_owl.playground"; static components = { Counter, Card }; + + setup(){ + this.str1 = "
some content
"; + this.str2 = markup("
some content
"); + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 27292532aec..691aa00c9ef 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -7,8 +7,8 @@
- - + +
From 2ad352d41090fd05a75a0c57fe1720068194f607 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Mon, 23 Jun 2025 14:25:03 +0200 Subject: [PATCH 24/42] [IMP] awesome_owl: Add validation - callbacks - foreach --- awesome_owl/static/src/card/card.js | 4 ++++ awesome_owl/static/src/counter/counter.js | 6 ++++++ awesome_owl/static/src/playground.js | 12 ++++++++++-- awesome_owl/static/src/playground.xml | 6 ++++-- awesome_owl/static/src/todo_list/todo_item.js | 11 +++++++++++ awesome_owl/static/src/todo_list/todo_item.xml | 9 +++++++++ awesome_owl/static/src/todo_list/todo_list.js | 15 +++++++++++++++ awesome_owl/static/src/todo_list/todo_list.xml | 10 ++++++++++ 8 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 awesome_owl/static/src/todo_list/todo_item.js create mode 100644 awesome_owl/static/src/todo_list/todo_item.xml create mode 100644 awesome_owl/static/src/todo_list/todo_list.js create mode 100644 awesome_owl/static/src/todo_list/todo_list.xml diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js index ceb4fb065c9..894b42ecd26 100644 --- a/awesome_owl/static/src/card/card.js +++ b/awesome_owl/static/src/card/card.js @@ -2,4 +2,8 @@ import { Component } from "@odoo/owl"; export class Card extends Component { static template = "awesome_owl.Card"; + static props = { + title: String, + content: String, + }; } diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js index bf295e5c4d9..6dcc108f5e9 100644 --- a/awesome_owl/static/src/counter/counter.js +++ b/awesome_owl/static/src/counter/counter.js @@ -2,6 +2,9 @@ import { Component, useState } from "@odoo/owl"; export class Counter extends Component { static template = "awesome_owl.Counter"; + static props = { + onChange: { type: Function, optional: true } + }; setup(){ this.state = useState({ value: 0 }); @@ -9,5 +12,8 @@ export class Counter extends Component { increment(){ this.state.value++; + if(this.props.onChange){ + this.props.onChange(); + } } } diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 1c80a6c3b5d..324ad4ab415 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,15 +1,23 @@ /** @odoo-module **/ -import { Component , markup} from "@odoo/owl"; +import { Component , markup, useState } from "@odoo/owl"; import { Counter } from "./counter/counter"; import { Card } from "./card/card"; +import { TodoList } from "./todo_list/todo_list"; export class Playground extends Component { static template = "awesome_owl.playground"; - static components = { Counter, Card }; + static components = { Counter, Card, TodoList }; + static props = {}; setup(){ this.str1 = "
some content
"; this.str2 = markup("
some content
"); + this.sumresult = useState({ value: 0}); + } + + sum(){ + this.sumresult.value++; + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 691aa00c9ef..c235f3344a3 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -3,13 +3,15 @@
- - + +
+
The sum is:
+
diff --git a/awesome_owl/static/src/todo_list/todo_item.js b/awesome_owl/static/src/todo_list/todo_item.js new file mode 100644 index 00000000000..3f1fa545fc2 --- /dev/null +++ b/awesome_owl/static/src/todo_list/todo_item.js @@ -0,0 +1,11 @@ +import { Component } from "@odoo/owl" + +export class TodoItem extends Component{ + static template = "awesome_owl.TodoItem"; + static props = { + todo: { + type: Object, + shape: { id: Number, description: String, isCompleted: Boolean } + } + }; +} diff --git a/awesome_owl/static/src/todo_list/todo_item.xml b/awesome_owl/static/src/todo_list/todo_item.xml new file mode 100644 index 00000000000..46a31b7223a --- /dev/null +++ b/awesome_owl/static/src/todo_list/todo_item.xml @@ -0,0 +1,9 @@ + + + +
+ . + +
+
+
diff --git a/awesome_owl/static/src/todo_list/todo_list.js b/awesome_owl/static/src/todo_list/todo_list.js new file mode 100644 index 00000000000..08823c3a052 --- /dev/null +++ b/awesome_owl/static/src/todo_list/todo_list.js @@ -0,0 +1,15 @@ +import {Component, useState} from "@odoo/owl"; +import { TodoItem } from "./todo_item"; + +export class TodoList extends Component{ + static template = "awesome_owl.TodoList"; + static components = { TodoItem }; + static props = {}; + + setup(){ + this.todos = useState([ + {id: 2, description: "write tutorial", isCompleted: true}, + {id: 3, description: "buy milk", isCompleted: false}, + ]); + } +} diff --git a/awesome_owl/static/src/todo_list/todo_list.xml b/awesome_owl/static/src/todo_list/todo_list.xml new file mode 100644 index 00000000000..61ec27bbcd4 --- /dev/null +++ b/awesome_owl/static/src/todo_list/todo_list.xml @@ -0,0 +1,10 @@ + + + +
+ + + +
+
+
From 58f4c236c257d31f47289a0c2695b9cde038922d Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Mon, 23 Jun 2025 15:07:26 +0200 Subject: [PATCH 25/42] [IMP] awesome_owl: Add dynamic attributes and event handler --- awesome_owl/static/src/todo_list/todo_item.xml | 2 +- awesome_owl/static/src/todo_list/todo_list.js | 18 ++++++++++++++---- awesome_owl/static/src/todo_list/todo_list.xml | 1 + 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/awesome_owl/static/src/todo_list/todo_item.xml b/awesome_owl/static/src/todo_list/todo_item.xml index 46a31b7223a..f0f196d187f 100644 --- a/awesome_owl/static/src/todo_list/todo_item.xml +++ b/awesome_owl/static/src/todo_list/todo_item.xml @@ -1,7 +1,7 @@ -
+
.
diff --git a/awesome_owl/static/src/todo_list/todo_list.js b/awesome_owl/static/src/todo_list/todo_list.js index 08823c3a052..8451366b24e 100644 --- a/awesome_owl/static/src/todo_list/todo_list.js +++ b/awesome_owl/static/src/todo_list/todo_list.js @@ -7,9 +7,19 @@ export class TodoList extends Component{ static props = {}; setup(){ - this.todos = useState([ - {id: 2, description: "write tutorial", isCompleted: true}, - {id: 3, description: "buy milk", isCompleted: false}, - ]); + this.nextId = 0; + this.todos = useState([]); + } + + addTodo(ev){ + if (ev.keyCode !== 13 || ev.target.value === '') { + return; + } + this.todos.push({ + id: this.nextId++, + description: ev.target.value, + isCompleted: false, + }); + ev.target.value = ''; } } diff --git a/awesome_owl/static/src/todo_list/todo_list.xml b/awesome_owl/static/src/todo_list/todo_list.xml index 61ec27bbcd4..f90a7c6b46d 100644 --- a/awesome_owl/static/src/todo_list/todo_list.xml +++ b/awesome_owl/static/src/todo_list/todo_list.xml @@ -2,6 +2,7 @@
+ From 9644d50d33f23fde8cb0586307972cd068714fb6 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Mon, 23 Jun 2025 15:42:27 +0200 Subject: [PATCH 26/42] [IMP] awesome_owl: Add autofocus on todo list --- awesome_owl/static/src/todo_list/todo_list.js | 2 ++ awesome_owl/static/src/todo_list/todo_list.xml | 2 +- awesome_owl/static/src/utils.js | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 awesome_owl/static/src/utils.js diff --git a/awesome_owl/static/src/todo_list/todo_list.js b/awesome_owl/static/src/todo_list/todo_list.js index 8451366b24e..d235a07b106 100644 --- a/awesome_owl/static/src/todo_list/todo_list.js +++ b/awesome_owl/static/src/todo_list/todo_list.js @@ -1,5 +1,6 @@ import {Component, useState} from "@odoo/owl"; import { TodoItem } from "./todo_item"; +import { useAutoFocus } from "../utils"; export class TodoList extends Component{ static template = "awesome_owl.TodoList"; @@ -9,6 +10,7 @@ export class TodoList extends Component{ setup(){ this.nextId = 0; this.todos = useState([]); + useAutoFocus("input") } addTodo(ev){ diff --git a/awesome_owl/static/src/todo_list/todo_list.xml b/awesome_owl/static/src/todo_list/todo_list.xml index f90a7c6b46d..8430d3e5fac 100644 --- a/awesome_owl/static/src/todo_list/todo_list.xml +++ b/awesome_owl/static/src/todo_list/todo_list.xml @@ -2,7 +2,7 @@
- + diff --git a/awesome_owl/static/src/utils.js b/awesome_owl/static/src/utils.js new file mode 100644 index 00000000000..de064271230 --- /dev/null +++ b/awesome_owl/static/src/utils.js @@ -0,0 +1,6 @@ +import { useRef, onMounted } from "@odoo/owl"; + +export function useAutoFocus(refName) { + const ref = useRef(refName); + onMounted(() => { ref.el.focus(); }); +} From 3355116dc8a863909855c98d32dd71961e052552 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Mon, 23 Jun 2025 16:46:54 +0200 Subject: [PATCH 27/42] [IMP] awesome_owl: Add deleting button on todo items --- awesome_owl/static/src/todo_list/todo_item.js | 12 +++++++++++- awesome_owl/static/src/todo_list/todo_item.xml | 10 +++++++--- awesome_owl/static/src/todo_list/todo_list.js | 15 +++++++++++++++ awesome_owl/static/src/todo_list/todo_list.xml | 2 +- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/awesome_owl/static/src/todo_list/todo_item.js b/awesome_owl/static/src/todo_list/todo_item.js index 3f1fa545fc2..332069a5dea 100644 --- a/awesome_owl/static/src/todo_list/todo_item.js +++ b/awesome_owl/static/src/todo_list/todo_item.js @@ -6,6 +6,16 @@ export class TodoItem extends Component{ todo: { type: Object, shape: { id: Number, description: String, isCompleted: Boolean } - } + }, + toggleTodo: { type: Function }, + removeTodo: { type: Function }, }; + + onChange(){ + this.props.toggleTodo(this.props.todo.id); + } + + onDelete(){ + this.props.removeTodo(this.props.todo.id); + } } diff --git a/awesome_owl/static/src/todo_list/todo_item.xml b/awesome_owl/static/src/todo_list/todo_item.xml index f0f196d187f..a19ee8868b7 100644 --- a/awesome_owl/static/src/todo_list/todo_item.xml +++ b/awesome_owl/static/src/todo_list/todo_item.xml @@ -1,9 +1,13 @@ -
- . - +
+ +
diff --git a/awesome_owl/static/src/todo_list/todo_list.js b/awesome_owl/static/src/todo_list/todo_list.js index d235a07b106..4e62d90cf92 100644 --- a/awesome_owl/static/src/todo_list/todo_list.js +++ b/awesome_owl/static/src/todo_list/todo_list.js @@ -24,4 +24,19 @@ export class TodoList extends Component{ }); ev.target.value = ''; } + toggleTodo(todoId){ + const todo = this.todos.find(todo => todo.id === todoId); + if(todo){ + todo.isCompleted = !todo.isCompleted; + } + } + + removeTodo(todoId){ + const index = this.todos.findIndex(todo => todo.id === todoId); + if (index >= 0) { + + this.todos.splice(index, 1); + } + } } + diff --git a/awesome_owl/static/src/todo_list/todo_list.xml b/awesome_owl/static/src/todo_list/todo_list.xml index 8430d3e5fac..e73fdc75308 100644 --- a/awesome_owl/static/src/todo_list/todo_list.xml +++ b/awesome_owl/static/src/todo_list/todo_list.xml @@ -4,7 +4,7 @@
- +
From 4a021e2731f8e94513cd70de2e5573f4a1d94afd Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Mon, 23 Jun 2025 17:04:54 +0200 Subject: [PATCH 28/42] [IMP] awesome_owl: Add slots --- awesome_owl/static/src/card/card.js | 7 ++++++- awesome_owl/static/src/card/card.xml | 4 +++- awesome_owl/static/src/playground.xml | 8 ++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js index 894b42ecd26..4deb905dcf2 100644 --- a/awesome_owl/static/src/card/card.js +++ b/awesome_owl/static/src/card/card.js @@ -4,6 +4,11 @@ export class Card extends Component { static template = "awesome_owl.Card"; static props = { title: String, - content: String, + slots: { + type: Object, + shape: { + default: true + }, + } }; } diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml index 23878b0723a..5c284e81757 100644 --- a/awesome_owl/static/src/card/card.xml +++ b/awesome_owl/static/src/card/card.xml @@ -4,7 +4,9 @@
-

+

+ +

diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index c235f3344a3..74afe81d0d7 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -7,8 +7,12 @@
- - + + some content + + + +
The sum is:
From 374b7005a1dcf7442a2ca7f7fcd352b2c77ed4cd Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Mon, 23 Jun 2025 17:22:06 +0200 Subject: [PATCH 29/42] [IMP] awesome_owl: Add t-if --- awesome_owl/static/src/card/card.js | 13 ++++++++++++- awesome_owl/static/src/card/card.xml | 7 +++++-- awesome_owl/static/src/counter/counter.js | 8 +++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js index 4deb905dcf2..def158bfd9f 100644 --- a/awesome_owl/static/src/card/card.js +++ b/awesome_owl/static/src/card/card.js @@ -1,4 +1,4 @@ -import { Component } from "@odoo/owl"; +import { Component, useState, onWillDestroy } from "@odoo/owl"; export class Card extends Component { static template = "awesome_owl.Card"; @@ -11,4 +11,15 @@ export class Card extends Component { }, } }; + + setup() { + this.state = useState({ isOpen: true }); + onWillDestroy(() => { + console.log("Card will be destroyed"); + }); + } + + toggleCard() { + this.state.isOpen = !this.state.isOpen; + } } diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml index 5c284e81757..501224d5c1f 100644 --- a/awesome_owl/static/src/card/card.xml +++ b/awesome_owl/static/src/card/card.xml @@ -3,8 +3,11 @@
-
-

+

+ + +
+

diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js index 6dcc108f5e9..ab33677fc52 100644 --- a/awesome_owl/static/src/counter/counter.js +++ b/awesome_owl/static/src/counter/counter.js @@ -1,4 +1,4 @@ -import { Component, useState } from "@odoo/owl"; +import { Component, useState, onWillDestroy } from "@odoo/owl"; export class Counter extends Component { static template = "awesome_owl.Counter"; @@ -7,7 +7,11 @@ export class Counter extends Component { }; setup(){ + console.log("Counter created"); this.state = useState({ value: 0 }); + onWillDestroy(() => { + console.log("Counter will be destroyed"); + }); } increment(){ @@ -15,5 +19,7 @@ export class Counter extends Component { if(this.props.onChange){ this.props.onChange(); } + } } + From e7ab77e0c1236a0287a73beb376ab2a85fa9aef9 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Tue, 24 Jun 2025 10:59:31 +0200 Subject: [PATCH 30/42] [ADD] awesome_dashboard: Add base layout --- awesome_dashboard/static/src/dashboard.js | 8 ++++++++ awesome_dashboard/static/src/dashboard.scss | 3 +++ awesome_dashboard/static/src/dashboard.xml | 4 +++- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 awesome_dashboard/static/src/dashboard.scss diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 637fa4bb972..3284a3f17e7 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -2,9 +2,17 @@ import { Component } from "@odoo/owl"; import { registry } from "@web/core/registry"; +import { Layout } from "@web/search/layout"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; + static components = { Layout }; + + setup(){ + this.display = { + controlPanel: {}, + }; + } } registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard.scss b/awesome_dashboard/static/src/dashboard.scss new file mode 100644 index 00000000000..51f9e73e642 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard.scss @@ -0,0 +1,3 @@ +.o_dashboard{ + background-color: gray; +} diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 1a2ac9a2fed..e6b169704af 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -2,7 +2,9 @@ - hello dashboard + + some content + From 82592bd3e9a84293386f0611c7b2a65e94ed5d0a Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Tue, 24 Jun 2025 13:29:38 +0200 Subject: [PATCH 31/42] [IMP] awesome_dashboard: Add navigation and dashboard item --- awesome_dashboard/static/src/dashboard.js | 21 ++++++++++++++++++- awesome_dashboard/static/src/dashboard.xml | 16 +++++++++++++- .../static/src/dashboard_item.js | 19 +++++++++++++++++ .../static/src/dashboard_item.xml | 12 +++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 awesome_dashboard/static/src/dashboard_item.js create mode 100644 awesome_dashboard/static/src/dashboard_item.xml diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 3284a3f17e7..e26c3cc454d 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -3,16 +3,35 @@ import { Component } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; +import { useService } from "@web/core/utils/hooks"; +import { DashboardItem } from "./dashboard_item"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; - static components = { Layout }; + static components = { Layout, DashboardItem }; setup(){ + this.action = useService("action"); this.display = { controlPanel: {}, }; } + + openCustomerView() { + this.action.doAction("base.action_partner_form"); + } + + openLeadView(){ + this.action.doAction({ + type: "ir.actions.act_window", + name: "All leads", + res_model: "crm.lead", + views: [ + [false, "list"], + [false, "form"] + ], + }); + } } registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index e6b169704af..5ab5b80bcee 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -3,7 +3,21 @@ - some content + + + + +
+ + some content + + + I love milk + + + some content + +
diff --git a/awesome_dashboard/static/src/dashboard_item.js b/awesome_dashboard/static/src/dashboard_item.js new file mode 100644 index 00000000000..a1c7fa90d5d --- /dev/null +++ b/awesome_dashboard/static/src/dashboard_item.js @@ -0,0 +1,19 @@ +import { Component } from "@odoo/owl"; + +export class DashboardItem extends Component { + static template = "awesome_dashboard.DashboardItem"; + static props = { + slots: { + type: Object, + shape: { + default: Object + }, + }, + size: { + type: Number, + default: 1, + optional: true, + + }, + } +} diff --git a/awesome_dashboard/static/src/dashboard_item.xml b/awesome_dashboard/static/src/dashboard_item.xml new file mode 100644 index 00000000000..a0ba8116392 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard_item.xml @@ -0,0 +1,12 @@ + + + + +
+
+ +
+
+
+ +
From fa0e393e7d5320d724a2a86096eb4475cff07da5 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Tue, 24 Jun 2025 13:43:11 +0200 Subject: [PATCH 32/42] [IMP] awesome_dashboard: Call the server to show statistics --- awesome_dashboard/static/src/dashboard.js | 6 ++++- awesome_dashboard/static/src/dashboard.xml | 29 +++++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index e26c3cc454d..9a8120517bd 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,10 +1,11 @@ /** @odoo-module **/ -import { Component } from "@odoo/owl"; +import { Component, onWillStart } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; import { useService } from "@web/core/utils/hooks"; import { DashboardItem } from "./dashboard_item"; +import { rpc } from "@web/core/network/rpc"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; @@ -15,6 +16,9 @@ class AwesomeDashboard extends Component { this.display = { controlPanel: {}, }; + onWillStart(async() => { + this.statistics = await rpc("/awesome_dashboard/statistics"); + }) } openCustomerView() { diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 5ab5b80bcee..3c29e70fd63 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -9,13 +9,34 @@
- some content + Average amount of t-shirt by order this month +
+ +
- - I love milk + + Average time for an order to go from 'new' to 'sent' or 'cancelled' +
+ +
+
+ + Number of new orders this month +
+ +
+
+ + Number of cancelled orders this month +
+ +
- some content + Total amount of new orders this month +
+ +
From d704def579a9b6b5205e645ea2df37c02dfa0f4f Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Tue, 24 Jun 2025 14:22:53 +0200 Subject: [PATCH 33/42] [IMP] awesome_dashboard: Add statistics service --- awesome_dashboard/static/src/dashboard.js | 4 ++-- .../static/src/statistics_service.js | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 awesome_dashboard/static/src/statistics_service.js diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 9a8120517bd..baccf6e89b2 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -5,7 +5,6 @@ import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; import { useService } from "@web/core/utils/hooks"; import { DashboardItem } from "./dashboard_item"; -import { rpc } from "@web/core/network/rpc"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; @@ -13,11 +12,12 @@ class AwesomeDashboard extends Component { setup(){ this.action = useService("action"); + this.statisticsService = useService("awesome_dashboard.statistics"); this.display = { controlPanel: {}, }; onWillStart(async() => { - this.statistics = await rpc("/awesome_dashboard/statistics"); + this.statistics = await this.statisticsService.getStatistics(); }) } diff --git a/awesome_dashboard/static/src/statistics_service.js b/awesome_dashboard/static/src/statistics_service.js new file mode 100644 index 00000000000..a1536d7f0e5 --- /dev/null +++ b/awesome_dashboard/static/src/statistics_service.js @@ -0,0 +1,17 @@ +import { rpc } from "@web/core/network/rpc"; +import { registry } from "@web/core/registry"; +import { memoize } from "@web/core/utils/functions"; + +const statisticsService = { + async: [ + "getStatistics", + ], + start(){ + + return{ + getStatistics: memoize(() => rpc("/awesome_dashboard/statistics")) + }; + }, +}; + +registry.category("services").add("awesome_dashboard.statistics", statisticsService); From e0c04181699b9c5072aee3cec0d8a5caa697faa8 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Tue, 24 Jun 2025 17:06:55 +0200 Subject: [PATCH 34/42] [IMP] awesome_dashboard: Add pie chart --- awesome_dashboard/static/src/dashboard.js | 3 +- awesome_dashboard/static/src/dashboard.xml | 4 +++ awesome_dashboard/static/src/pie_chart.js | 41 ++++++++++++++++++++++ awesome_dashboard/static/src/pie_chart.xml | 10 ++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 awesome_dashboard/static/src/pie_chart.js create mode 100644 awesome_dashboard/static/src/pie_chart.xml diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index baccf6e89b2..160aba1a6c2 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -5,10 +5,11 @@ import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; import { useService } from "@web/core/utils/hooks"; import { DashboardItem } from "./dashboard_item"; +import { PieChart } from "./pie_chart"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; - static components = { Layout, DashboardItem }; + static components = { Layout, DashboardItem, PieChart }; setup(){ this.action = useService("action"); diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 3c29e70fd63..0ccbd0de1eb 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -38,6 +38,10 @@
+ + Shirt orders by size + +
diff --git a/awesome_dashboard/static/src/pie_chart.js b/awesome_dashboard/static/src/pie_chart.js new file mode 100644 index 00000000000..fcaf3ed010c --- /dev/null +++ b/awesome_dashboard/static/src/pie_chart.js @@ -0,0 +1,41 @@ +import { loadJS } from "@web/core/assets"; +import { getColor } from "@web/core/colors/colors"; +import { Component, onWillStart, onWillUnmount, onMounted, useRef } from "@odoo/owl"; + +export class PieChart extends Component { + static template = "awesome_dashboard.PieChart"; + static props = { + label: { type: String, optional: true }, + data: { type: Object, optional: true }, + }; + + setup(){ + this.canvasRef = useRef("canvas"); + onWillStart (() => loadJS("/web/static/lib/Chart/Chart.js")); + onMounted(() => { + this.renderChart(); + }); + onWillUnmount(() => { + this.chart.destroy(); + }); + }; + + renderChart(){ + const labels = Object.keys(this.props.data); + const data = Object.values(this.props.data); + const color = labels.map((_, index) => getColor(index)); + this.chart = new Chart(this.canvasRef.el, { + type: "pie", + data: { + labels: labels, + datasets: [ + { + data: data, + backgroundColor: color, + }, + ], + }, + }); + } +} + diff --git a/awesome_dashboard/static/src/pie_chart.xml b/awesome_dashboard/static/src/pie_chart.xml new file mode 100644 index 00000000000..a7ef5641dcd --- /dev/null +++ b/awesome_dashboard/static/src/pie_chart.xml @@ -0,0 +1,10 @@ + + + +
+
+ +
+
+
+
From d2cdb2a88089b64a1e029477e335d776eb013e14 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 25 Jun 2025 09:15:47 +0200 Subject: [PATCH 35/42] [IMP] awesome_dashboard: reactive use --- awesome_dashboard/__manifest__.py | 5 +++++ awesome_dashboard/static/src/dashboard.js | 9 +++------ awesome_dashboard/static/src/dashboard.xml | 2 +- .../static/src/dashboard_loader.js | 10 ++++++++++ .../static/src/dashboard_loader.xml | 6 ++++++ awesome_dashboard/static/src/pie_chart.js | 13 ++++++++----- .../static/src/statistics_service.js | 18 +++++++++++------- 7 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 awesome_dashboard/static/src/dashboard_loader.js create mode 100644 awesome_dashboard/static/src/dashboard_loader.xml diff --git a/awesome_dashboard/__manifest__.py b/awesome_dashboard/__manifest__.py index 31406e8addb..2d86782d5b1 100644 --- a/awesome_dashboard/__manifest__.py +++ b/awesome_dashboard/__manifest__.py @@ -24,7 +24,12 @@ 'assets': { 'web.assets_backend': [ 'awesome_dashboard/static/src/**/*', + ('remove', 'awesome_dashboard/static/src/dashboard/**/*'), ], + 'awesome_dashboard.dashboard': [ + 'awesome_dashboard/static/src/dashboard/**/*', + ], + }, 'license': 'AGPL-3' } diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js index 160aba1a6c2..5cbf93d68eb 100644 --- a/awesome_dashboard/static/src/dashboard.js +++ b/awesome_dashboard/static/src/dashboard.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { Component, onWillStart } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; import { useService } from "@web/core/utils/hooks"; @@ -13,13 +13,10 @@ class AwesomeDashboard extends Component { setup(){ this.action = useService("action"); - this.statisticsService = useService("awesome_dashboard.statistics"); + this.statistics = useState(useService("awesome_dashboard.statistics")); this.display = { controlPanel: {}, }; - onWillStart(async() => { - this.statistics = await this.statisticsService.getStatistics(); - }) } openCustomerView() { @@ -39,4 +36,4 @@ class AwesomeDashboard extends Component { } } -registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard); +registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml index 0ccbd0de1eb..85ebe7eb4cd 100644 --- a/awesome_dashboard/static/src/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard.xml @@ -7,7 +7,7 @@ -
+
Average amount of t-shirt by order this month
diff --git a/awesome_dashboard/static/src/dashboard_loader.js b/awesome_dashboard/static/src/dashboard_loader.js new file mode 100644 index 00000000000..1d71955edda --- /dev/null +++ b/awesome_dashboard/static/src/dashboard_loader.js @@ -0,0 +1,10 @@ +import { registry } from "@web/core/registry"; +import { LazyComponent } from "@web/core/assets"; +import { Component } from "@odoo/owl"; + +class AwesomeDashboardLoader extends Component { + static template = "awesome_dashboard.AwesomeDashboardLoader"; + static components = { LazyComponent }; + static props = {}; +} +registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboardLoader); \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard_loader.xml b/awesome_dashboard/static/src/dashboard_loader.xml new file mode 100644 index 00000000000..b8fe07446b9 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard_loader.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/awesome_dashboard/static/src/pie_chart.js b/awesome_dashboard/static/src/pie_chart.js index fcaf3ed010c..b7349b520c2 100644 --- a/awesome_dashboard/static/src/pie_chart.js +++ b/awesome_dashboard/static/src/pie_chart.js @@ -1,6 +1,6 @@ import { loadJS } from "@web/core/assets"; import { getColor } from "@web/core/colors/colors"; -import { Component, onWillStart, onWillUnmount, onMounted, useRef } from "@odoo/owl"; +import { Component, onWillStart, onWillUnmount, onMounted, onWillUpdateProps, useRef } from "@odoo/owl"; export class PieChart extends Component { static template = "awesome_dashboard.PieChart"; @@ -15,14 +15,18 @@ export class PieChart extends Component { onMounted(() => { this.renderChart(); }); + onWillUpdateProps((nextProps) => { + this.chart.destroy(); + this.renderChart(nextProps); + }) onWillUnmount(() => { this.chart.destroy(); }); }; - renderChart(){ - const labels = Object.keys(this.props.data); - const data = Object.values(this.props.data); + renderChart(props = this.props) { + const labels = Object.keys(props.data); + const data = Object.values(props.data); const color = labels.map((_, index) => getColor(index)); this.chart = new Chart(this.canvasRef.el, { type: "pie", @@ -38,4 +42,3 @@ export class PieChart extends Component { }); } } - diff --git a/awesome_dashboard/static/src/statistics_service.js b/awesome_dashboard/static/src/statistics_service.js index a1536d7f0e5..eb4da19fe8c 100644 --- a/awesome_dashboard/static/src/statistics_service.js +++ b/awesome_dashboard/static/src/statistics_service.js @@ -1,16 +1,20 @@ import { rpc } from "@web/core/network/rpc"; import { registry } from "@web/core/registry"; -import { memoize } from "@web/core/utils/functions"; +import { reactive } from "@odoo/owl"; const statisticsService = { - async: [ - "getStatistics", - ], start(){ + const statistics = reactive({ isReady: false }); - return{ - getStatistics: memoize(() => rpc("/awesome_dashboard/statistics")) - }; + async function getData(){ + const updates = await rpc("/awesome_dashboard/statistics"); + Object.assign(statistics, updates, { isReady: true }); + } + + setInterval(getData, 1000*60*10); + getData(); + + return statistics; }, }; From a0dabb838c7b7fdc95e1656d793833241793a268 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 25 Jun 2025 09:37:47 +0200 Subject: [PATCH 36/42] [MOV] awesome_dashboard: made bundle folder for dashboard --- awesome_dashboard/static/src/{ => dashboard}/dashboard.js | 0 awesome_dashboard/static/src/{ => dashboard}/dashboard.scss | 0 awesome_dashboard/static/src/{ => dashboard}/dashboard.xml | 0 awesome_dashboard/static/src/{ => dashboard}/dashboard_item.js | 0 awesome_dashboard/static/src/{ => dashboard}/dashboard_item.xml | 0 awesome_dashboard/static/src/{ => dashboard}/pie_chart.js | 0 awesome_dashboard/static/src/{ => dashboard}/pie_chart.xml | 0 .../static/src/{ => dashboard}/statistics_service.js | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename awesome_dashboard/static/src/{ => dashboard}/dashboard.js (100%) rename awesome_dashboard/static/src/{ => dashboard}/dashboard.scss (100%) rename awesome_dashboard/static/src/{ => dashboard}/dashboard.xml (100%) rename awesome_dashboard/static/src/{ => dashboard}/dashboard_item.js (100%) rename awesome_dashboard/static/src/{ => dashboard}/dashboard_item.xml (100%) rename awesome_dashboard/static/src/{ => dashboard}/pie_chart.js (100%) rename awesome_dashboard/static/src/{ => dashboard}/pie_chart.xml (100%) rename awesome_dashboard/static/src/{ => dashboard}/statistics_service.js (100%) diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard/dashboard.js similarity index 100% rename from awesome_dashboard/static/src/dashboard.js rename to awesome_dashboard/static/src/dashboard/dashboard.js diff --git a/awesome_dashboard/static/src/dashboard.scss b/awesome_dashboard/static/src/dashboard/dashboard.scss similarity index 100% rename from awesome_dashboard/static/src/dashboard.scss rename to awesome_dashboard/static/src/dashboard/dashboard.scss diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard/dashboard.xml similarity index 100% rename from awesome_dashboard/static/src/dashboard.xml rename to awesome_dashboard/static/src/dashboard/dashboard.xml diff --git a/awesome_dashboard/static/src/dashboard_item.js b/awesome_dashboard/static/src/dashboard/dashboard_item.js similarity index 100% rename from awesome_dashboard/static/src/dashboard_item.js rename to awesome_dashboard/static/src/dashboard/dashboard_item.js diff --git a/awesome_dashboard/static/src/dashboard_item.xml b/awesome_dashboard/static/src/dashboard/dashboard_item.xml similarity index 100% rename from awesome_dashboard/static/src/dashboard_item.xml rename to awesome_dashboard/static/src/dashboard/dashboard_item.xml diff --git a/awesome_dashboard/static/src/pie_chart.js b/awesome_dashboard/static/src/dashboard/pie_chart.js similarity index 100% rename from awesome_dashboard/static/src/pie_chart.js rename to awesome_dashboard/static/src/dashboard/pie_chart.js diff --git a/awesome_dashboard/static/src/pie_chart.xml b/awesome_dashboard/static/src/dashboard/pie_chart.xml similarity index 100% rename from awesome_dashboard/static/src/pie_chart.xml rename to awesome_dashboard/static/src/dashboard/pie_chart.xml diff --git a/awesome_dashboard/static/src/statistics_service.js b/awesome_dashboard/static/src/dashboard/statistics_service.js similarity index 100% rename from awesome_dashboard/static/src/statistics_service.js rename to awesome_dashboard/static/src/dashboard/statistics_service.js From 1207f8cc2c9412439fe33e383f3338ce3b42b64e Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 25 Jun 2025 10:49:14 +0200 Subject: [PATCH 37/42] [MOV] awesome_dashboard: made a pie chart folder --- awesome_dashboard/static/src/dashboard/dashboard.js | 2 +- .../static/src/{dashboard => pie_chart}/pie_chart.js | 0 .../static/src/{dashboard => pie_chart}/pie_chart.xml | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename awesome_dashboard/static/src/{dashboard => pie_chart}/pie_chart.js (100%) rename awesome_dashboard/static/src/{dashboard => pie_chart}/pie_chart.xml (100%) diff --git a/awesome_dashboard/static/src/dashboard/dashboard.js b/awesome_dashboard/static/src/dashboard/dashboard.js index 5cbf93d68eb..ffaffc39a5d 100644 --- a/awesome_dashboard/static/src/dashboard/dashboard.js +++ b/awesome_dashboard/static/src/dashboard/dashboard.js @@ -5,7 +5,7 @@ import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; import { useService } from "@web/core/utils/hooks"; import { DashboardItem } from "./dashboard_item"; -import { PieChart } from "./pie_chart"; +import { PieChart } from "../pie_chart/pie_chart"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; diff --git a/awesome_dashboard/static/src/dashboard/pie_chart.js b/awesome_dashboard/static/src/pie_chart/pie_chart.js similarity index 100% rename from awesome_dashboard/static/src/dashboard/pie_chart.js rename to awesome_dashboard/static/src/pie_chart/pie_chart.js diff --git a/awesome_dashboard/static/src/dashboard/pie_chart.xml b/awesome_dashboard/static/src/pie_chart/pie_chart.xml similarity index 100% rename from awesome_dashboard/static/src/dashboard/pie_chart.xml rename to awesome_dashboard/static/src/pie_chart/pie_chart.xml From a5d023cd45ad6a0a7b95aafea4ff20f5c40c8c4e Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 25 Jun 2025 11:20:05 +0200 Subject: [PATCH 38/42] [IMP] awesome_dashboard: Generic Dashboard --- .../static/src/dashboard/dashboard.js | 5 +- .../static/src/dashboard/dashboard.xml | 40 ++----------- .../static/src/dashboard/dashboard_items.js | 60 +++++++++++++++++++ .../static/src/pie_chart/pie_chart.js | 4 +- .../src/pie_chart_card/pie_chart_card.js | 11 ++++ .../src/pie_chart_card/pie_chart_card.xml | 7 +++ .../static/src/stat_card/stat_card.js | 9 +++ .../static/src/stat_card/stat_card.xml | 9 +++ 8 files changed, 108 insertions(+), 37 deletions(-) create mode 100644 awesome_dashboard/static/src/dashboard/dashboard_items.js create mode 100644 awesome_dashboard/static/src/pie_chart_card/pie_chart_card.js create mode 100644 awesome_dashboard/static/src/pie_chart_card/pie_chart_card.xml create mode 100644 awesome_dashboard/static/src/stat_card/stat_card.js create mode 100644 awesome_dashboard/static/src/stat_card/stat_card.xml diff --git a/awesome_dashboard/static/src/dashboard/dashboard.js b/awesome_dashboard/static/src/dashboard/dashboard.js index ffaffc39a5d..46a5ae7a145 100644 --- a/awesome_dashboard/static/src/dashboard/dashboard.js +++ b/awesome_dashboard/static/src/dashboard/dashboard.js @@ -5,15 +5,16 @@ import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; import { useService } from "@web/core/utils/hooks"; import { DashboardItem } from "./dashboard_item"; -import { PieChart } from "../pie_chart/pie_chart"; +import { items } from "./dashboard_items"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; - static components = { Layout, DashboardItem, PieChart }; + static components = { Layout, DashboardItem }; setup(){ this.action = useService("action"); this.statistics = useState(useService("awesome_dashboard.statistics")); + this.items = items; this.display = { controlPanel: {}, }; diff --git a/awesome_dashboard/static/src/dashboard/dashboard.xml b/awesome_dashboard/static/src/dashboard/dashboard.xml index 85ebe7eb4cd..0f711ab83c2 100644 --- a/awesome_dashboard/static/src/dashboard/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard/dashboard.xml @@ -8,40 +8,12 @@
- - Average amount of t-shirt by order this month -
- -
-
- - Average time for an order to go from 'new' to 'sent' or 'cancelled' -
- -
-
- - Number of new orders this month -
- -
-
- - Number of cancelled orders this month -
- -
-
- - Total amount of new orders this month -
- -
-
- - Shirt orders by size - - + + + + + +
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_items.js b/awesome_dashboard/static/src/dashboard/dashboard_items.js new file mode 100644 index 00000000000..7b85d181b15 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard_items.js @@ -0,0 +1,60 @@ +import { StatCard } from "../stat_card/stat_card"; +import { PieChartCard } from "../pie_chart_card/pie_chart_card"; + +export const items = [ + { + id: "average_quantity", + description: "Average amount of t-shirt", + Component: StatCard, + props: (data) => ({ + title: "Average amount of t-shirt by order this month", + value: data.average_quantity, + }) + }, + { + id: "average_time", + description: "Average time for an order", + Component: StatCard, + props: (data) => ({ + title: "Average time for an order to go from 'new' to 'sent' or 'cancelled'", + value: data.average_time, + }) + }, + { + id: "number_new_orders", + description: "New orders this month", + Component: StatCard, + props: (data) => ({ + title: "Number of new orders this month", + value: data.nb_new_orders, + }) + }, + { + id: "cancelled_orders", + description: "Cancelled orders this month", + Component: StatCard, + props: (data) => ({ + title: "Number of cancelled orders this month", + value: data.nb_cancelled_orders, + }) + }, + { + id: "amount_new_orders", + description: "amount orders this month", + Component: StatCard, + props: (data) => ({ + title: "Total amount of new orders this month", + value: data.total_amount, + }) + }, + { + id: "pie_chart", + description: "Shirt orders by size", + Component: PieChartCard, + size: 2, + props: (data) => ({ + title: "Shirt orders by size", + values: data.orders_by_size, + }) + } +] \ No newline at end of file diff --git a/awesome_dashboard/static/src/pie_chart/pie_chart.js b/awesome_dashboard/static/src/pie_chart/pie_chart.js index b7349b520c2..5dac6affc10 100644 --- a/awesome_dashboard/static/src/pie_chart/pie_chart.js +++ b/awesome_dashboard/static/src/pie_chart/pie_chart.js @@ -20,7 +20,9 @@ export class PieChart extends Component { this.renderChart(nextProps); }) onWillUnmount(() => { - this.chart.destroy(); + if (this.chart) { + this.chart.destroy(); + } }); }; diff --git a/awesome_dashboard/static/src/pie_chart_card/pie_chart_card.js b/awesome_dashboard/static/src/pie_chart_card/pie_chart_card.js new file mode 100644 index 00000000000..02ec94c99ed --- /dev/null +++ b/awesome_dashboard/static/src/pie_chart_card/pie_chart_card.js @@ -0,0 +1,11 @@ +import { Component } from "@odoo/owl"; +import { PieChart } from "../pie_chart/pie_chart"; + +export class PieChartCard extends Component { + static template = "awesome_dashboard.PieChartCard"; + static components = { PieChart }; + static props = { + title: {type: String}, + values: {type: Object}, + }; +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/pie_chart_card/pie_chart_card.xml b/awesome_dashboard/static/src/pie_chart_card/pie_chart_card.xml new file mode 100644 index 00000000000..b0700333e6d --- /dev/null +++ b/awesome_dashboard/static/src/pie_chart_card/pie_chart_card.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/awesome_dashboard/static/src/stat_card/stat_card.js b/awesome_dashboard/static/src/stat_card/stat_card.js new file mode 100644 index 00000000000..7ceb674cda6 --- /dev/null +++ b/awesome_dashboard/static/src/stat_card/stat_card.js @@ -0,0 +1,9 @@ +import { Component } from "@odoo/owl"; + +export class StatCard extends Component { + static template = "awesome_dashboard.StatCard"; + static props = { + title: {type: String}, + value: {type: Number}, + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/stat_card/stat_card.xml b/awesome_dashboard/static/src/stat_card/stat_card.xml new file mode 100644 index 00000000000..b0a4456bdf8 --- /dev/null +++ b/awesome_dashboard/static/src/stat_card/stat_card.xml @@ -0,0 +1,9 @@ + + + + +
+ +
+
+
\ No newline at end of file From 8f2fe668090f24130a258a8f0ab3270fad34d5ef Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 25 Jun 2025 11:38:54 +0200 Subject: [PATCH 39/42] [IMP] awesome_dashboard: Extensible Dashboard --- awesome_dashboard/static/src/dashboard/dashboard.js | 3 +-- .../static/src/dashboard/dashboard_items.js | 9 +++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/awesome_dashboard/static/src/dashboard/dashboard.js b/awesome_dashboard/static/src/dashboard/dashboard.js index 46a5ae7a145..0268399988b 100644 --- a/awesome_dashboard/static/src/dashboard/dashboard.js +++ b/awesome_dashboard/static/src/dashboard/dashboard.js @@ -5,7 +5,6 @@ import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; import { useService } from "@web/core/utils/hooks"; import { DashboardItem } from "./dashboard_item"; -import { items } from "./dashboard_items"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; @@ -14,7 +13,7 @@ class AwesomeDashboard extends Component { setup(){ this.action = useService("action"); this.statistics = useState(useService("awesome_dashboard.statistics")); - this.items = items; + this.items = registry.category("item").getAll(); this.display = { controlPanel: {}, }; diff --git a/awesome_dashboard/static/src/dashboard/dashboard_items.js b/awesome_dashboard/static/src/dashboard/dashboard_items.js index 7b85d181b15..c0e2f3fed3d 100644 --- a/awesome_dashboard/static/src/dashboard/dashboard_items.js +++ b/awesome_dashboard/static/src/dashboard/dashboard_items.js @@ -1,7 +1,8 @@ import { StatCard } from "../stat_card/stat_card"; import { PieChartCard } from "../pie_chart_card/pie_chart_card"; +import { registry } from "@web/core/registry"; -export const items = [ +const items = [ { id: "average_quantity", description: "Average amount of t-shirt", @@ -57,4 +58,8 @@ export const items = [ values: data.orders_by_size, }) } -] \ No newline at end of file +] + +items.forEach((item) => { + registry.category("item").add(item.id, item); +}); \ No newline at end of file From 35aa75a0428909f6e135b61d16aec72f6213a9ca Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Wed, 25 Jun 2025 13:55:59 +0200 Subject: [PATCH 40/42] [IMP] awesome_dashboard: Add a dialog to configure the items --- .../static/src/dashboard/dashboard.js | 47 +++++++++++++++++++ .../static/src/dashboard/dashboard.xml | 22 ++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/awesome_dashboard/static/src/dashboard/dashboard.js b/awesome_dashboard/static/src/dashboard/dashboard.js index 0268399988b..55d221b39b7 100644 --- a/awesome_dashboard/static/src/dashboard/dashboard.js +++ b/awesome_dashboard/static/src/dashboard/dashboard.js @@ -5,6 +5,9 @@ import { registry } from "@web/core/registry"; import { Layout } from "@web/search/layout"; import { useService } from "@web/core/utils/hooks"; import { DashboardItem } from "./dashboard_item"; +import { Dialog } from "@web/core/dialog/dialog"; +import { CheckBox } from "@web/core/checkbox/checkbox"; +import { browser } from "@web/core/browser/browser"; class AwesomeDashboard extends Component { static template = "awesome_dashboard.AwesomeDashboard"; @@ -14,11 +17,26 @@ class AwesomeDashboard extends Component { this.action = useService("action"); this.statistics = useState(useService("awesome_dashboard.statistics")); this.items = registry.category("item").getAll(); + this.dialog = useService("dialog"); this.display = { controlPanel: {}, }; + this.state = useState({ + disabledItems: browser.localStorage.getItem("disabledDashboardItems")?.split(",") || [], + }); + } + + openConfiguration(newDisabledItems) { + this.dialog.add(ConfigurationDialog, { + items: this.items, + disabledItems: this.state.disabledItems, + onUpdateConfiguration: this.updateConfiguration.bind(this), + }) } + updateConfiguration(newDisabledItems) { + this.state.disabledItems = newDisabledItems; + } openCustomerView() { this.action.doAction("base.action_partner_form"); } @@ -36,4 +54,33 @@ class AwesomeDashboard extends Component { } } +class ConfigurationDialog extends Component { + static template = "awesome_dashboard.ConfigurationDialog"; + static components = { Dialog, CheckBox }; + static props = ["close", "items", "disabledItems", "onUpdateConfiguration"]; + + setup(){ + this.items = useState(this.props.items.map((item) => { + return { + ...item, + enabled: !this.props.disabledItems.includes(item.id), + }; + })); + } + + done(){ + this.props.close(); + } + + onChange(checked, changedItem){ + changedItem.enabled = checked; + const newDisabledItems = Object.values(this.items).filter( + (item) => !item.enabled).map((item) => item.id); + + browser.localStorage.setItem("disabledDashboardItems", newDisabledItems); + + this.props.onUpdateConfiguration(newDisabledItems); + } +} + registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard/dashboard.xml b/awesome_dashboard/static/src/dashboard/dashboard.xml index 0f711ab83c2..7230aed69f3 100644 --- a/awesome_dashboard/static/src/dashboard/dashboard.xml +++ b/awesome_dashboard/static/src/dashboard/dashboard.xml @@ -7,9 +7,14 @@ + + +
- + @@ -17,5 +22,20 @@
+ + + Which cards do you whish to see ? + + + + + + + + + + From cf8ca378cf656db3735f14e4c615de42e49a278d Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Thu, 26 Jun 2025 10:23:33 +0200 Subject: [PATCH 41/42] [IMP] estate: Add master and demo data --- estate/__manifest__.py | 2 ++ estate/data/master_data.xml | 14 ++++++++++++++ estate/demo/estate.property.csv | 4 ++++ 3 files changed, 20 insertions(+) create mode 100644 estate/data/master_data.xml create mode 100644 estate/demo/estate.property.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index b02cdfea387..c7a0b39c869 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,6 +3,7 @@ 'version': '18.0.1.0.0', 'depends': ['base'], 'data': [ + 'data/master_data.xml', 'security/ir.model.access.csv', 'views/estate_property_views.xml', 'views/estate_property_offer_views.xml', @@ -11,6 +12,7 @@ 'views/res_users_views.xml', 'views/estate_menus.xml', ], + 'demo': ['demo/estate.property.csv'], 'author': "baje", 'category': 'Uncategorized', 'description': """ diff --git a/estate/data/master_data.xml b/estate/data/master_data.xml new file mode 100644 index 00000000000..6ba45ad3a09 --- /dev/null +++ b/estate/data/master_data.xml @@ -0,0 +1,14 @@ + + + Residential + + + Commercial + + + Industrial + + + Land + + diff --git a/estate/demo/estate.property.csv b/estate/demo/estate.property.csv new file mode 100644 index 00000000000..60e25bd6af5 --- /dev/null +++ b/estate/demo/estate.property.csv @@ -0,0 +1,4 @@ +id,name,description,postcode,date_availability,expected_price,selling_price,bedrooms,living_area,facades,garage,garden,garden_area,garden_orientation,active,state +100,Housing Number 100,"A house. Here's a random fraction: 0,815281954423783",165,2025-07-11,2660000,2660000,2,294,1,True,True,45,west,True,offer_received +101,Housing Number 101,"A house. Here's a random fraction: 0,722300355198869",340,2025-06-06,2410000,2410000,2,472,1,True,True,70,south,True,canceled +102,Housing Number 102,"A house. Here's a random fraction: 0,676182648737866",480,2025-11-09,730000,730000,1,143,0,True,True,80,east,True,canceled From 1595efc7fc830cf76227553b2834716014e7e1d4 Mon Sep 17 00:00:00 2001 From: baje-odoo Date: Thu, 26 Jun 2025 11:50:34 +0200 Subject: [PATCH 42/42] [IMP] estate: Add demo offers --- estate/__manifest__.py | 2 +- estate/demo/estate_offer_demo.xml | 33 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 estate/demo/estate_offer_demo.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index c7a0b39c869..ced5495c62b 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -12,7 +12,7 @@ 'views/res_users_views.xml', 'views/estate_menus.xml', ], - 'demo': ['demo/estate.property.csv'], + 'demo': ['demo/estate.property.csv', 'demo/estate_offer_demo.xml'], 'author': "baje", 'category': 'Uncategorized', 'description': """ diff --git a/estate/demo/estate_offer_demo.xml b/estate/demo/estate_offer_demo.xml new file mode 100644 index 00000000000..a07c01674c5 --- /dev/null +++ b/estate/demo/estate_offer_demo.xml @@ -0,0 +1,33 @@ + + + + Residential + + + + Big Villa + 300000 + + + + + + + 10000 + 14 + + + + + + 1500000 + 14 + + + + + + 1500001 + 14 + +