From a6ca23655a2346c95b3e021733544a3c7d15a8bf Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Wed, 31 Dec 2025 15:06:54 +0530 Subject: [PATCH 01/17] [ADD] estate: created the model and Initalized it with the postgre db - The Estate Module, which is needed as of tutorials - Required fields were added, including the selection - CHAPTER 1,2,3 --- estate/__init__.py | 1 + estate/__manifest__.py | 10 ++++++++++ estate/models/__init__.py | 1 + estate/models/estate_property.py | 23 +++++++++++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 00000000000..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..4694b175a7a --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,10 @@ +{ + 'name': "Estate", + 'version': '1.0', + 'depends': ['base'], + 'author': "japat", + 'category': 'Category', + 'description': """ + Hello , Real Estate for everyone + """, +} \ 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..ee202ae4c50 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,23 @@ +from odoo import models, fields + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Estate Property" + + name = fields.Char(required=True) + description = fields.Text() + postcode = fields.Char() + date_availability = fields.Date() + expected_price = fields.Float(required=True) + selling_price = fields.Float() + bedrooms = fields.Integer() + living_area = fields.Integer() + facades = fields.Integer() + garage = fields.Boolean() + garden = fields.Boolean() + garden_area = fields.Integer() + garden_orientation = fields.Selection( + string='Direction', + selection=[('east','East'),('west','West'),('north','North'),('south','South')], + ) + From 26ff004eec974c91fc9fcc27bba64e3493f2990e Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Thu, 1 Jan 2026 18:15:00 +0530 Subject: [PATCH 02/17] [IMP] estate: done ui major search, list, form -Added the list and form view for proper readability with the group feature -Added the search functionality, i.e, available and Postcode -Added the security read, write, and create rules to solve the warning message --- estate/__manifest__.py | 8 ++- estate/models/__init__.py | 2 +- estate/models/estate_property.py | 15 +++-- estate/security/ir.model.access.csv | 2 + estate/views/estate_menus.xml | 10 +++ estate/views/estate_property_views.xml | 84 ++++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 estate/security/ir.model.access.csv create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 4694b175a7a..561ccd2066e 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,4 +7,10 @@ 'description': """ Hello , Real Estate for everyone """, -} \ No newline at end of file + "data": [ + "security/ir.model.access.csv", + "views/estate_property_views.xml", + "views/estate_menus.xml", + ], + "application": True, +} 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 ee202ae4c50..c47e17ad450 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import models, fields +from datetime import date, timedelta class EstateProperty(models.Model): _name = "estate.property" @@ -7,17 +8,19 @@ class EstateProperty(models.Model): name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date() + date_availability = fields.Date(copy=False,default=lambda self: fields.Date.today() + timedelta(days=90)) expected_price = fields.Float(required=True) - selling_price = fields.Float() - bedrooms = fields.Integer() + selling_price = fields.Float(readonly=True,copy=False) + bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( - string='Direction', - selection=[('east','East'),('west','West'),('north','North'),('south','South')], + string='Direction', + selection=[('east', 'East'), ('west', 'West'), ('north', 'North'), ('south', 'South')], ) - + active = fields.Boolean(default=True) + state = fields.Selection(string="state", selection=[('new','New'),('offer_received','Offer Received'),('offer_accepted','Offer Accepted'),('sold_and_cancelled','Sold and Cancelled')]) + diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..98f4671fb0d --- /dev/null +++ b/estate/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..6e0175e25cb --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..09d5ede7d9e --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,84 @@ + + + estate property list + estate.property + + + + + + + + + + + + + + + estate property form + estate.property + +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate property search + estate.property + + + + + + + + + + + + + + + + + + Properties + estate.property + list,form + +
From dbcf3f0d388403d9d3706e47f7e0c86302b7c043 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Thu, 1 Jan 2026 18:35:21 +0530 Subject: [PATCH 03/17] [IMP] estate: improvement in the code structure -improved the structure of the estate property -CHAPTER 4,5,6 --- estate/__manifest__.py | 13 ++--- estate/models/estate_property.py | 37 ++++++++++---- estate/views/estate_menus.xml | 14 +++--- estate/views/estate_property_views.xml | 67 +++++++++++++------------- 4 files changed, 75 insertions(+), 56 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 561ccd2066e..12e1a93c0bd 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -1,10 +1,10 @@ { - 'name': "Estate", - 'version': '1.0', - 'depends': ['base'], - 'author': "japat", - 'category': 'Category', - 'description': """ + "name": "Estate", + "version": "1.0", + "depends": ["base"], + "author": "japat", + "category": "Category", + "description": """ Hello , Real Estate for everyone """, "data": [ @@ -12,5 +12,6 @@ "views/estate_property_views.xml", "views/estate_menus.xml", ], + "license": "LGPL-3", "application": True, } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index c47e17ad450..8c0e72eeab2 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,16 +1,19 @@ from odoo import models, fields -from datetime import date, timedelta +from datetime import timedelta -class EstateProperty(models.Model): + +class Property(models.Model): _name = "estate.property" - _description = "Estate Property" + _description = "estate property details" name = fields.Char(required=True) description = fields.Text() postcode = fields.Char() - date_availability = fields.Date(copy=False,default=lambda self: fields.Date.today() + timedelta(days=90)) + date_availability = fields.Date( + copy=False, default=lambda self: fields.Date.today() + timedelta(days=90) + ) expected_price = fields.Float(required=True) - selling_price = fields.Float(readonly=True,copy=False) + selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) living_area = fields.Integer() facades = fields.Integer() @@ -18,9 +21,23 @@ class EstateProperty(models.Model): garden = fields.Boolean() garden_area = fields.Integer() garden_orientation = fields.Selection( - string='Direction', - selection=[('east', 'East'), ('west', 'West'), ('north', 'North'), ('south', 'South')], + selection=[ + ("north", "North"), + ("west", "West"), + ("east", "East"), + ("south", "South"), + ] + ) + active = fields.Boolean(default=False) + state = fields.Selection( + selection=[ + ("new", "New"), + ("offer_received", "Offer Received"), + ("offer_accepted", "Offer Accepted"), + ("sold", "Sold"), + ("cancelled", "Cancelled"), + ], + default="new", + copy=False, + required=True, ) - active = fields.Boolean(default=True) - state = fields.Selection(string="state", selection=[('new','New'),('offer_received','Offer Received'),('offer_accepted','Offer Accepted'),('sold_and_cancelled','Sold and Cancelled')]) - diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml index 6e0175e25cb..0f3d4669271 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,10 +1,10 @@ - + - + - - + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 09d5ede7d9e..5c6d240aa5b 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -4,13 +4,13 @@ estate.property - - - - - - - + + + + + + + @@ -21,34 +21,34 @@
-

