diff --git a/awesome_clicker/__init__.py b/awesome_clicker/__init__.py index 40a96afc6ff..e69de29bb2d 100644 --- a/awesome_clicker/__init__.py +++ b/awesome_clicker/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/awesome_clicker/__manifest__.py b/awesome_clicker/__manifest__.py index 56dc2f779b9..8b8e88a9c34 100644 --- a/awesome_clicker/__manifest__.py +++ b/awesome_clicker/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { 'name': "Awesome Clicker", diff --git a/awesome_dashboard/__init__.py b/awesome_dashboard/__init__.py index b0f26a9a602..979534583fb 100644 --- a/awesome_dashboard/__init__.py +++ b/awesome_dashboard/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - -from . import controllers +from . import controllers as controllers diff --git a/awesome_dashboard/__manifest__.py b/awesome_dashboard/__manifest__.py index a1cd72893d7..2cb770167c2 100644 --- a/awesome_dashboard/__manifest__.py +++ b/awesome_dashboard/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { 'name': "Awesome Dashboard", diff --git a/awesome_dashboard/controllers/__init__.py b/awesome_dashboard/controllers/__init__.py index 457bae27e11..979534583fb 100644 --- a/awesome_dashboard/controllers/__init__.py +++ b/awesome_dashboard/controllers/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - -from . import controllers \ No newline at end of file +from . import controllers as controllers diff --git a/awesome_dashboard/controllers/controllers.py b/awesome_dashboard/controllers/controllers.py index 56d4a051287..66bc1614ccd 100644 --- a/awesome_dashboard/controllers/controllers.py +++ b/awesome_dashboard/controllers/controllers.py @@ -1,15 +1,13 @@ -# -*- coding: utf-8 -*- - import logging import random from odoo import http -from odoo.http import request logger = logging.getLogger(__name__) + class AwesomeDashboard(http.Controller): - @http.route('/awesome_dashboard/statistics', type='json', auth='user') + @http.route("/awesome_dashboard/statistics", type="json", auth="user") def get_statistics(self): """ Returns a dict of statistics about the orders: @@ -22,15 +20,14 @@ def get_statistics(self): """ return { - 'average_quantity': random.randint(4, 12), - 'average_time': random.randint(4, 123), - 'nb_cancelled_orders': random.randint(0, 50), - 'nb_new_orders': random.randint(10, 200), - 'orders_by_size': { - 'm': random.randint(0, 150), - 's': random.randint(0, 150), - 'xl': random.randint(0, 150), + "average_quantity": random.randint(4, 12), + "average_time": random.randint(4, 123), + "nb_cancelled_orders": random.randint(0, 50), + "nb_new_orders": random.randint(10, 200), + "orders_by_size": { + "m": random.randint(0, 150), + "s": random.randint(0, 150), + "xl": random.randint(0, 150), }, - 'total_amount': random.randint(100, 1000) + "total_amount": random.randint(100, 1000), } - diff --git a/awesome_gallery/__init__.py b/awesome_gallery/__init__.py index a0fdc10fe11..253f5724e28 100644 --- a/awesome_gallery/__init__.py +++ b/awesome_gallery/__init__.py @@ -1,2 +1 @@ -# -*- coding: utf-8 -*- -from . import models +from . import models as models diff --git a/awesome_gallery/__manifest__.py b/awesome_gallery/__manifest__.py index f0fe026a9c6..7f1ac00e141 100644 --- a/awesome_gallery/__manifest__.py +++ b/awesome_gallery/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { 'name': "Gallery View", 'summary': """ diff --git a/awesome_gallery/models/__init__.py b/awesome_gallery/models/__init__.py index 7f0930ee744..d4ac8a945a7 100644 --- a/awesome_gallery/models/__init__.py +++ b/awesome_gallery/models/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # import filename_python_file_within_folder_or_subfolder -from . import ir_action -from . import ir_ui_view +from . import ir_action as ir_action +from . import ir_ui_view as ir_ui_view diff --git a/awesome_gallery/models/ir_action.py b/awesome_gallery/models/ir_action.py index eae20acbf5c..aa9cd3da816 100644 --- a/awesome_gallery/models/ir_action.py +++ b/awesome_gallery/models/ir_action.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from odoo import fields, models diff --git a/awesome_gallery/models/ir_ui_view.py b/awesome_gallery/models/ir_ui_view.py index 0c11b8298ac..72d3816cb90 100644 --- a/awesome_gallery/models/ir_ui_view.py +++ b/awesome_gallery/models/ir_ui_view.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- from odoo import fields, models class View(models.Model): - _inherit = 'ir.ui.view' + _inherit = "ir.ui.view" - type = fields.Selection(selection_add=[('gallery', "Awesome Gallery")]) + type = fields.Selection(selection_add=[("gallery", "Awesome Gallery")]) diff --git a/awesome_kanban/__init__.py b/awesome_kanban/__init__.py index 40a96afc6ff..e69de29bb2d 100644 --- a/awesome_kanban/__init__.py +++ b/awesome_kanban/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/awesome_kanban/__manifest__.py b/awesome_kanban/__manifest__.py index 6f31bc8de0d..d8a39336912 100644 --- a/awesome_kanban/__manifest__.py +++ b/awesome_kanban/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- { 'name': "Awesome Kanban", 'summary': """ diff --git a/awesome_owl/__init__.py b/awesome_owl/__init__.py index 457bae27e11..979534583fb 100644 --- a/awesome_owl/__init__.py +++ b/awesome_owl/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - -from . import controllers \ No newline at end of file +from . import controllers as controllers diff --git a/awesome_owl/__manifest__.py b/awesome_owl/__manifest__.py index e8ac1cda552..bcb46376986 100644 --- a/awesome_owl/__manifest__.py +++ b/awesome_owl/__manifest__.py @@ -1,41 +1,36 @@ -# -*- coding: utf-8 -*- { - 'name': "Awesome Owl", - - 'summary': """ + "name": "Awesome Owl", + "summary": """ Starting module for "Discover the JS framework, chapter 1: Owl components" """, - - 'description': """ + "description": """ Starting module for "Discover the JS framework, chapter 1: Owl components" """, - - 'author': "Odoo", - 'website': "https://www.odoo.com", - + "author": "Odoo", + "website": "https://www.odoo.com", # Categories can be used to filter modules in modules listing # Check https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/data/ir_module_category_data.xml # for the full list - 'category': 'Tutorials', - 'version': '0.1', - + "category": "Tutorials", + "version": "0.1", # any module necessary for this one to work correctly - 'depends': ['base', 'web'], - 'application': True, - 'installable': True, - 'data': [ - 'views/templates.xml', + "depends": ["base", "web"], + "application": True, + "installable": True, + "data": [ + "views/templates.xml", ], - 'assets': { - 'awesome_owl.assets_playground': [ - ('include', 'web._assets_helpers'), - 'web/static/src/scss/pre_variables.scss', - 'web/static/lib/bootstrap/scss/_variables.scss', - ('include', 'web._assets_bootstrap'), - ('include', 'web._assets_core'), - 'web/static/src/libs/fontawesome/css/font-awesome.css', - 'awesome_owl/static/src/**/*', + "assets": { + "awesome_owl.assets_playground": [ + ("include", "web._assets_helpers"), + "web/static/src/scss/pre_variables.scss", + "web/static/lib/bootstrap/scss/_variables.scss", + "web/static/lib/bootstrap/scss/_maps.scss", + ("include", "web._assets_bootstrap"), + ("include", "web._assets_core"), + "web/static/src/libs/fontawesome/css/font-awesome.css", + "awesome_owl/static/src/**/*", ], }, - 'license': 'AGPL-3' + "license": "AGPL-3", } diff --git a/awesome_owl/controllers/__init__.py b/awesome_owl/controllers/__init__.py index 457bae27e11..979534583fb 100644 --- a/awesome_owl/controllers/__init__.py +++ b/awesome_owl/controllers/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - -from . import controllers \ No newline at end of file +from . import controllers as controllers diff --git a/awesome_owl/controllers/controllers.py b/awesome_owl/controllers/controllers.py index bccfd6fe283..0d0dd8f3160 100644 --- a/awesome_owl/controllers/controllers.py +++ b/awesome_owl/controllers/controllers.py @@ -1,5 +1,5 @@ from odoo import http -from odoo.http import request, route +from odoo.http import request class OwlPlayground(http.Controller): @http.route(['/awesome_owl'], type='http', auth='public') diff --git a/awesome_owl/static/src/main.js b/awesome_owl/static/src/main.js index 1aaea902b55..63fda6c4ec1 100644 --- a/awesome_owl/static/src/main.js +++ b/awesome_owl/static/src/main.js @@ -8,5 +8,10 @@ const config = { }; // Mount the Playground component when the document.body is ready -whenReady(() => mountComponent(Playground, document.body, config)); +whenReady(() => { + const target = document.getElementById('app'); + if (target) { + mountComponent(Playground, target, config); + } +}); diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..541da25a298 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,13 @@ -import { Component } from "@odoo/owl"; +import { useState, Component } from "@odoo/owl"; export class Playground extends Component { - static template = "awesome_owl.playground"; + static template = "my_module.Counter"; + + setup() { + this.state = useState({ value: 0 }); + } + + increment() { + this.state.value++; + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..8abba65d11d 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -1,9 +1,10 @@ - +
- hello world +

Counter:

+
diff --git a/awesome_owl/views/templates.xml b/awesome_owl/views/templates.xml index aa54c1a7241..2dfb0428129 100644 --- a/awesome_owl/views/templates.xml +++ b/awesome_owl/views/templates.xml @@ -8,6 +8,7 @@ +
diff --git a/estate/.vscode/launch.json b/estate/.vscode/launch.json new file mode 100644 index 00000000000..320a2837a4e --- /dev/null +++ b/estate/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "debug current file", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/odoo/odoo-bin", + "args": [ + "--addons-path=addons,../enterprise/,../tutorials/", + "-d", + "rd-demo", + "-u", + "estate", + "--dev", + "all", + "--http-interface=127.0.0.1" + ], + "console": "integratedTerminal", + "justMyCode": true + } + ] +} 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..26aa806496a --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,18 @@ +{ + "name": "Estate", + "version": "1.0", + "author": "Joyep", + "license": "LGPL-3", + "depends": ["base"], + "data": [ + "data/ir.model.access.csv", + "views/estate_property_views.xml", + "views/estate_property_type_views.xml", + "views/estate_property_tag_views.xml", + "views/estate_menus.xml", + "views/inherited_users_views.xml", + ], + "installable": True, + "application": True, + "auto_install": False, +} diff --git a/estate/data/ir.model.access.csv b/estate/data/ir.model.access.csv new file mode 100644 index 00000000000..0104d1eadf3 --- /dev/null +++ b/estate/data/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink +access_estate_property_user,Estate Property User,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_type_user,Estate Property Type User,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag_user,Estate Property Tag User,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer_user,Estate Property Offer User,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..8d1b0ed8937 --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1,7 @@ +from . import ( + estate_property, + estate_property_offer, + estate_property_tag, + estate_property_type, + res_users, +) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..98f64363660 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,127 @@ +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class EstateProperty(models.Model): + # ---------------------------------------- + # Private attributes + # ---------------------------------------- + _name = "estate.property" + _description = "Estate Property" + _order = "id desc" + + # ---------------------------------------- + # Field declarations + # ---------------------------------------- + name = fields.Char("Title", required=True) + sequence = fields.Integer("Sequence", default=10) + description = fields.Text("Description") + postcode = fields.Char("Postcode") + date_availability = fields.Date( + "Available From", + default=lambda self: fields.Date.today() + relativedelta(months=3), + copy=False, + ) + expected_price = fields.Float("Expected Price", required=True) + selling_price = fields.Float("Selling Price", copy=False, readonly=True) + bedrooms = fields.Integer("Bedrooms", default=2) + living_area = fields.Integer("Living Area (sqm)") + facades = fields.Integer("Number of Facades") + garage = fields.Boolean("Garage") + garden = fields.Boolean("Garden") + garden_area = fields.Integer("Garden Area (sqm)") + garden_orientation = fields.Selection( + selection=[ + ("north", "North"), + ("south", "South"), + ("east", "East"), + ("west", "West"), + ], + string="Garden Orientation", + ) + active = fields.Boolean("Active", default=True) + status = fields.Selection( + selection=[ + ("new", "New"), + ("offer_received", "Offer Received"), + ("offer_accepted", "Offer Accepted"), + ("sold", "Sold"), + ("canceled", "Canceled"), + ], + string="Status", + default="new", + required=True, + copy=False, + ) + salesperson_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user) + partner_id = fields.Many2one("res.partner", string="Partner", copy=False) + property_type_id = fields.Many2one("estate.property.type", string="Property Type") + 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", store=True) + best_offer = fields.Float("Best Offer", compute="_compute_best_offer", store=True) + + # ---------------------------------------- + # SQL constraints + # ---------------------------------------- + _price_positive = [ + ("check_expected_price", "CHECK(expected_price > 0)", "The expected price must be positive."), + ("check_selling_price", "CHECK(selling_price >= 0)", "The selling price cannot be negative."), + ] + + # ---------------------------------------- + # Compute methods + # ---------------------------------------- + @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: + prices = record.offer_ids.mapped("price") + record.best_offer = max(prices) if prices else 0.0 + + # ---------------------------------------- + # Onchange methods + # ---------------------------------------- + @api.onchange("garden") + def _onchange_garden(self): + if not self.garden: + self.garden_area = 0 + self.garden_orientation = False + else: + if not self.garden_area: + self.garden_area = 10 + if not self.garden_orientation: + self.garden_orientation = "north" + + # ---------------------------------------- + # CRUD methods + # ---------------------------------------- + @api.ondelete(at_uninstall=False) + def _unlink_if_new_or_canceled(self): + for record in self: + if record.status not in ("new", "canceled"): + msg = "You can only delete properties with status 'New' or 'Canceled'." + raise UserError(msg) + + # ---------------------------------------- + # Action methods + # ---------------------------------------- + def action_set_sold(self): + for record in self: + if record.status == "canceled": + msg = "A cancelled property cannot be set as sold." + raise UserError(msg) + record.status = "sold" + + def action_set_canceled(self): + for record in self: + if record.status == "sold": + msg = "A sold property cannot be cancelled." + raise UserError(msg) + record.status = "canceled" diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..68d0e9dfc27 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,107 @@ +from datetime import timedelta + +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class EstatePropertyOffer(models.Model): + # ---------------------------------------- + # Private attributes + # ---------------------------------------- + _name = "estate.property.offer" + _description = "Estate Property Offer" + _order = "price desc" + + # ---------------------------------------- + # Field declarations + # ---------------------------------------- + price = fields.Float("Price") + status = fields.Selection( + selection=[ + ("accepted", "Accepted"), + ("refused", "Refused"), + ], + string="Status", + copy=False, + ) + 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)", default=7) + date_deadline = fields.Date("Deadline", compute="_compute_date_deadline", inverse="_inverse_date_deadline") + property_type_id = fields.Many2one( + "estate.property.type", + related="property_id.property_type_id", + string="Property Type", + store=True, + ) + + # ---------------------------------------- + # SQL constraints + # ---------------------------------------- + _offer_price_positive = models.Constraint("CHECK(price > 0)") + + # ---------------------------------------- + # Compute and inverse methods + # ---------------------------------------- + @api.depends("create_date", "validity") + def _compute_date_deadline(self): + for offer in self: + if offer.create_date: + offer.date_deadline = offer.create_date.date() + timedelta(days=offer.validity) + else: # Fallback if create_date is not set + offer.date_deadline = fields.Date.today() + timedelta(days=offer.validity) + + def _inverse_date_deadline(self): + for offer in self: + if offer.date_deadline and offer.create_date: + offer.validity = (offer.date_deadline - offer.create_date.date()).days + elif offer.date_deadline: # Fallback if create_date is not set + offer.validity = (offer.date_deadline - fields.Date.today()).days + + # ---------------------------------------- + # CRUD methods + # ---------------------------------------- + @api.model_create_multi + def create(self, vals_list): + # Validate offer amounts before creation + for vals in vals_list: + if "property_id" in vals and "price" in vals: + # Get the property record + property_record = self.env["estate.property"].browse(vals["property_id"]) + # Check if there are existing offers with higher or equal prices + existing_offers = property_record.offer_ids + if existing_offers: + max_existing_price = max(existing_offers.mapped("price")) + if vals["price"] <= max_existing_price: + msg = f"The offer amount must be higher than the existing offer of {max_existing_price}." + raise UserError(msg) + + # Create the offers + offers = super().create(vals_list) + # Update property status to 'offer_received' for all related properties + offers.mapped("property_id").write({"status": "offer_received"}) + return offers + + # ---------------------------------------- + # Action methods + # ---------------------------------------- + def action_accept_offer(self): + for offer in self: + # Check if there's already an accepted offer + other_offers = offer.property_id.offer_ids - offer + if any(other_offers.filtered(lambda o: o.status == "accepted")): + error_msg = "An offer has already been accepted for this property." + raise UserError(error_msg) + offer.status = "accepted" + # Refuse other offers for the same property + other_offers.write({"status": "refused"}) + # Update the property status + offer.property_id.status = "offer_accepted" + offer.property_id.selling_price = offer.price + offer.property_id.partner_id = offer.partner_id + return True + + def action_refuse_offer(self): + for offer in self: + offer.status = "refused" + return True diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..815ee0f85d1 --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,21 @@ +from odoo import fields, models + + +class EstatePropertyTag(models.Model): + # ---------------------------------------- + # Private attributes + # ---------------------------------------- + _name = "estate.property.tag" + _description = "Estate Property Tag" + _order = "name" + + # ---------------------------------------- + # Field declarations + # ---------------------------------------- + name = fields.Char("Name", required=True) + color = fields.Integer("Color Index", default=0) + + # ---------------------------------------- + # SQL constraints + # ---------------------------------------- + _tag_name_unique = models.Constraint("UNIQUE(name)") diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..f1cbdc46edd --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,31 @@ +from odoo import fields, models + + +class EstatePropertyType(models.Model): + # ---------------------------------------- + # Private attributes + # ---------------------------------------- + _name = "estate.property.type" + _description = "Estate Property Type" + _order = "sequence, name" + + # ---------------------------------------- + # Field declarations + # ---------------------------------------- + name = fields.Char("Name", required=True) + sequence = fields.Integer("Sequence", default=1, help="Used to order property types") + property_ids = fields.One2many("estate.property", "property_type_id", string="Properties") + offer_ids = fields.One2many("estate.property.offer", "property_type_id", string="Offers") + offer_count = fields.Integer("Offer Count", compute="_compute_offer_count") + + # ---------------------------------------- + # SQL constraints + # ---------------------------------------- + _property_type_name_unique = models.Constraint("UNIQUE(name)") + + # ---------------------------------------- + # Compute methods + # ---------------------------------------- + def _compute_offer_count(self): + for property_type in self: + property_type.offer_count = len(property_type.offer_ids) diff --git a/estate/models/res_users.py b/estate/models/res_users.py new file mode 100644 index 00000000000..884a3084a05 --- /dev/null +++ b/estate/models/res_users.py @@ -0,0 +1,13 @@ +from odoo import fields, models + + +class ResUsers(models.Model): + # ---------------------------------------- + # Private attributes + # ---------------------------------------- + _inherit = "res.users" + + # ---------------------------------------- + # Field declarations + # ---------------------------------------- + property_ids = fields.One2many("estate.property", "salesperson_id", string="Real Estate Properties") diff --git a/estate/ruff.toml b/estate/ruff.toml new file mode 100644 index 00000000000..4713f8a60fe --- /dev/null +++ b/estate/ruff.toml @@ -0,0 +1,3 @@ +# ~/Codes/tutorials/ruff.toml + +extend = "../../odoo/ruff.toml" diff --git a/estate/views/estate_menus.xml b/estate/views/estate_menus.xml new file mode 100644 index 00000000000..29029540af4 --- /dev/null +++ b/estate/views/estate_menus.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/estate/views/estate_property_tag_views.xml b/estate/views/estate_property_tag_views.xml new file mode 100644 index 00000000000..ac484509ee1 --- /dev/null +++ b/estate/views/estate_property_tag_views.xml @@ -0,0 +1,18 @@ + + + + Property Tags + estate.property.tag + list + + + estate.property.tag.list + estate.property.tag + + + + + + + + diff --git a/estate/views/estate_property_type_views.xml b/estate/views/estate_property_type_views.xml new file mode 100644 index 00000000000..f12bd7f63a0 --- /dev/null +++ b/estate/views/estate_property_type_views.xml @@ -0,0 +1,78 @@ + + + + + estate.property.offer + Offers + list + [('property_type_id', '=', active_id)] + {'default_property_type_id': active_id} + + + + estate.property.offer.property.type.list + estate.property.offer + + + + + + + + + + + Property Types + estate.property.type + list,form,search + + + estate.property.type.list + estate.property.type + + + + + + + + + estate.property.type.form + estate.property.type + +
+ +
+ +
+ + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.type.search + estate.property.type + + + + + + +
diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml new file mode 100644 index 00000000000..bf0f902855a --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,147 @@ + + + + Properties + estate.property + kanban,list,form,search + + {'search_default_available': True} + + + + estate.property.kanban + estate.property + + + + + +
+ +
+ Expected Price: +
+
+ Best Offer: +
+
+ Selling Price: +
+ +
+
+
+
+
+
+ + + estate.property.list + estate.property + + + + + + + + + + + + + + + + + + estate.property.form + estate.property + +
+ +
+
+
+

+ +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +