From 2be8a14b3f9e261a329591f626a6e3b9ddc46aad Mon Sep 17 00:00:00 2001 From: ahham Date: Mon, 16 Feb 2026 10:39:25 +0100 Subject: [PATCH 01/10] [ADD] estate: add estate property module declaration --- estate/__init__.py | 0 estate/__manifest__.py | 15 ++++++++++++++ estate/static/description/icon.png | Bin 0 -> 5636 bytes estate/static/description/icon.svg | 32 +++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py create mode 100644 estate/static/description/icon.png create mode 100644 estate/static/description/icon.svg diff --git a/estate/__init__.py b/estate/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/estate/__manifest__.py b/estate/__manifest__.py new file mode 100644 index 00000000000..7599bbc4303 --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,15 @@ +{ + 'name': 'Estate', + 'version': '0.0', + 'summary': 'Real Estate Management', + 'depends': [ + 'base', + ], + 'data': [ + + ], + + 'installable': True, + 'application': True, + 'author': 'ahham', +} \ No newline at end of file diff --git a/estate/static/description/icon.png b/estate/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0ceddd7d3e975b14e55005dc26e7fd96563b1591 GIT binary patch literal 5636 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 eRwvwwP5dtfk@|8;*@LJ60000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4d165da137fbf58011e5b0495c4dbe53e3fb14ae Mon Sep 17 00:00:00 2001 From: ahham Date: Mon, 16 Feb 2026 11:03:02 +0100 Subject: [PATCH 02/10] [IMP] estate: add estate_property model [LINT] estate: add linting and formatting [IMP] estate: add estate_property model --- estate/__init__.py | 1 + estate/__manifest__.py | 4 +--- estate/models/__init__.py | 1 + estate/models/estate_property.py | 24 ++++++++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py diff --git a/estate/__init__.py b/estate/__init__.py index e69de29bb2d..0650744f6bc 100644 --- a/estate/__init__.py +++ b/estate/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 7599bbc4303..d404849c581 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,10 +6,8 @@ 'base', ], 'data': [ - ], - 'installable': True, 'application': True, 'author': 'ahham', -} \ No newline at end of file +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..5e1963c9d2f --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1 @@ +from . import estate_property diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..05a9f44133d --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,24 @@ +from odoo import models, fields + +GARDEN_ORIENTATION = [("north", "North"), ("south", "South"), + ("east", "East"), ("west", "West")] + + +class EstateProperty(models.Model): + _name = "estate.property" + _description = "Real Estate Property" + + name = fields.Char(string="Name", required=True) + description = fields.Text(string="Description") + postcode = fields.Char(string="Postcode") + date_availability = fields.Date(string="Date Availability") + expected_price = fields.Float(string="Expected Price", required=True) + selling_price = fields.Float(string="Selling Price") + bedrooms = fields.Integer(string="Bedrooms") + living_area = fields.Integer(string="Living Area") + facades = fields.Integer(string="Facades") + garage = fields.Boolean(string="Garage") + garden = fields.Boolean(string="Garden") + garden_area = fields.Integer(string="Garden Area") + garden_orientation = fields.Selection(string="Garden Orientation", + selection=GARDEN_ORIENTATION) From 426e4bc86fca947fa0f4a7815871360bfec0e382 Mon Sep 17 00:00:00 2001 From: ahham Date: Mon, 16 Feb 2026 13:06:37 +0100 Subject: [PATCH 03/10] [IMP] estate: add access control to estate_property model --- estate/__manifest__.py | 1 + estate/security/ir.model.access.csv | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 estate/security/ir.model.access.csv diff --git a/estate/__manifest__.py b/estate/__manifest__.py index d404849c581..3bf0d259c00 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -6,6 +6,7 @@ 'base', ], 'data': [ + 'security/ir.model.access.csv', ], 'installable': True, 'application': True, 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 f0f5d2659beb1f3d76d12b029f0872991e7a723b Mon Sep 17 00:00:00 2001 From: ahham Date: Mon, 16 Feb 2026 14:41:30 +0100 Subject: [PATCH 04/10] [IMP] estate: add menus and action for estate_property --- estate/__manifest__.py | 2 ++ estate/models/estate_property.py | 13 ++++++++----- estate/views/estate_menus.xml | 8 ++++++++ estate/views/estate_property_views.xml | 9 +++++++++ 4 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 estate/views/estate_menus.xml create mode 100644 estate/views/estate_property_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 3bf0d259c00..3d93a19349a 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -7,6 +7,8 @@ ], 'data': [ 'security/ir.model.access.csv', + 'views/estate_property_views.xml', + 'views/estate_menus.xml' ], 'installable': True, 'application': True, diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 05a9f44133d..228d5239f70 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,7 +1,8 @@ from odoo import models, fields +from odoo.tools.date_utils import relativedelta -GARDEN_ORIENTATION = [("north", "North"), ("south", "South"), - ("east", "East"), ("west", "West")] +GARDEN_ORIENTATION = [("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")] +PROPERTY_STATE = [("new", "New"), ("offer_received", "Offer Received"), ("offer_accepted", "Offer Accepted"), ("sold", "Sold"), ("cancelled", "Cancelled")] class EstateProperty(models.Model): @@ -11,10 +12,10 @@ class EstateProperty(models.Model): name = fields.Char(string="Name", required=True) description = fields.Text(string="Description") postcode = fields.Char(string="Postcode") - date_availability = fields.Date(string="Date Availability") + date_availability = fields.Date(string="Date Availability", copy=False, default=lambda self: fields.Date.context_today(self) + relativedelta(months=3)) expected_price = fields.Float(string="Expected Price", required=True) - selling_price = fields.Float(string="Selling Price") - bedrooms = fields.Integer(string="Bedrooms") + selling_price = fields.Float(string="Selling Price", readonly=True, copy=False) + bedrooms = fields.Integer(string="Bedrooms", default=2) living_area = fields.Integer(string="Living Area") facades = fields.Integer(string="Facades") garage = fields.Boolean(string="Garage") @@ -22,3 +23,5 @@ class EstateProperty(models.Model): garden_area = fields.Integer(string="Garden Area") garden_orientation = fields.Selection(string="Garden Orientation", selection=GARDEN_ORIENTATION) + active = fields.Boolean(string="Active", default=True) + state = fields.Selection(string="Status", selection=PROPERTY_STATE, required=True, copy=False, default="new") diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..9b158c63e02 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..e01bce85a95 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,9 @@ + + + + + Properties + estate.property + list,form + + \ No newline at end of file From f37f97789245db5f59a707152cec39996c13cf29 Mon Sep 17 00:00:00 2001 From: ahham Date: Mon, 16 Feb 2026 16:09:52 +0100 Subject: [PATCH 05/10] [IMP] estate: add list,form and search custom views for estate_property --- estate/__manifest__.py | 3 +- estate/models/estate_property.py | 6 +-- estate/views/estate_property_views.xml | 73 ++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index 3d93a19349a..b523f8485ca 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -10,7 +10,6 @@ 'views/estate_property_views.xml', 'views/estate_menus.xml' ], - 'installable': True, 'application': True, - 'author': 'ahham', + 'author': 'Odoo S.A.', } diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 228d5239f70..afc9ead8fee 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -9,18 +9,18 @@ class EstateProperty(models.Model): _name = "estate.property" _description = "Real Estate Property" - name = fields.Char(string="Name", required=True) + name = fields.Char(string="Title", required=True) description = fields.Text(string="Description") postcode = fields.Char(string="Postcode") date_availability = fields.Date(string="Date Availability", copy=False, default=lambda self: fields.Date.context_today(self) + relativedelta(months=3)) expected_price = fields.Float(string="Expected Price", required=True) selling_price = fields.Float(string="Selling Price", readonly=True, copy=False) bedrooms = fields.Integer(string="Bedrooms", default=2) - living_area = fields.Integer(string="Living Area") + living_area = fields.Integer(string="Living Area (sqm)") facades = fields.Integer(string="Facades") garage = fields.Boolean(string="Garage") garden = fields.Boolean(string="Garden") - garden_area = fields.Integer(string="Garden Area") + garden_area = fields.Integer(string="Garden Area (sqm)") garden_orientation = fields.Selection(string="Garden Orientation", selection=GARDEN_ORIENTATION) active = fields.Boolean(string="Active", default=True) diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index e01bce85a95..0239cf84ede 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -6,4 +6,77 @@ estate.property list,form + + estate.property.list + estate.property + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + estate.property.search + estate.property + + + + + + + + + + + + + + + \ No newline at end of file From 332eef48fa596204d38d2692efbc620960cf370a Mon Sep 17 00:00:00 2001 From: ahham Date: Tue, 17 Feb 2026 10:27:39 +0100 Subject: [PATCH 06/10] [IMP] estate: add property type,tag and offers and defined their relationship with estate_property --- estate/__manifest__.py | 5 ++++- estate/models/__init__.py | 3 +++ estate/models/estate_property.py | 6 ++++++ estate/models/estate_property_offer.py | 12 ++++++++++++ estate/models/estate_property_tag.py | 8 ++++++++ estate/models/estate_property_type.py | 8 ++++++++ estate/security/ir.model.access.csv | 5 ++++- estate/views/estate_menus.xml | 6 +++++- estate/views/estate_property_offer_views.xml | 15 +++++++++++++++ estate/views/estate_property_tag_views.xml | 9 +++++++++ estate/views/estate_property_type_views.xml | 9 +++++++++ estate/views/estate_property_views.xml | 15 +++++++++++++-- 12 files changed, 96 insertions(+), 5 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_views.xml create mode 100644 estate/views/estate_property_tag_views.xml create mode 100644 estate/views/estate_property_type_views.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index b523f8485ca..4c2f83995c7 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -8,7 +8,10 @@ 'data': [ 'security/ir.model.access.csv', 'views/estate_property_views.xml', - 'views/estate_menus.xml' + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', + 'views/estate_menus.xml', + 'views/estate_property_offer_views.xml', ], 'application': True, 'author': 'Odoo S.A.', 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 afc9ead8fee..6c57848ca79 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -25,3 +25,9 @@ class EstateProperty(models.Model): selection=GARDEN_ORIENTATION) active = fields.Boolean(string="Active", default=True) state = fields.Selection(string="Status", selection=PROPERTY_STATE, required=True, copy=False, default="new") + + property_type_id = fields.Many2one("estate.property.type", string="Property Type") + buyer_id = fields.Many2one("res.partner", string="Buyer") + salesman_id = fields.Many2one("res.users", string="Salesman", default=lambda self: self.env.user) + property_tag_ids = fields.Many2many("estate.property.tag") + 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..60955fed0dc --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,12 @@ +from odoo import models, fields + +PROPERTY_OFFER_STATE = [("accepted", "Accepted"), ("refused", "Refused")] +class PropertyOffer(models.Model): + _name = "estate.property.offer" + _description = "Real Estate Property Offer" + + price = fields.Float() + status = fields.Selection(selection=PROPERTY_OFFER_STATE, 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..786a4a9462e --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class PropertyTag(models.Model): + _name = "estate.property.tag" + _description = "Real Estate Property Tag" + + name = fields.Char(string="Name", required=True) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..61acc863f27 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class PropertyType(models.Model): + _name = "estate.property.type" + _description = "Real Estate Property Type" + + name = fields.Char(string="Property Type", required=True) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv index 976b61e8cb3..4c593ed42e4 100644 --- a/estate/security/ir.model.access.csv +++ b/estate/security/ir.model.access.csv @@ -1,2 +1,5 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink -access_estate_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 +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_menus.xml b/estate/views/estate_menus.xml index 9b158c63e02..b3476fd443e 100644 --- a/estate/views/estate_menus.xml +++ b/estate/views/estate_menus.xml @@ -1,8 +1,12 @@ - + + + + + \ 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..dd7c59dffeb --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,15 @@ + + + + estate.property.offer.list + estate.property.offer + + + + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..5242ced6a1e --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,9 @@ + + + + Property Tags + estate.property.tag + list,form + + + \ 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..9e1842b15a7 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,9 @@ + + + + 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 0239cf84ede..c0271ef4864 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -33,8 +33,10 @@ + + @@ -56,13 +58,22 @@ + + + + + + + + + - estate.property.search + estate.property.view.search estate.property @@ -73,7 +84,7 @@ - + From 02cf3dcf88e80cc5099ad7ed8d84ba152da56617 Mon Sep 17 00:00:00 2001 From: ahham Date: Tue, 17 Feb 2026 11:16:17 +0100 Subject: [PATCH 07/10] [IMP] estate: add computed fields and set onchange method --- estate/models/estate_property.py | 24 +++++++++++++++++++- estate/models/estate_property_offer.py | 18 ++++++++++++++- estate/views/estate_property_offer_views.xml | 2 ++ estate/views/estate_property_views.xml | 2 ++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 6c57848ca79..9dc4d57a90e 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api from odoo.tools.date_utils import relativedelta GARDEN_ORIENTATION = [("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")] @@ -31,3 +31,25 @@ class EstateProperty(models.Model): salesman_id = fields.Many2one("res.users", string="Salesman", default=lambda self: self.env.user) property_tag_ids = fields.Many2many("estate.property.tag") offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers") + + total_area = fields.Integer(string="Total Area (sqm)", compute="_compute_total_area") + best_offer = fields.Float(string="Best Offer", compute="_compute_best_offer") + + @api.depends("living_area", "garden_area") + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + @api.depends("offer_ids.price") + def _compute_best_offer(self): + for record in self: + record.best_offer = max(record.offer_ids.mapped("price"), default=0) + + @api.onchange("garden") + def _onchange_garden(self): + if self.garden: + self.garden_area = 10 + self.garden_orientation = "north" + else: + self.garden_area = 0 + self.garden_orientation = False diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 60955fed0dc..03ce5fe2051 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -1,6 +1,9 @@ -from odoo import models, fields +from odoo import models, fields, api +from odoo.tools.date_utils import relativedelta PROPERTY_OFFER_STATE = [("accepted", "Accepted"), ("refused", "Refused")] + + class PropertyOffer(models.Model): _name = "estate.property.offer" _description = "Real Estate Property Offer" @@ -10,3 +13,16 @@ class PropertyOffer(models.Model): partner_id = fields.Many2one("res.partner", required=True) property_id = fields.Many2one("estate.property", required=True) + + validity = fields.Integer(string="Validity (days)", default=7) + date_deadline = fields.Date(compute="_compute_date_deadline", inverse="_inverse_date_deadline") + + @api.depends("validity") + def _compute_date_deadline(self): + for record in self: + if record.create_date: + record.date_deadline = record.create_date + relativedelta(days=record.validity) + def _inverse_date_deadline(self): + for record in self: + if record.create_date and record.date_deadline: + record.validity = (record.date_deadline - record.create_date.date()).days diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index dd7c59dffeb..1e9705c3a67 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -8,6 +8,8 @@ + + diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index c0271ef4864..034e68e836b 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -42,6 +42,7 @@ + @@ -56,6 +57,7 @@ + From 3245306607bbc5eab15109d098ef6ad70ba02ac1 Mon Sep 17 00:00:00 2001 From: ahham Date: Tue, 17 Feb 2026 13:25:39 +0100 Subject: [PATCH 08/10] [IMP] estate: add actions for property offers and property state --- estate/models/estate_property.py | 16 ++++++++++++++++ estate/models/estate_property_offer.py | 14 ++++++++++++++ estate/views/estate_property_offer_views.xml | 2 ++ estate/views/estate_property_views.xml | 5 +++++ 4 files changed, 37 insertions(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 9dc4d57a90e..385fa970064 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -1,4 +1,5 @@ from odoo import models, fields, api +from odoo.exceptions import UserError from odoo.tools.date_utils import relativedelta GARDEN_ORIENTATION = [("north", "North"), ("south", "South"), ("east", "East"), ("west", "West")] @@ -53,3 +54,18 @@ def _onchange_garden(self): else: self.garden_area = 0 self.garden_orientation = False + + def action_cancel_property(self): + for record in self: + if record.state == "sold": + raise UserError("Sold properties cannot be cancelled") + else: + record.state = "cancelled" + return True + def action_sold_property(self): + for record in self: + if record.state == "cancelled": + raise UserError("Cancelled properties cannot be sold") + else: + record.state = "sold" + return True diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py index 03ce5fe2051..366beb49e1e 100644 --- a/estate/models/estate_property_offer.py +++ b/estate/models/estate_property_offer.py @@ -26,3 +26,17 @@ def _inverse_date_deadline(self): for record in self: if record.create_date and record.date_deadline: record.validity = (record.date_deadline - record.create_date.date()).days + + def action_accept_offer(self): + for record in self: + record.status = "accepted" + record.property_id.buyer_id = record.partner_id + record.property_id.selling_price = record.price + for offer in record.property_id.offer_ids: + if offer != record: + offer.status = "refused" + return True + def action_refuse_offer(self): + for record in self: + record.status = "refused" + return True diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml index 1e9705c3a67..ab719002b0f 100644 --- a/estate/views/estate_property_offer_views.xml +++ b/estate/views/estate_property_offer_views.xml @@ -10,6 +10,8 @@ + + +
+

+ +

+ +
+ + + + + + + + + + + + + +
+ + + estate.property.type.list + estate.property.type + + + + + + + \ No newline at end of file diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index 26034e4029d..46675b1a6aa 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -5,19 +5,20 @@ Properties estate.property list,form + {'search_default_available': True, 'search_default_current': True} estate.property.list estate.property - + - + @@ -29,19 +30,20 @@

- +
- - + @@ -60,13 +62,13 @@ - - + + - + @@ -88,7 +90,7 @@ - +