- -

+

+ +

- - + + - - + + - - - - - - - - - - + + + + + + + + + + @@ -62,15 +62,16 @@ estate.property - - - - - - - + + + + + + + - + @@ -81,4 +82,4 @@ estate.property list,form - + \ No newline at end of file From 833d4f5e32427af5786e38e9cac4239461b14bb7 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Fri, 2 Jan 2026 17:21:11 +0530 Subject: [PATCH 04/17] [IMP] estate: add property offer model and relations - Created the 'estate.property.offer' model with fields - Add a One2many relationship 'offer_ids' to the 'estate.property' model. - Implement list and form views for the offer model. - Update the property form view to display offers in a dedicated tab. --- estate/__manifest__.py | 3 ++ estate/models/__init__.py | 3 ++ estate/models/estate_property.py | 9 +++++- estate/models/estate_property_offer.py | 13 ++++++++ estate/models/estate_property_tag.py | 8 +++++ estate/models/estate_property_type.py | 8 +++++ estate/security/ir.model.access.csv | 3 ++ estate/views/estate_menus.xml | 10 +++++- estate/views/estate_property_offer.xml | 35 +++++++++++++++++++++ estate/views/estate_property_tag_views.xml | 8 +++++ estate/views/estate_property_type_views.xml | 8 +++++ estate/views/estate_property_views.xml | 18 ++++++++++- 12 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 estate/models/estate_property_offer.py create mode 100644 estate/models/estate_property_tag.py create mode 100644 estate/models/estate_property_type.py create mode 100644 estate/views/estate_property_offer.xml create mode 100644 estate/views/estate_property_tag_views.xml create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 12e1a93c0bd..10ce071e7ca 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,7 +10,10 @@ "data": [ "security/ir.model.access.csv", "views/estate_property_views.xml", + "views/estate_property_type_views.xml", "views/estate_menus.xml", + "views/estate_property_tag_views.xml", + "views/estate_property_offer.xml", ], "license": "LGPL-3", "application": True, diff --git a/estate/models/__init__.py b/estate/models/__init__.py index 5e1963c9d2f..2f1821a39c1 100644 --- a/estate/models/__init__.py +++ b/estate/models/__init__.py @@ -1 +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 8c0e72eeab2..b2652796429 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import fields, models from datetime import timedelta @@ -41,3 +41,10 @@ class Property(models.Model): copy=False, required=True, ) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") + salesperson_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="Property Tags") + offer_ids = fields.One2many("estate.property.offer", "property_id") diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..f4c6bd34f1a --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,13 @@ +from odoo import fields, models + + +class EstatePropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Estate Property Offer" + + price = fields.Float() + status = fields.Selection( + selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False + ) + partner_id = fields.Many2one("res.partner", required=True) + property_id = fields.Many2one("estate.property", required=True) diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..fd8e9562fda --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstatePropertyTags(models.Model): + _name = "estate.property.tag" + _description = "Estate Property Tags" + + name = fields.Char(required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..7860a26125e --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import fields, models + + +class EstatePropertyType(models.Model): + _name = "estate.property.type" + _description = "Estate Property Type" + + name = fields.Char(required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 98f4671fb0d..0db13e578ec 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 +estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1 +estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 +estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,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 0f3d4669271..202fe5a0e35 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -7,4 +7,12 @@ name="Properties" parent="estate_first_level_menu" action="estate_property_action" /> - \ No newline at end of file + + + + + + + diff --git a/estate/views/estate_property_offer.xml b/estate/views/estate_property_offer.xml new file mode 100644 index 00000000000..b4a57081ed2 --- /dev/null +++ b/estate/views/estate_property_offer.xml @@ -0,0 +1,35 @@ + + + Property Offers + estate.property.offer + list,form + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + + estate.property.offer.form + estate.property.offer + + + + + + + + + + + + + diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..988d7db57c9 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,8 @@ + + + Properties Tags + estate.property.tag + list,form + + + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..9e359000df1 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,8 @@ + + + Properties Type + estate.property.type + list,form + + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 5c6d240aa5b..88c81c20520 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -26,7 +26,12 @@ + + + + + @@ -51,6 +56,17 @@ + + + + + + + + + + +
@@ -82,4 +98,4 @@ estate.property list,form - \ No newline at end of file + From 7c49cc740f41ff3bab7a0fb2ad67d9f7cb919038 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Mon, 5 Jan 2026 18:50:47 +0530 Subject: [PATCH 05/17] [IMP] estate: added computed fields - Implemented computed fields for 'total_area' and 'best_price' to automatically aggregate property data and identify the highest offer. - Added 'validity' and 'date_deadline' to property offers with inverse logic, allowing users to define deadlines either by a date or a duration in days. --- estate/models/estate_property.py | 24 +++++++++++++++++++++--- estate/models/estate_property_offer.py | 21 ++++++++++++++++++++- estate/views/estate_property_offer.xml | 4 ++++ estate/views/estate_property_views.xml | 5 ++++- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index b2652796429..5166684a7b8 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import api, fields, models from datetime import timedelta @@ -15,11 +15,11 @@ class Property(models.Model): expected_price = fields.Float(required=True) selling_price = fields.Float(readonly=True, copy=False) bedrooms = fields.Integer(default=2) - living_area = fields.Integer() + living_area = fields.Integer(string="Living Area(sqm)") facades = fields.Integer() garage = fields.Boolean() garden = fields.Boolean() - garden_area = fields.Integer() + garden_area = fields.Integer(string="Garden Area(sqm)") garden_orientation = fields.Selection( selection=[ ("north", "North"), @@ -48,3 +48,21 @@ class Property(models.Model): buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) tag_ids = fields.Many2many("estate.property.tag", string="Property Tags") offer_ids = fields.One2many("estate.property.offer", "property_id") + total_area = fields.Float(string="Total Area(sqm)", compute="_compute_total_area") + best_price = fields.Float( + string="Best Offer", compute="_compute_best_offer", readonly=True, copy=False + ) + + @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") + def _compute_best_offer(self): + for record in self: + prices = record.offer_ids.mapped("price") + if prices: + record.best_price = max(prices) + else: + record.best_price = 0.0 diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index f4c6bd34f1a..40a11101559 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 api, fields, models +from datetime import timedelta class EstatePropertyOffer(models.Model): @@ -11,3 +12,21 @@ class EstatePropertyOffer(models.Model): ) partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True) + validity = fields.Integer(default=7, string="Validity(Days)") + date_deadline = fields.Date( + string="Deadline", + compute="_compute_date_deadline", + inverse="_inverse_date_deadline", + ) + + @api.depends("validity", "date_deadline") + def _compute_date_deadline(self): + for record in self: + creation_date = fields.Date.today() or record.create_date() + record.date_deadline = timedelta(days=record.validity) + creation_date + + def _inverse_date_deadline(self): + for record in self: + creation_date = fields.Date.today() or record.create_date() + date_diff = record.date_deadline - creation_date + record.validity = date_diff.days diff --git a/estate/views/estate_property_offer.xml b/estate/views/estate_property_offer.xml index b4a57081ed2..cb40b51da1f 100644 --- a/estate/views/estate_property_offer.xml +++ b/estate/views/estate_property_offer.xml @@ -13,6 +13,8 @@ + + @@ -27,6 +29,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 88c81c20520..74062a80e86 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -26,7 +26,8 @@ - + @@ -38,6 +39,7 @@ + @@ -54,6 +56,7 @@ + From 37413c2e7b3b8335b95d0faafa9aebc478404b5d Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Tue, 6 Jan 2026 11:24:26 +0530 Subject: [PATCH 06/17] [IMP] estate: add onchange and fix parseError - Add an onchange method for the garden field - Fix ParseError by ensuring actions are defined before menuitems - chapter 8 --- estate/__manifest__.py | 2 +- estate/models/estate_property.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 10ce071e7ca..a6d5a7e4319 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -11,8 +11,8 @@ "security/ir.model.access.csv", "views/estate_property_views.xml", "views/estate_property_type_views.xml", - "views/estate_menus.xml", "views/estate_property_tag_views.xml", + "views/estate_menus.xml", "views/estate_property_offer.xml", ], "license": "LGPL-3", diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5166684a7b8..175ba29fb79 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -66,3 +66,12 @@ def _compute_best_offer(self): record.best_price = max(prices) else: record.best_price = 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 From 364860cdaa449c6afecfb86fee0f719ac5b67582 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Wed, 7 Jan 2026 19:03:34 +0530 Subject: [PATCH 07/17] [IMP] estate: added property and offer state management - Added 'Sold' and 'Cancel' buttons to estate.property. - Added 'Accept' and 'Refuse' buttons to estate.property.offer. - Implement logic to prevent selling canceled properties and vice-versa. - Update property state, selling price, and buyer when an offer is accepted. --- estate/models/estate_property.py | 29 ++++++++++++++++++--- estate/models/estate_property_offer.py | 35 +++++++++++++++++++++++--- estate/security/ir.model.access.csv | 2 +- estate/views/estate_property_offer.xml | 9 +++++-- estate/views/estate_property_views.xml | 9 ++++--- 5 files changed, 70 insertions(+), 14 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 175ba29fb79..b6c54e38b55 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,8 @@ -from odoo import api, fields, models from datetime import timedelta +from odoo import api, fields, models +from odoo.exceptions import UserError + class Property(models.Model): _name = "estate.property" @@ -28,7 +30,7 @@ class Property(models.Model): ("south", "South"), ] ) - active = fields.Boolean(default=False) + active = fields.Boolean(default=True) state = fields.Selection( selection=[ ("new", "New"), @@ -41,11 +43,17 @@ class Property(models.Model): copy=False, required=True, ) + status = fields.Selection( + selection=[("new", "New"), ("sold", "Sold"), ("cancelled", "Cancelled")], + default="new", + copy=False, + ) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") salesperson_id = fields.Many2one( "res.users", string="Salesman", default=lambda self: self.env.user ) - buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False) + buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False, readonly=True) tag_ids = fields.Many2many("estate.property.tag", string="Property Tags") offer_ids = fields.One2many("estate.property.offer", "property_id") total_area = fields.Float(string="Total Area(sqm)", compute="_compute_total_area") @@ -75,3 +83,18 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = False + + def sold_button_action(self): + for record in self: + if record.status == "cancelled": + raise UserError("Cancelled Property cannot be sold.") + else: + record.state = "sold" + record.status = "sold" + + def cancelled_button_action(self): + for record in self: + if record.status == "sold": + raise UserError("Sold Property cannot be Cancelled.") + record.state = "cancelled" + record.status = "cancelled" diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 40a11101559..3a47781baec 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,6 +1,8 @@ -from odoo import api, fields, models from datetime import timedelta +from odoo import api, fields, models +from odoo.exceptions import UserError + class EstatePropertyOffer(models.Model): _name = "estate.property.offer" @@ -8,7 +10,8 @@ class EstatePropertyOffer(models.Model): price = fields.Float() status = fields.Selection( - selection=[("accepted", "Accepted"), ("refused", "Refused")], copy=False + selection=[("accepted", "Accepted"), ("refused", "Refused")], + copy=False, ) partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True) @@ -22,11 +25,35 @@ class EstatePropertyOffer(models.Model): @api.depends("validity", "date_deadline") def _compute_date_deadline(self): for record in self: - creation_date = fields.Date.today() or record.create_date() + if record.create_date: + creation_date = record.create_date.date() + else: + creation_date = fields.Date.today() record.date_deadline = timedelta(days=record.validity) + creation_date def _inverse_date_deadline(self): for record in self: - creation_date = fields.Date.today() or record.create_date() + if record.create_date: + creation_date = record.create_date.date() + else: + creation_date = fields.Date.today() date_diff = record.date_deadline - creation_date record.validity = date_diff.days + + def refuse_offer_action_icon(self): + for record in self: + if record.status == "accepted": + raise UserError("You cant refuse an already accepted offer") + record.status = "refused" + return True + + def accept_offer_action_icon(self): + for record in self: + if "accepted" in record.property_id.offer_ids.mapped("status"): + raise UserError("An Offer has already been accepted for this offer") + + record.status = "accepted" + record.property_id.selling_price = record.price + record.property_id.state = "offer_accepted" + record.property_id.buyer_id = record.partner_id + return True diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 0db13e578ec..0c0b62b7fee 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 estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1 estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1 estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1 -estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1 \ No newline at end of file +estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/views/estate_property_offer.xml b/estate/views/estate_property_offer.xml index cb40b51da1f..927464e9640 100644 --- a/estate/views/estate_property_offer.xml +++ b/estate/views/estate_property_offer.xml @@ -12,9 +12,15 @@ - + + +

@@ -20,7 +29,7 @@ - +
@@ -40,4 +49,4 @@
- + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index d4d9e765b78..a39d2fe2722 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -96,7 +96,7 @@ - + @@ -113,4 +113,4 @@ list,form {'search_default_available': 1} - + \ No newline at end of file From 68148e279b7fa0d7d999bcb2f89d9088c8a96758 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Fri, 16 Jan 2026 10:46:32 +0530 Subject: [PATCH 11/17] [FIX] estate: add missing imports for UserError and translation Add missing imports for 'UserError' from 'odoo.exceptions' and the translation function '_' from 'odoo'. --- estate/models/estate_property.py | 2 +- estate/models/estate_property_offer.py | 2 +- estate/views/estate_property_offer.xml | 2 +- estate/views/estate_property_type_views.xml | 2 +- estate/views/estate_property_views.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index a7f62cb6401..1a5f959927c 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,6 +1,6 @@ from datetime import timedelta -from odoo import api, fields, models +from odoo import api, fields, models, _ from odoo.exceptions import UserError, ValidationError from odoo.tools.float_utils import float_compare, float_is_zero diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index c5d2b18fd8f..b48cf9732fc 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,6 +1,6 @@ from datetime import timedelta -from odoo import api, fields, models +from odoo import api, fields, models, _ from odoo.exceptions import UserError diff --git a/estate/views/estate_property_offer.xml b/estate/views/estate_property_offer.xml index ce7f485487b..92470b6c7fa 100644 --- a/estate/views/estate_property_offer.xml +++ b/estate/views/estate_property_offer.xml @@ -48,4 +48,4 @@ list [('property_type_id', '=', active_id)] - \ No newline at end of file + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml index eac2e961372..a172adc38d8 100644 --- a/estate/views/estate_property_type_views.xml +++ b/estate/views/estate_property_type_views.xml @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index a39d2fe2722..712fee59857 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -113,4 +113,4 @@ list,form {'search_default_available': 1} - \ No newline at end of file + From 7e4b9e3e0c1fb8068788e0b280e53a855e4a2db9 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Mon, 19 Jan 2026 12:28:20 +0530 Subject: [PATCH 12/17] [IMP] estate: implement business logic and extend res.users - Add business logic to prevent the deletion of a property unless its state is 'New' or 'Cancelled' using the @ondelete decorator. - Update the property state to 'Offer Received' upon offer creation. - Add validation to ensure new offers are not created with an amount lower than existing offers for the same property. - Inherit the 'res.users' model to add a 'property_ids' One2many field. - Extend the 'res.users' form view to display the 'property_ids' field within a new notebook page. - CHP 12 --- estate/__manifest__.py | 1 + estate/models/__init__.py | 1 + estate/models/estate_property.py | 22 ++++++++++++++-------- estate/models/estate_property_offer.py | 9 +++++++++ estate/models/res_users.py | 7 +++++++ estate/views/res_users_views.xml | 25 +++++++++++++++++++++++++ 6 files changed, 57 insertions(+), 8 deletions(-) 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 a6d5a7e4319..2a288b55c1e 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -14,6 +14,7 @@ "views/estate_property_tag_views.xml", "views/estate_menus.xml", "views/estate_property_offer.xml", + "views/res_users_views.xml", ], "license": "LGPL-3", "application": True, 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 1a5f959927c..e8e7e184208 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -107,14 +107,20 @@ def _check_price(self): ) def sold_button_action(self): - for record in self: - if self.filtered(lambda x: x.state == "cancelled"): - raise UserError(_("Cancelled Property cannot be sold.")) - else: - record.state = "sold" + if self.filtered(lambda x: x.state == "cancelled"): + raise UserError(_("Cancelled Property cannot be sold.")) + else: + self.state = "sold" def cancelled_button_action(self): + if self.filtered(lambda x: x.state == "sold"): + raise UserError(_("Sold Property cannot be Cancelled.")) + self.state = "cancelled" + + @api.ondelete(at_uninstall=False) + def _check_state_before_deletion(self): for record in self: - if record.state == "sold": - raise UserError(_("Sold Property cannot be Cancelled.")) - record.state = "cancelled" + if record.state not in ["new", "cancelled"]: + raise UserError( + _("You can only delete properties that are 'New' or 'Cancelled'.") + ) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index b48cf9732fc..e06bbae8e1b 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -72,3 +72,12 @@ def action_accept_offer(self): record.property_id.state = "offer_accepted" record.property_id.buyer_id = record.partner_id return True + + @api.model + def create(self, vals): + for val in vals: + linked_property = self.env["estate.property"].browse(val["property_id"]) + if val["price"] < linked_property.best_price: + raise UserError("An offer with higher price already exists") + linked_property.state = "offer_received" + return super().create(vals) diff --git a/estate/models/res_users.py b/estate/models/res_users.py new file mode 100644 index 00000000000..ef2f1b083d1 --- /dev/null +++ b/estate/models/res_users.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class ResUsers(models.Model): + _inherit = "res.users" + + property_ids = fields.One2many("estate.property", "salesperson_id") diff --git a/estate/views/res_users_views.xml b/estate/views/res_users_views.xml new file mode 100644 index 00000000000..c157cad5283 --- /dev/null +++ b/estate/views/res_users_views.xml @@ -0,0 +1,25 @@ + + + res.users.view.form.inherit.property + res.users + + + + + + + + + + + + + + + + + + + + + From 8215c2cad16af1732c67032b7adc35e15146008e Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Wed, 21 Jan 2026 15:08:06 +0530 Subject: [PATCH 13/17] [ADD] estate_account: create invoice when property is sold - Create 'estate_account' link module depending on 'estate' and 'account'. - Inherit 'estate.property' and override 'action_sold' to trigger invoice creation. - Create a customer invoice (account.move) for the property's buyer. - Add two invoice lines to the created move: - A 6% commission based on the property selling price. - A flat 100.00 administrative fee. - Add rainbow_man effect when the state is set to won - CHP 13 --- estate/models/estate_property.py | 7 ++++++ estate/views/estate_property_views.xml | 2 ++ estate_account/__init__.py | 1 + estate_account/__manifest__.py | 16 ++++++++++++ estate_account/models/__init__.py | 1 + estate_account/models/estate_property.py | 31 ++++++++++++++++++++++++ 6 files changed, 58 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/models/estate_property.py b/estate/models/estate_property.py index e8e7e184208..3f6b33fcdfe 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -111,6 +111,13 @@ def sold_button_action(self): raise UserError(_("Cancelled Property cannot be sold.")) else: self.state = "sold" + return { + "effect": { + "fadeout": "no", + "message": "helloo", + "type": "rainbow_man", + } + } def cancelled_button_action(self): if self.filtered(lambda x: x.state == "sold"): diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 712fee59857..613a37b139c 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -35,6 +35,8 @@ statusbar_visible="new,offer_received,offer_accepted,sold" /> +

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..ee2385553e6 --- /dev/null +++ b/estate_account/__manifest__.py @@ -0,0 +1,16 @@ +{ + "name": "Estate Account", + "version": "1.0", + "author": "ODOO", + "category": "Category", + "description": """ + Real Estate Account Section + """, + "depends": [ + "estate", + "account", + ], + "license": "LGPL-3", + "application": False, + "installable": True, +} 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..8c9a754c28f --- /dev/null +++ b/estate_account/models/estate_property.py @@ -0,0 +1,31 @@ +from odoo import Command, models + + +class EstateProperty(models.Model): + _inherit = "estate.property" + + def sold_button_action(self): + self.env["account.move"].create( + { + "partner_id": self.buyer_id.id, + "move_type": "out_invoice", + "line_ids": [ + Command.create( + { + "name": "selling price addons (6%)", + "quantity": 1, + "price_unit": self.selling_price * 0.06, + } + ), + Command.create( + { + "name": "Administrative fees", + "quantity": 1, + "price_unit": 100, + } + ), + ], + } + ) + + return super().sold_button_action() From 58fd0093b6878ea7010cfd30ad5d478d9936a7c3 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Sat, 24 Jan 2026 09:32:45 +0530 Subject: [PATCH 14/17] [IMP] estate: add kanban view for properties - Implement a basic Kanban view grouped by property type by default. - Use QWeb conditional logic to display the 'Best Price' only when an offer has been received and the 'Selling Price' only when accepted. - Include property tags with color support for better categorization. - Fixed the offer accept and reject buttons logic --- estate/models/estate_property.py | 33 +++++++++---------- estate/models/estate_property_offer.py | 11 ++----- estate/views/estate_property_offer.xml | 6 ++-- estate/views/estate_property_views.xml | 40 +++++++++++++++++++++--- estate_account/models/estate_property.py | 4 +-- 5 files changed, 59 insertions(+), 35 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 3f6b33fcdfe..63c65eeb7bc 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -58,7 +58,7 @@ class Property(models.Model): string="Best Offer", compute="_compute_best_offer", readonly=True, copy=False ) _check_expected_price = models.Constraint( - "CHECK(expected_price >= 0)", + "CHECK(expected_price > 0)", "The expected price should be strictly positive", ) _check_selling_price = models.Constraint( @@ -91,22 +91,19 @@ def _onchange_garden(self): @api.constrains("selling_price", "expected_price") def _check_price(self): - for record in self: - if float_is_zero(record.selling_price, precision_rounding=0.001): - continue - if ( - float_compare( - record.selling_price, - record.expected_price * 0.9, - precision_rounding=0.01, - ) - == -1 - ): - raise ValidationError( - "The selling price must be at least 90% of the expected price" - ) + if not float_is_zero(self.selling_price, precision_rounding=0.001) and ( + float_compare( + self.selling_price, + self.expected_price * 0.9, + precision_rounding=0.01, + ) + == -1 + ): + raise ValidationError( + "The selling price must be at least 90% of the expected price" + ) - def sold_button_action(self): + def action_sold_button(self): if self.filtered(lambda x: x.state == "cancelled"): raise UserError(_("Cancelled Property cannot be sold.")) else: @@ -119,13 +116,13 @@ def sold_button_action(self): } } - def cancelled_button_action(self): + def action_cancel_button(self): if self.filtered(lambda x: x.state == "sold"): raise UserError(_("Sold Property cannot be Cancelled.")) self.state = "cancelled" @api.ondelete(at_uninstall=False) - def _check_state_before_deletion(self): + def _unlink_check_state_before_deletion(self): for record in self: if record.state not in ["new", "cancelled"]: raise UserError( diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index e06bbae8e1b..f86f2bd236a 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -22,7 +22,6 @@ class EstatePropertyOffer(models.Model): compute="_compute_date_deadline", inverse="_inverse_date_deadline", ) - disable_buttons = fields.Boolean(compute="_disable_offer_buttons", default=False) property_type_id = fields.Many2one( related="property_id.property_type_id", string="Property Type", store=True ) @@ -39,13 +38,6 @@ def _compute_date_deadline(self): creation_date = fields.Date.today() record.date_deadline = timedelta(days=record.validity) + creation_date - @api.depends("property_id.offer_ids.status") - def _disable_offer_buttons(self): - for record in self: - record.disable_buttons = "accepted" in record.property_id.offer_ids.mapped( - "status" - ) - def _inverse_date_deadline(self): for record in self: if record.create_date: @@ -71,6 +63,9 @@ def action_accept_offer(self): record.property_id.selling_price = record.price record.property_id.state = "offer_accepted" record.property_id.buyer_id = record.partner_id + records = record.property_id.offer_ids.filtered(lambda x: x.id != record.id) + records.write({"status": "refused"}) + return True @api.model diff --git a/estate/views/estate_property_offer.xml b/estate/views/estate_property_offer.xml index 92470b6c7fa..d9c616205dc 100644 --- a/estate/views/estate_property_offer.xml +++ b/estate/views/estate_property_offer.xml @@ -17,10 +17,10 @@ + + + + \ No newline at end of file diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..5b5b34beccf 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,18 @@ -import { Component } from "@odoo/owl"; +import { markup, Component, 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 }; + + setup() { + this.state = useState({ sum: 2 }); + } + + incrementSum() { + this.state.sum++; + } +} \ No newline at end of file diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..92240e07629 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -1,10 +1,21 @@ - + -
- hello world -
-
+
+
+ Hello World + + - +
Total Sum:
+
+
+ + +
+
+ + + +
\ No newline at end of file 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..4876dbdcbc7 --- /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 } + } + }; +} \ No newline at end of file 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..05574703aab --- /dev/null +++ b/awesome_owl/static/src/todo_list/todo_item.xml @@ -0,0 +1,9 @@ + + + +
+ . + +
+
+
\ No newline at end of file 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..4715326aa38 --- /dev/null +++ b/awesome_owl/static/src/todo_list/todo_list.js @@ -0,0 +1,23 @@ +import { Component, useState } from "@odoo/owl"; +import { TodoItem } from "./todo_item"; + +export class TodoList extends Component { + static template = "awesome_owl.TodoList"; + static components = { TodoItem }; + + setup() { + this.nextId = 0; + this.todos = useState([]); + } + + addTodo(ev) { + if (ev.keyCode === 13 && ev.target.value != "") { + this.todos.push({ + id: this.nextId++, + description: ev.target.value, + isCompleted: false + }); + ev.target.value = ""; + } + } +} \ No newline at end of file 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..091e9b9424f --- /dev/null +++ b/awesome_owl/static/src/todo_list/todo_list.xml @@ -0,0 +1,11 @@ + + + +
+ + + + +
+
+
\ No newline at end of file From 05e5fbd7a4a80f49304d98dcac7a35af92c0b0e4 Mon Sep 17 00:00:00 2001 From: japat-odoo Date: Tue, 3 Feb 2026 18:56:47 +0530 Subject: [PATCH 17/17] [IMP] awesome_owl: add autofocus to todo input using useRef - Implement autofocus on TodoList input using useRef and onMounted. - Created useAutofocus utility hook for reusability. - Addded t-ref to the input field in the TodoList template. - CHP1 , SUB-CHP- 11 --- awesome_owl/static/src/todo_list/todo_list.js | 2 ++ awesome_owl/static/src/todo_list/todo_list.xml | 4 ++-- awesome_owl/static/src/utils.js | 8 ++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) 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 4715326aa38..43304308467 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"; @@ -8,6 +9,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 091e9b9424f..af6b4370baf 100644 --- a/awesome_owl/static/src/todo_list/todo_list.xml +++ b/awesome_owl/static/src/todo_list/todo_list.xml @@ -2,10 +2,10 @@
- +
-
\ No newline at end of file + diff --git a/awesome_owl/static/src/utils.js b/awesome_owl/static/src/utils.js new file mode 100644 index 00000000000..6a5475b357e --- /dev/null +++ b/awesome_owl/static/src/utils.js @@ -0,0 +1,8 @@ +import { useRef, onMounted } from "@odoo/owl"; + +export function useAutofocus(refName) { + const ref = useRef(refName); + onMounted(() => { + ref.el.focus(); + }); +} \ No newline at end of file