From df874172a5e0cfaab8aad05c69613765baad4ee5 Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Tue, 24 Mar 2026 08:39:05 +0100 Subject: [PATCH 01/11] [ADD] awesome_dashboard,awesome_owl,estate,estate_account --- awesome_dashboard/__init__.py | 1 + awesome_dashboard/__manifest__.py | 10 +- awesome_dashboard/models/__init__.py | 1 + awesome_dashboard/models/dashboard_item.py | 50 +++++ .../security/ir.model.access.csv | 2 + awesome_dashboard/static/src/dashboard.js | 8 - awesome_dashboard/static/src/dashboard.xml | 8 - .../dashboard_configuration_dialog.js | 44 +++++ .../dashboard_configuration_dialog.xml | 23 +++ .../dashboard_item/dashboard_item.js | 22 +++ .../dashboard_item/dashboard_item.scss | 20 ++ .../dashboard_item/dashboard_item.xml | 20 ++ .../components/number_card/number_card.js | 9 + .../components/number_card/number_card.scss | 20 ++ .../components/number_card/number_card.xml | 9 + .../components/pie_chart/pie_chart.js | 83 ++++++++ .../components/pie_chart/pie_chart.xml | 8 + .../pie_chart_card/pie_chart_card.js | 14 ++ .../pie_chart_card/pie_chart_card.scss | 14 ++ .../pie_chart_card/pie_chart_card.xml | 10 + .../static/src/dashboard/dashboard.js | 97 ++++++++++ .../static/src/dashboard/dashboard.scss | 40 ++++ .../static/src/dashboard/dashboard.xml | 38 ++++ .../src/dashboard/dashboard_registry.js | 79 ++++++++ .../static/src/dashboard/dashboard_utility.js | 3 + .../dashboard/services/statistics_service.js | 34 ++++ .../static/src/dashboard_action.js | 14 ++ ...awesome_dashboard_dashboard_item_views.xml | 58 ++++++ .../awesome_dashboard_dashboard_views.xml | 8 + ...{views.xml => awesome_dashboard_views.xml} | 5 - awesome_owl/__init__.py | 2 +- awesome_owl/__manifest__.py | 4 +- awesome_owl/controllers/__init__.py | 2 +- awesome_owl/controllers/controllers.py | 3 +- .../static/src/components/card/card.js | 35 ++++ .../static/src/components/card/card.xml | 35 ++++ .../static/src/components/counter/counter.js | 26 +++ .../static/src/components/counter/counter.xml | 9 + .../src/components/todo-item/todo-item.js | 33 ++++ .../src/components/todo-item/todo-item.xml | 20 ++ .../src/components/todo-list/todo-list.js | 90 +++++++++ .../src/components/todo-list/todo-list.xml | 14 ++ awesome_owl/static/src/main.js | 9 +- awesome_owl/static/src/playground.js | 29 ++- awesome_owl/static/src/playground.xml | 20 +- awesome_owl/static/src/utils.js | 9 + awesome_owl/views/templates.xml | 5 +- estate/__init__.py | 1 + estate/__manifest__.py | 21 ++ estate/models/__init__.py | 5 + estate/models/estate_property.py | 125 ++++++++++++ estate/models/estate_property_offer.py | 75 ++++++++ estate/models/estate_property_tag.py | 16 ++ estate/models/estate_property_type.py | 37 ++++ estate/models/res_users.py | 9 + estate/security/ir.model.access.csv | 5 + estate/static/description/icon.png | Bin 0 -> 96701 bytes estate/views/estate_menu_views.xml | 12 ++ estate/views/estate_property_offer_views.xml | 37 ++++ estate/views/estate_property_tag_views.xml | 46 +++++ estate/views/estate_property_type_views.xml | 71 +++++++ estate/views/estate_property_views.xml | 180 ++++++++++++++++++ estate/views/res_users_views.xml | 15 ++ estate_account/__init__.py | 1 + estate_account/__manifest__.py | 15 ++ estate_account/models/__init__.py | 2 + estate_account/models/estate_property.py | 46 +++++ .../models/estate_property_offer.py | 8 + estate_account/static/description/icon.png | Bin 0 -> 87487 bytes .../views/estate_property_views.xml | 31 +++ 70 files changed, 1815 insertions(+), 40 deletions(-) create mode 100644 awesome_dashboard/models/__init__.py create mode 100644 awesome_dashboard/models/dashboard_item.py create mode 100644 awesome_dashboard/security/ir.model.access.csv delete mode 100644 awesome_dashboard/static/src/dashboard.js delete mode 100644 awesome_dashboard/static/src/dashboard.xml create mode 100644 awesome_dashboard/static/src/dashboard/components/dashboard_configuration_dialog/dashboard_configuration_dialog.js create mode 100644 awesome_dashboard/static/src/dashboard/components/dashboard_configuration_dialog/dashboard_configuration_dialog.xml create mode 100644 awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.js create mode 100644 awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.scss create mode 100644 awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.xml create mode 100644 awesome_dashboard/static/src/dashboard/components/number_card/number_card.js create mode 100644 awesome_dashboard/static/src/dashboard/components/number_card/number_card.scss create mode 100644 awesome_dashboard/static/src/dashboard/components/number_card/number_card.xml create mode 100644 awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.js create mode 100644 awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.xml create mode 100644 awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.js create mode 100644 awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.scss create mode 100644 awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.xml create mode 100644 awesome_dashboard/static/src/dashboard/dashboard.js create mode 100644 awesome_dashboard/static/src/dashboard/dashboard.scss create mode 100644 awesome_dashboard/static/src/dashboard/dashboard.xml create mode 100644 awesome_dashboard/static/src/dashboard/dashboard_registry.js create mode 100644 awesome_dashboard/static/src/dashboard/dashboard_utility.js create mode 100644 awesome_dashboard/static/src/dashboard/services/statistics_service.js create mode 100644 awesome_dashboard/static/src/dashboard_action.js create mode 100644 awesome_dashboard/views/awesome_dashboard_dashboard_item_views.xml create mode 100644 awesome_dashboard/views/awesome_dashboard_dashboard_views.xml rename awesome_dashboard/views/{views.xml => awesome_dashboard_views.xml} (66%) create mode 100644 awesome_owl/static/src/components/card/card.js create mode 100644 awesome_owl/static/src/components/card/card.xml create mode 100644 awesome_owl/static/src/components/counter/counter.js create mode 100644 awesome_owl/static/src/components/counter/counter.xml create mode 100644 awesome_owl/static/src/components/todo-item/todo-item.js create mode 100644 awesome_owl/static/src/components/todo-item/todo-item.xml create mode 100644 awesome_owl/static/src/components/todo-list/todo-list.js create mode 100644 awesome_owl/static/src/components/todo-list/todo-list.xml create mode 100644 awesome_owl/static/src/utils.js create mode 100644 estate/__init__.py create mode 100644 estate/__manifest__.py create mode 100644 estate/models/__init__.py create mode 100644 estate/models/estate_property.py create mode 100644 estate/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/models/res_users.py create mode 100644 estate/security/ir.model.access.csv create mode 100644 estate/static/description/icon.png create mode 100644 estate/views/estate_menu_views.xml 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 create mode 100644 estate/views/estate_property_views.xml create mode 100644 estate/views/res_users_views.xml 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 create mode 100644 estate_account/models/estate_property_offer.py create mode 100644 estate_account/static/description/icon.png create mode 100644 estate_account/views/estate_property_views.xml diff --git a/awesome_dashboard/__init__.py b/awesome_dashboard/__init__.py index b0f26a9a602..aa4d0fd63a9 100644 --- a/awesome_dashboard/__init__.py +++ b/awesome_dashboard/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- from . import controllers +from . import models diff --git a/awesome_dashboard/__manifest__.py b/awesome_dashboard/__manifest__.py index a1cd72893d7..70ada2b7e15 100644 --- a/awesome_dashboard/__manifest__.py +++ b/awesome_dashboard/__manifest__.py @@ -17,13 +17,19 @@ 'application': True, 'installable': True, 'depends': ['base', 'web', 'mail', 'crm'], - 'data': [ - 'views/views.xml', + 'security/ir.model.access.csv', + 'views/awesome_dashboard_dashboard_views.xml', + 'views/awesome_dashboard_dashboard_item_views.xml', + 'views/awesome_dashboard_views.xml', ], 'assets': { 'web.assets_backend': [ 'awesome_dashboard/static/src/**/*', + ('remove', 'awesome_dashboard/static/src/dashboard/**/*'), + ], + 'awesome_dashboard.assets_dashboard': [ + 'awesome_dashboard/static/src/dashboard/**/*', ], }, 'license': 'AGPL-3' diff --git a/awesome_dashboard/models/__init__.py b/awesome_dashboard/models/__init__.py new file mode 100644 index 00000000000..9e8fa382cbb --- /dev/null +++ b/awesome_dashboard/models/__init__.py @@ -0,0 +1 @@ +from . import dashboard_item diff --git a/awesome_dashboard/models/dashboard_item.py b/awesome_dashboard/models/dashboard_item.py new file mode 100644 index 00000000000..4bebcfe4b8b --- /dev/null +++ b/awesome_dashboard/models/dashboard_item.py @@ -0,0 +1,50 @@ +from odoo import models, fields, api + + +class DashboardItem(models.Model): + _name = 'awesome_dashboard.dashboard.item' + _description = "Dashboard Item" + + code = fields.Char(string="Code", required=True) + property = fields.Selection( + string="Property", + required=True, + selection=[ + ('average_quantity', "average_quantity"), + ('average_time', "average_time"), + ('nb_cancelled_orders', "nb_cancelled_orders"), + ('nb_new_orders', "nb_new_orders"), + ('orders_by_size', "orders_by_size"), + ('total_amount', "total_amount"), + ] + ) + size = fields.Integer(string="Size", default=1) + name = fields.Char(string="Name", required=True, translate=True) + description = fields.Text(string="Description", required=True, translate=True) + component_type = fields.Selection( + string="Component Type", + required=True, + selection=[ + ('number_card', "Number"), + ('pie_chart_chart', "PieChart"), + ] + ) + sequence = fields.Integer(string="Sequence", default=1) + user_id = fields.Many2one(comodel_name='res.users', string="User", required=True, default=lambda self: self.env.user) + + _code_user_unique_idx = models.UniqueIndex( + '(code, user_id)', + "The code and user must be unique." + ) + + @api.model + def get_by_current_user(self): + return self.search_read([('user_id', '=', self.env.user.id)], [ + 'code', + 'property', + 'size', + 'name', + 'description', + 'component_type', + 'sequence', + ], order='sequence ASC') diff --git a/awesome_dashboard/security/ir.model.access.csv b/awesome_dashboard/security/ir.model.access.csv new file mode 100644 index 00000000000..c88496a383a --- /dev/null +++ b/awesome_dashboard/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_awesome_dashboard_dashboard_item,access_awesome_dashboard_dashboard_item_user,model_awesome_dashboard_dashboard_item,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard.js b/awesome_dashboard/static/src/dashboard.js deleted file mode 100644 index c4fb245621b..00000000000 --- a/awesome_dashboard/static/src/dashboard.js +++ /dev/null @@ -1,8 +0,0 @@ -import { Component } from "@odoo/owl"; -import { registry } from "@web/core/registry"; - -class AwesomeDashboard extends Component { - static template = "awesome_dashboard.AwesomeDashboard"; -} - -registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard.xml b/awesome_dashboard/static/src/dashboard.xml deleted file mode 100644 index 1a2ac9a2fed..00000000000 --- a/awesome_dashboard/static/src/dashboard.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - hello dashboard - - - diff --git a/awesome_dashboard/static/src/dashboard/components/dashboard_configuration_dialog/dashboard_configuration_dialog.js b/awesome_dashboard/static/src/dashboard/components/dashboard_configuration_dialog/dashboard_configuration_dialog.js new file mode 100644 index 00000000000..76adce6c826 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/dashboard_configuration_dialog/dashboard_configuration_dialog.js @@ -0,0 +1,44 @@ +import {Component, useState} from "@odoo/owl"; +import {Dialog} from "@web/core/dialog/dialog"; +import {registry} from "@web/core/registry"; +import {getDashboardStorageKey} from "../../dashboard_utility"; + +export class DashboardConfigurationDialog extends Component { + static template = "awesome_dashboard.DashboardConfigurationDialog"; + static components = { + Dialog, + } + static props = { + close: Function, + } + + setup() { + super.setup(); + + const values = []; + + for (const item of registry.category("awesome_dashboard").getAll()) { + values.push({id: item.id, description: item.description, visible: localStorage.getItem(getDashboardStorageKey(item.id)) === "true"}); + } + + this.state = useState({ + values, + }); + } + + onCheckboxChange(value_id, event) { + this.state.values.find(x => x.id === value_id).visible = event.target.checked; + } + + async onConfirm() { + for (const value of this.state.values) { + localStorage.setItem(getDashboardStorageKey(value.id), value.visible); + } + + this.props.close(); + } + + onDiscard() { + this.props.close(); + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/dashboard_configuration_dialog/dashboard_configuration_dialog.xml b/awesome_dashboard/static/src/dashboard/components/dashboard_configuration_dialog/dashboard_configuration_dialog.xml new file mode 100644 index 00000000000..6b4ba371aec --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/dashboard_configuration_dialog/dashboard_configuration_dialog.xml @@ -0,0 +1,23 @@ + + + + +
+ Which cards do you wish to see ? + + +
+ + +
+
+
+ + + + + +
+
+
\ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.js b/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.js new file mode 100644 index 00000000000..878387e02f8 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.js @@ -0,0 +1,22 @@ +import {Component} from "@odoo/owl"; + +export class DashboardItem extends Component { + static template = "awesome_dashboard.DashboardItem"; + + static defaultProps = { + size: 1, + } + + static props = { + size: {type: Number, optional: true}, + slots: {optional: true}, + } + + get size() { + if (this.env.isSmall) { + return '100%'; + } + + return `${this.props.size * 18}rem`; + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.scss b/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.scss new file mode 100644 index 00000000000..682c098aec4 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.scss @@ -0,0 +1,20 @@ +.o_dashboard-item { + background-color: #fff; + border: 1px solid black; + color: black; + padding: 1.5rem 1rem; + display: flex; + flex-direction: column; + align-items: center; + + .o_dashboard-item-title { + font-weight: bold; + text-align: center; + } + + .o_dashboard-item-count { + font-weight: bold; + font-size: 30px; + color: darkgreen; + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.xml b/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.xml new file mode 100644 index 00000000000..8ffd3684a40 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/dashboard_item/dashboard_item.xml @@ -0,0 +1,20 @@ + + + +
+ +
+ +
+
+ + +
+ +
+
+ + +
+
+
\ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/number_card/number_card.js b/awesome_dashboard/static/src/dashboard/components/number_card/number_card.js new file mode 100644 index 00000000000..7a5660b8c96 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/number_card/number_card.js @@ -0,0 +1,9 @@ +import {Component} from "@odoo/owl"; + +export class NumberCard extends Component { + static template = "awesome_dashboard.NumberCard"; + static props = { + title: String, + value: Number, + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/number_card/number_card.scss b/awesome_dashboard/static/src/dashboard/components/number_card/number_card.scss new file mode 100644 index 00000000000..dba62c10e97 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/number_card/number_card.scss @@ -0,0 +1,20 @@ +.o_number-card { + text-align: center; + + p { + padding: 0; + margin: 0; + } + + .o_title { + font-weight: bold; + font-size: 1.25rem; + color: black; + } + + .o_value { + font-weight: bold; + font-size: 2rem; + color: darkgreen; + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/number_card/number_card.xml b/awesome_dashboard/static/src/dashboard/components/number_card/number_card.xml new file mode 100644 index 00000000000..12a83ff4145 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/number_card/number_card.xml @@ -0,0 +1,9 @@ + + + +
+

+

+
+
+
\ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.js b/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.js new file mode 100644 index 00000000000..a7a20ca4a59 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.js @@ -0,0 +1,83 @@ +import {Component, onMounted, onWillStart, onWillUnmount, onWillUpdateProps, useRef} from "@odoo/owl"; +import {loadJS} from "@web/core/assets"; +import {useService} from "@web/core/utils/hooks"; + +export class PieChart extends Component { + static template = "awesome_dashboard.PieChart"; + + static props = { + data: Object, + onClick: {type: Function, optional: true}, + } + + setup() { + super.setup(); + + this.canvasRef = useRef("chartCanvas"); + this.action = useService("action"); + + this.chart = null; + + onWillStart(async () => { + await loadJS(["/web/static/lib/Chart/Chart.js"]); + }); + + onMounted(() => { + if (this.chart) { + this.chart.destroy(); + } + + this.chart = new Chart(this.canvasRef.el, this._getChartConfig()); + }) + + onWillUpdateProps(nextProps => { + this.chart.data.labels = [...Object.keys(nextProps.data)]; + this.chart.data.datasets.forEach((dataset) => { + dataset.data = Object.values(nextProps.data); + }); + + this.chart.update(); + }); + + onWillUnmount(this.onWillUnmount); + } + + onWillUnmount() { + if (this.chart) { + this.chart.destroy(); + } + } + + _getChartConfig() { + return { + type: 'pie', + data: { + labels: [...Object.keys(this.props.data)], + datasets: [{ + data: Object.values(this.props.data), + backgroundColor: [ + 'yellow', 'salmon', 'green', + ], + borderWidth: 1, + hoverOffset: 4, + }] + }, + options: { + events: ['click'], + }, + plugins: [{ + id: 'customEventCatcher', + beforeEvent: (chart, args) => { + if (args?.event.type === 'click') { + const [activeElement] = chart.getElementsAtEventForMode(args.event, 'nearest', {intersect: true}, true); + const index = activeElement.index; + + if (this.props.onClick) { + this.props.onClick(this.action, Object.keys(this.props.data)[index]); + } + } + } + }], + } + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.xml b/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.xml new file mode 100644 index 00000000000..db77f575ce5 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.xml @@ -0,0 +1,8 @@ + + + +
+ +
+
+
\ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.js b/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.js new file mode 100644 index 00000000000..fffd2deb31c --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.js @@ -0,0 +1,14 @@ +import {Component} from "@odoo/owl"; +import {PieChart} from "../pie_chart/pie_chart"; + +export class PieChartCard extends Component { + static template = "awesome_dashboard.PieChartCard"; + static components = { + PieChart, + } + static props = { + title: String, + data: Object, + onClick: {type: Function, optional: true}, + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.scss b/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.scss new file mode 100644 index 00000000000..b1ac0c26fb2 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.scss @@ -0,0 +1,14 @@ +.o_number-card { + text-align: center; + + p { + padding: 0; + margin: 0; + } + + .o_title { + font-weight: bold; + font-size: 1.25rem; + color: black; + } +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.xml b/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.xml new file mode 100644 index 00000000000..02761a2b8b5 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/components/pie_chart_card/pie_chart_card.xml @@ -0,0 +1,10 @@ + + + +
+

+ + +
+
+
\ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/dashboard.js b/awesome_dashboard/static/src/dashboard/dashboard.js new file mode 100644 index 00000000000..3e14c74c496 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard.js @@ -0,0 +1,97 @@ +import {Component, onMounted, useState} from "@odoo/owl"; +import {registry} from "@web/core/registry"; +import {Layout} from "@web/search/layout"; +import {useService} from "@web/core/utils/hooks"; +import {_t} from "@web/core/l10n/translation"; +import {DashboardItem} from "./components/dashboard_item/dashboard_item"; +import {PieChart} from "./components/pie_chart/pie_chart"; +import {NumberCard} from "./components/number_card/number_card"; +import {PieChartCard} from "./components/pie_chart_card/pie_chart_card"; + +class AwesomeDashboard extends Component { + static template = "awesome_dashboard.AwesomeDashboard"; + static components = { + Layout, + DashboardItem, + PieChart, + }; + + setup() { + this.action = useService("action"); + this.orm = useService("orm"); + + this.statistics = useState(useService("statistics")); + + this.state = useState({ + items: null, + }); + + onMounted(async () => { + await this._refreshSources(); + }) + } + + openCustomerKanban() { + this.action.doAction("base.action_partner_form"); + } + + openLeads() { + this.action.doAction({ + type: "ir.actions.act_window", + name: _t("Pipeline"), + target: "current", + res_model: "crm.lead", + views: [[false, "kanban"], [false, "list"], [false, "form"]], + }); + } + + openConfiguration() { + this.action.doAction({ + type: "ir.actions.act_window", + name: _t("Dashboard Configuration"), + target: "current", + res_model: "awesome_dashboard.dashboard.item", + views: [[false, "list"], [false, "form"]], + context: { + search_default_my_items: true, + }, + }); + } + + async _refreshSources() { + const results = await this.orm.call("awesome_dashboard.dashboard.item", "get_by_current_user"); + + const items = []; + + for (const result of results) { + const item = { + id: result.id, + description: result.name, + size: result.size, + } + + switch (result.component_type) { + case 'number_card': + item.component = NumberCard; + item.props = (data) => ({ + title: result.description, + value: data[result.property], + }); + break; + case 'pie_chart_chart': + item.component = PieChartCard; + item.props = (data) => ({ + title: result.description, + data: data[result.property], + }); + break; + } + + items.push(item); + } + + this.state.items = items; + } +} + +registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard); diff --git a/awesome_dashboard/static/src/dashboard/dashboard.scss b/awesome_dashboard/static/src/dashboard/dashboard.scss new file mode 100644 index 00000000000..fa30bcbc89b --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard.scss @@ -0,0 +1,40 @@ +.o_dashboard { + background-color: #cfcfcf; + display: flex; + flex-flow: row wrap; + align-content: flex-start; + gap: 1rem; + padding: 0.5rem; + justify-content: flex-start; + + .text-unicorn { + background: linear-gradient(90deg, + red, + orange, + yellow, + green, + blue, + indigo, + violet + ); + background-size: 200% 200%; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + animation: rainbow 3s ease-in-out infinite; + font-weight: bold; + font-size: 1.2rem; + + @keyframes rainbow { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } + } + } +} diff --git a/awesome_dashboard/static/src/dashboard/dashboard.xml b/awesome_dashboard/static/src/dashboard/dashboard.xml new file mode 100644 index 00000000000..407cd9856d5 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + +
+ +
Loading dashboard...
+
+
+ +
+ +
Looks like you are all alone. Try changing the display with the configuration menu
+
+
+ + + + + + + + +
+
+
diff --git a/awesome_dashboard/static/src/dashboard/dashboard_registry.js b/awesome_dashboard/static/src/dashboard/dashboard_registry.js new file mode 100644 index 00000000000..0ef20284cb7 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard_registry.js @@ -0,0 +1,79 @@ +import {NumberCard} from "./components/number_card/number_card"; +import {PieChartCard} from "./components/pie_chart_card/pie_chart_card"; +import {registry} from "@web/core/registry"; +import {_t} from "@web/core/l10n/translation"; + +const dashboard_items = [ + { + id: "average_quantity", + description: _t("Average amount of t-shirt"), + Component: NumberCard, + size: 2, + props: (data) => ({ + title: _t("Average amount of t-shirt by order this month"), + value: data.average_quantity + }), + }, + { + id: "average_time", + description: _t("Average time"), + Component: NumberCard, + size: 2, + props: (data) => ({ + title: _t("Average time for an order to go from 'new' to 'sent' or 'cancelled'"), + value: data.average_time + }), + }, + { + id: "nb_new_orders", + description: _t("Number of new orders this month"), + Component: NumberCard, + size: 2, + props: (data) => ({ + title: _t("Number of new orders this month"), + value: data.nb_new_orders + }), + }, + { + id: "nb_cancelled_orders", + description: _t("Number of cancelled orders this month"), + Component: NumberCard, + size: 2, + props: (data) => ({ + title: _t("Number of cancelled orders this month"), + value: data.nb_cancelled_orders + }), + }, + { + id: "total_amount", + description: _t("Total amount of new orders this month"), + Component: NumberCard, + size: 1, + props: (data) => ({ + title: _t("Total amount of new orders this month"), + value: data.total_amount + }), + }, + { + id: "orders_by_size", + description: _t("Shirt orders by size"), + Component: PieChartCard, + size: 2, + props: (data) => ({ + title: _t("Shirt orders by size"), + data: data.orders_by_size, + onClick: (action, item) => action.doAction({ + type: 'ir.actions.act_window', + name: _t('Pipeline'), + target: 'current', + res_model: 'crm.lead', + views: [[false, 'kanban'], [false, 'list'], [false, 'form']], + domain: [['name', '=', item]], + }), + }), + }, +]; + +for (const dashboardItem of dashboard_items) { + registry.category("awesome_dashboard").add(dashboardItem.id, dashboardItem); +} diff --git a/awesome_dashboard/static/src/dashboard/dashboard_utility.js b/awesome_dashboard/static/src/dashboard/dashboard_utility.js new file mode 100644 index 00000000000..d925053b5d5 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/dashboard_utility.js @@ -0,0 +1,3 @@ +export function getDashboardStorageKey(id) { + return `awesome_dashboard:${id}:visible`; +} \ No newline at end of file diff --git a/awesome_dashboard/static/src/dashboard/services/statistics_service.js b/awesome_dashboard/static/src/dashboard/services/statistics_service.js new file mode 100644 index 00000000000..1ab2c353606 --- /dev/null +++ b/awesome_dashboard/static/src/dashboard/services/statistics_service.js @@ -0,0 +1,34 @@ +import {rpc} from "@web/core/network/rpc"; +import {registry} from "@web/core/registry"; +import {reactive} from "@odoo/owl"; +import {browser} from "@web/core/browser/browser"; + +export class StatisticsService { + constructor() { + this.state = reactive({loaded: false}); + + browser.setInterval(async () => await this._loadStatistics(), 10_000); + + const odoo = (globalThis.odoo ||= {}); + + if (odoo.debug == "1") { + browser.setTimeout(async () => await this._loadStatistics(), 2_500); + } else { + this._loadStatistics(); + } + } + + async _loadStatistics() { + const data = await rpc("/awesome_dashboard/statistics"); + + Object.assign(this.state, data, {loaded: true}); + } +} + +export const statisticsService = { + start() { + return (new StatisticsService()).state; + } +} + +registry.category("services").add("statistics", statisticsService); diff --git a/awesome_dashboard/static/src/dashboard_action.js b/awesome_dashboard/static/src/dashboard_action.js new file mode 100644 index 00000000000..e549694663c --- /dev/null +++ b/awesome_dashboard/static/src/dashboard_action.js @@ -0,0 +1,14 @@ +import {Component, xml} from "@odoo/owl"; +import {registry} from "@web/core/registry"; +import {LazyComponent} from "@web/core/assets"; + +class AwesomeDashboardLoader extends Component { + static components = { + LazyComponent, + }; + static template = xml` + + ` +} + +registry.category("actions").add("awesome_dashboard.dashboard", AwesomeDashboardLoader); diff --git a/awesome_dashboard/views/awesome_dashboard_dashboard_item_views.xml b/awesome_dashboard/views/awesome_dashboard_dashboard_item_views.xml new file mode 100644 index 00000000000..53fa4143d15 --- /dev/null +++ b/awesome_dashboard/views/awesome_dashboard_dashboard_item_views.xml @@ -0,0 +1,58 @@ + + + + dashboard.item.view.list + awesome_dashboard.dashboard.item + + + + + + + + + + + + + dashboard.item.view.form + awesome_dashboard.dashboard.item + +
+ +
+
+

+ +

+
+
+ + + + + + + +
+
+
+
+ + + dashboard.item.view.search + awesome_dashboard.dashboard.item + + + + + + + + + Dashboard Items + awesome_dashboard.dashboard.item + list,form + {'search_default_my_items': True} + +
\ No newline at end of file diff --git a/awesome_dashboard/views/awesome_dashboard_dashboard_views.xml b/awesome_dashboard/views/awesome_dashboard_dashboard_views.xml new file mode 100644 index 00000000000..bb0b9d8292a --- /dev/null +++ b/awesome_dashboard/views/awesome_dashboard_dashboard_views.xml @@ -0,0 +1,8 @@ + + + + Dashboard + awesome_dashboard.dashboard + + + diff --git a/awesome_dashboard/views/views.xml b/awesome_dashboard/views/awesome_dashboard_views.xml similarity index 66% rename from awesome_dashboard/views/views.xml rename to awesome_dashboard/views/awesome_dashboard_views.xml index 47fb2b6f258..e45dd6c9da4 100644 --- a/awesome_dashboard/views/views.xml +++ b/awesome_dashboard/views/awesome_dashboard_views.xml @@ -1,10 +1,5 @@ - - Dashboard - awesome_dashboard.dashboard - - diff --git a/awesome_owl/__init__.py b/awesome_owl/__init__.py index 457bae27e11..b0f26a9a602 100644 --- a/awesome_owl/__init__.py +++ b/awesome_owl/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import controllers \ No newline at end of file +from . import controllers diff --git a/awesome_owl/__manifest__.py b/awesome_owl/__manifest__.py index 55002ab81de..e702a426d22 100644 --- a/awesome_owl/__manifest__.py +++ b/awesome_owl/__manifest__.py @@ -7,8 +7,8 @@ """, 'description': """ - Starting module for "Discover the JS framework, chapter 1: Owl components" - """, + Starting module for "Discover the JS framework, chapter 1: Owl components" + """, 'author': "Odoo", 'website': "https://www.odoo.com", diff --git a/awesome_owl/controllers/__init__.py b/awesome_owl/controllers/__init__.py index 457bae27e11..b0f26a9a602 100644 --- a/awesome_owl/controllers/__init__.py +++ b/awesome_owl/controllers/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import controllers \ No newline at end of file +from . import controllers diff --git a/awesome_owl/controllers/controllers.py b/awesome_owl/controllers/controllers.py index bccfd6fe283..a50010bfca1 100644 --- a/awesome_owl/controllers/controllers.py +++ b/awesome_owl/controllers/controllers.py @@ -1,5 +1,6 @@ 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/components/card/card.js b/awesome_owl/static/src/components/card/card.js new file mode 100644 index 00000000000..9ff62da0cb2 --- /dev/null +++ b/awesome_owl/static/src/components/card/card.js @@ -0,0 +1,35 @@ +import {Component, markup, useState} from "@odoo/owl"; + +export class Card extends Component { + static template = "awesome_owl.card"; + + static defaultProps = { + collapsable: true, + } + + static props = { + title: String, + description: {type: String, optional: true}, + help: {type: String, optional: true}, + slots: {optional: true}, + collapsable: {optional: true, default: true}, + } + + setup() { + super.setup(); + + this.state = useState({ + title: this.props.title, + help: this.props.help ? markup(this.props.help) : null, + collapsed: false, + }); + } + + toggleCollapse() { + if (!this.props.collapsable) { + return; + } + + this.state.collapsed = !this.state.collapsed; + } +} diff --git a/awesome_owl/static/src/components/card/card.xml b/awesome_owl/static/src/components/card/card.xml new file mode 100644 index 00000000000..a19a21e64b5 --- /dev/null +++ b/awesome_owl/static/src/components/card/card.xml @@ -0,0 +1,35 @@ + + + +
+
+
+ + + + + +
+ + +
+ + + +
+
+
+ +
+ + + +
+
+
+
diff --git a/awesome_owl/static/src/components/counter/counter.js b/awesome_owl/static/src/components/counter/counter.js new file mode 100644 index 00000000000..dfb5c127ca6 --- /dev/null +++ b/awesome_owl/static/src/components/counter/counter.js @@ -0,0 +1,26 @@ +import {Component, useState} from "@odoo/owl"; + +export class Counter extends Component { + static template = "awesome_owl.counter"; + + static props = { + description: {type: String, optional: true}, + onChange: {type: Function, optional: true}, + } + + setup() { + super.setup(); + + this.state = useState({ + counter: 0, + }); + } + + increment() { + this.state.counter++; + + if (this.props.onChange) { + this.props.onChange(); + } + } +} diff --git a/awesome_owl/static/src/components/counter/counter.xml b/awesome_owl/static/src/components/counter/counter.xml new file mode 100644 index 00000000000..5a8bde52c88 --- /dev/null +++ b/awesome_owl/static/src/components/counter/counter.xml @@ -0,0 +1,9 @@ + + + +
+

Counter :

+ +
+
+
diff --git a/awesome_owl/static/src/components/todo-item/todo-item.js b/awesome_owl/static/src/components/todo-item/todo-item.js new file mode 100644 index 00000000000..7a0c0ac4a73 --- /dev/null +++ b/awesome_owl/static/src/components/todo-item/todo-item.js @@ -0,0 +1,33 @@ +import {Component} from "@odoo/owl"; +import {_t} from "@web/core/l10n/translation"; + +export class TodoItem extends Component { + static template = "awesome_owl.TodoItem"; + + static props = { + id: Number, + description: String, + isCompleted: Boolean, + toggleState: Function, + removeTodo: Function, + } + + setup() { + super.setup(); + } + + toggleState(ev) { + // You can use !this.state.isCompleted instead of ev.target.checked if you want + // to just toggle the state without getting the state from the DOM + this.props.toggleState(this.props.id, ev.target.checked); + } + + removeTodo(e) { + e.preventDefault(); + e.stopPropagation(); + + if (confirm(_t("Do you confirm the removal of this todo ?"))) { + this.props.removeTodo(this.props.id); + } + } +} diff --git a/awesome_owl/static/src/components/todo-item/todo-item.xml b/awesome_owl/static/src/components/todo-item/todo-item.xml new file mode 100644 index 00000000000..bc53678efd0 --- /dev/null +++ b/awesome_owl/static/src/components/todo-item/todo-item.xml @@ -0,0 +1,20 @@ + + + +
+ + +
+
+
\ No newline at end of file diff --git a/awesome_owl/static/src/components/todo-list/todo-list.js b/awesome_owl/static/src/components/todo-list/todo-list.js new file mode 100644 index 00000000000..39f62d03c33 --- /dev/null +++ b/awesome_owl/static/src/components/todo-list/todo-list.js @@ -0,0 +1,90 @@ +import {Component, useState} from "@odoo/owl"; +import {TodoItem} from "../todo-item/todo-item"; +import {useAutoFocus} from "../../utils"; +import {useService} from "@web/core/utils/hooks"; +import {_t} from "@web/core/l10n/translation"; + +export class TodoList extends Component { + static template = "awesome_owl.TodoList"; + static components = { + TodoItem, + } + + static props = { + description: {type: String, optional: true}, + } + + setup() { + super.setup(); + + this.notification = useService("notification"); + + useAutoFocus("todoInput"); + + this.state = useState({ + todos: this._getTodos(), + }); + } + + todoOnKeyup(event) { + if (event.keyCode !== 13) { + return; + } + + const value = event.target.value; + + if (!value || value.trim() === "") { + return; + } + + const max = (this.state.todos.length ? Math.max(...this.state.todos.map(todo => todo.id)) : 0) + 1; + + this._addTodo({id: max, description: value, isCompleted: false}); + + event.target.value = ""; + + this.notification.add(_t("Todo added successfully!"), {type: "success"}); + } + + _getTodos() { + const todos = localStorage.getItem("todos"); + + if (!todos) { + return []; + } + + try { + return JSON.parse(todos); + } catch (_error) { + return []; + } + } + + _saveTodos() { + localStorage.setItem("todos", JSON.stringify(this.state.todos)); + } + + _addTodo(todo) { + this.state.todos.push(todo); + + const todos = this._getTodos(); + + todos.push(todo); + + this._saveTodos(); + } + + toggleItemState(id, state) { + this.state.todos.find(x => x.id === id).isCompleted = state; + } + + removeTodo(id) { + const index = this.state.todos.findIndex(x => x.id === id); + + if (index !== -1) { + this.state.todos.splice(index, 1); + + this._saveTodos(); + } + } +} diff --git a/awesome_owl/static/src/components/todo-list/todo-list.xml b/awesome_owl/static/src/components/todo-list/todo-list.xml new file mode 100644 index 00000000000..5d23b4d46aa --- /dev/null +++ b/awesome_owl/static/src/components/todo-list/todo-list.xml @@ -0,0 +1,14 @@ + + + +
+ +
+ +
+ +
+
+
\ No newline at end of file diff --git a/awesome_owl/static/src/main.js b/awesome_owl/static/src/main.js index 1aaea902b55..76e63a962c7 100644 --- a/awesome_owl/static/src/main.js +++ b/awesome_owl/static/src/main.js @@ -1,12 +1,11 @@ -import { whenReady } from "@odoo/owl"; -import { mountComponent } from "@web/env"; -import { Playground } from "./playground"; +import {whenReady} from "@odoo/owl"; +import {mountComponent} from "@web/env"; +import {Playground} from "./playground"; const config = { dev: true, - name: "Owl Tutorial" + name: "Owl Tutorial", }; // Mount the Playground component when the document.body is ready whenReady(() => mountComponent(Playground, document.body, config)); - diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..c4c7d73afda 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,32 @@ -import { Component } from "@odoo/owl"; +import {Component, onWillStart, useState} from "@odoo/owl"; +import {Counter} from "./components/counter/counter"; +import {Card} from "./components/card/card"; +import {TodoList} from "./components/todo-list/todo-list"; export class Playground extends Component { static template = "awesome_owl.playground"; + static components = { + Counter, + Card, + TodoList, + } + + static props = { + description: {type: String, optional: true}, + } + + setup() { + this.state = useState({ + sum: 0, + }) + + // For training purposes + onWillStart(() => { + this.state.sum = 2; + }); + } + + incrementSum() { + this.state.sum++; + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..f32285c00a1 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -1,10 +1,22 @@ - -
- hello world +
+

The sum of the Counters :

+ + + + + + + + + + + + + +
- diff --git a/awesome_owl/static/src/utils.js b/awesome_owl/static/src/utils.js new file mode 100644 index 00000000000..f374a9dd3c8 --- /dev/null +++ b/awesome_owl/static/src/utils.js @@ -0,0 +1,9 @@ +import {onMounted, useRef} from "@odoo/owl"; + +export function useAutoFocus(name) { + const input = useRef(name); + + onMounted(() => { + input.el.focus(); + }); +} diff --git a/awesome_owl/views/templates.xml b/awesome_owl/views/templates.xml index aa54c1a7241..0a0f1046305 100644 --- a/awesome_owl/views/templates.xml +++ b/awesome_owl/views/templates.xml @@ -1,15 +1,16 @@ - 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..e07c1a9917d --- /dev/null +++ b/estate/__manifest__.py @@ -0,0 +1,21 @@ +{ + 'name': 'Real Estate', + 'description': 'Real Estate - Management', + 'category': 'Sales/CRM', + 'version': '1.0', + 'depends': ['base'], + 'author': 'Odoo S.A.', + 'license': 'LGPL-3', + 'data': [ + 'security/ir.model.access.csv', + 'views/estate_property_offer_views.xml', + 'views/estate_property_views.xml', + 'views/estate_property_type_views.xml', + 'views/estate_property_tag_views.xml', + 'views/res_users_views.xml', + 'views/estate_menu_views.xml', + ], + 'assets': {}, + 'application': True, + 'installable': True, +} diff --git a/estate/models/__init__.py b/estate/models/__init__.py new file mode 100644 index 00000000000..fea9f441d6d --- /dev/null +++ b/estate/models/__init__.py @@ -0,0 +1,5 @@ +from . import estate_property +from . import estate_property_offer +from . import estate_property_tag +from . import estate_property_type +from . import res_users diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py new file mode 100644 index 00000000000..1ec47f9b859 --- /dev/null +++ b/estate/models/estate_property.py @@ -0,0 +1,125 @@ +from dateutil.relativedelta import relativedelta + +from odoo import fields, models, api +from odoo.exceptions import UserError +from odoo.tools import float_compare + + +class EstateProperty(models.Model): + _name = 'estate.property' + _description = "Estate property" + + _order = 'id desc' + + name = fields.Char(required=True, string="Title") + description = fields.Text(string="Description") + postcode = fields.Char(required=True, string="Postcode") + date_availability = fields.Date(required=True, default=lambda self: fields.Date.today() + relativedelta(months=+3), copy=False, string="Available From") + expected_price = fields.Float(required=True, string="Expected Price") + selling_price = fields.Float(readonly=True, copy=False, string="Selling Price") + bedrooms = fields.Integer(required=True, default=2, string="Bedrooms") + living_area = fields.Integer(required=True, string="Living Area (sqm)", help="Living area with a ceiling height of minimum 4 feet") + facades = fields.Integer(required=True, string="Facades") + garage = fields.Boolean(string="Garage") + garden = fields.Boolean(string="Garden", inverse='_inverse_garden') + garden_area = fields.Integer(string="Garden Area (sqm)") + garden_orientation = fields.Selection([ + ('north', "North"), ('south', "South"), ('east', "East"), ('west', "West") + ], string="Garden Orientation") + total_area = fields.Integer(store=True, compute='_compute_total_area', string="Total Area (sqm)") + state = fields.Selection([ + ('new', "New"), ('offer_received', "Offer Received"), ('offer_accepted', "Offer Accepted"), ('sold', "Sold"), ('canceled', "Canceled") + ], string="State", default='new', required=True, readonly=True) + active = fields.Boolean(default=True) + property_type_id = fields.Many2one('estate.property.type', string="Property Type") + buyer_id = fields.Many2one('res.partner', string="Buyer", copy=False) + seller_id = fields.Many2one('res.users', string="Salesman", default=lambda self: self.env.user) + tag_ids = fields.Many2many('estate.property.tag', string="Tags") + offer_ids = fields.One2many('estate.property.offer', 'property_id', string="Offers") + best_price = fields.Float(required=True, string="Best Price", readonly=True, compute='_compute_best_price') + available_for_offers = fields.Boolean(store=False, compute='_compute_available_for_offers') + color = fields.Integer(string="Color Index", default=0) + + _check_expected_price = models.Constraint( + 'CHECK (expected_price > 0)', + "The expected price must be greater than 0." + ) + _check_selling_price = models.Constraint( + 'CHECK (selling_price >= 0)', + "The selling price must be greater or equal than 0." + ) + + @api.depends('living_area', 'garden_area', 'garden') + def _compute_total_area(self): + for record in self: + record.total_area = record.living_area + record.garden_area + + @api.onchange('garden') + def _on_change_garden(self): + for record in self: + if record.garden: + record.garden_area = 10 + record.garden_orientation = 'north' + else: + record.garden_area = 0 + record.garden_orientation = None + + def _inverse_garden(self): + for record in self: + if not record.garden: + record.garden_area = 0 + record.garden_orientation = None + + @api.depends('offer_ids.price') + def _compute_best_price(self): + for record in self: + record.best_price = max(record.offer_ids.mapped('price')) if len(record.offer_ids) > 0 else 0 + + def action_mark_as_sold(self): + for record in self: + if record.state != 'offer_accepted': + raise UserError(self.env._("Cannot mark a non offer_accepted property as sold")) + + if len(record.offer_ids.filtered(lambda offer: offer.status == 'accepted')) != 1: + raise UserError(self.env._("Cannot mark a property as sold without an accepted offer")) + + record.state = 'sold' + + def action_mark_as_canceled(self): + for record in self: + if record.state == 'sold': + raise UserError(self.env._("Cannot mark a sold property as canceled")) + record.state = 'canceled' + + def compute_accepted_offer(self, offer): + for record in self: + if not record.available_for_offers: + raise UserError(self.env._("Cannot add or update an offer on a property that is not available for offers")) + + record.selling_price = offer.price + record.buyer_id = offer.partner_id + record.state = 'offer_accepted' + + def compute_new_offer(self): + for record in self: + if not record.available_for_offers: + raise UserError(self.env._("Cannot add or update an offer on a property that is not available for offers")) + + if record.state == 'new': + record.state = 'offer_received' + + def _compute_available_for_offers(self): + for record in self: + record.available_for_offers = record.state in {'new', 'offer_received'} + + @api.constrains('selling_price', 'expected_price') + def _check_prices(self): + for record in self: + if len(record.offer_ids) > 0 and float_compare(record.selling_price, record.expected_price * 0.9, 2) == -1: + raise UserError(self.env._("The selling price must be at least 90% of the expected price")) + + @api.ondelete(at_uninstall=False) + def _check_ondelete(self): + for record in self: + if record.state not in {'new', 'canceled'}: + raise UserError(self.env._("Cannot delete a property that is not in the new or canceled state")) diff --git a/estate/models/estate_property_offer.py b/estate/models/estate_property_offer.py new file mode 100644 index 00000000000..11d1514e665 --- /dev/null +++ b/estate/models/estate_property_offer.py @@ -0,0 +1,75 @@ +from dateutil.relativedelta import relativedelta + +from odoo import fields, models, api +from odoo.exceptions import UserError + + +class EstatePropertyOffer(models.Model): + _name = 'estate.property.offer' + _description = "Estate property offer" + + _order = 'price desc' + + price = fields.Float(required=True, string="Price") + status = fields.Selection([ + ('accepted', "Accepted"), ('refused', "Refused"), + ], copy=False, string="Status", readonly=True) + partner_id = fields.Many2one('res.partner', string="Partner", required=True) + property_id = fields.Many2one('estate.property', string="Property", required=True) + validity = fields.Integer(required=True, string="Validity", default=7) + date_deadline = fields.Date(string="Deadline", compute='_compute_date_deadline', inverse='_inverse_date_deadline') + available_for_offers = fields.Boolean(related='property_id.available_for_offers') + property_type_id = fields.Many2one('estate.property.type', related='property_id.property_type_id', store=True) + + _check_price = models.Constraint( + 'CHECK (price > 0)', + "The price must be greater than 0." + ) + + @api.depends('validity') + def _compute_date_deadline(self): + for record in self: + record.date_deadline = fields.Date.today() + relativedelta(days=record.validity) + + def _inverse_date_deadline(self): + for record in self: + record.validity = relativedelta(record.date_deadline, (record.create_date if record.create_date else fields.Date.today())).days + + def action_mark_as_accepted(self): + for record in self: + if record.available_for_offers: + record.status = 'accepted' + record.property_id.compute_accepted_offer(record) + else: + raise UserError(self.env._("This offer cannot be accepted because the property is already sold or canceled or an offer has been accepted")) + + return True + + def action_mark_as_refused(self): + for record in self: + if record.available_for_offers: + record.status = 'refused' + else: + raise UserError(self.env._("This offer cannot be accepted because the property is already sold or canceled or an offer has been accepted")) + + return True + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if 'property_id' not in vals: + raise UserError(self.env._("You must select a property for the offer.")) + if 'price' not in vals: + raise UserError(self.env._("You must set a price for the offer.")) + + property = self.env['estate.property'].browse(vals['property_id']) + + if len(property.offer_ids.filtered(lambda offer: offer.price > vals['price'])) > 0: + raise UserError(self.env._("Cannot create an offer with a lower amount than the other offers for this property.")) + + offers = super().create(vals_list) + + for record in offers: + record.property_id.compute_new_offer() + + return offers diff --git a/estate/models/estate_property_tag.py b/estate/models/estate_property_tag.py new file mode 100644 index 00000000000..1dde5294eaf --- /dev/null +++ b/estate/models/estate_property_tag.py @@ -0,0 +1,16 @@ +from odoo import fields, models + + +class EstatePropertyTag(models.Model): + _name = 'estate.property.tag' + _description = "Estate property tag" + + _order = 'name' + + name = fields.Char(required=True, string="Name") + color = fields.Integer(string="Color") + + _name_unique_idx = models.UniqueIndex( + '(name)', + "The name of the property tag must be unique." + ) diff --git a/estate/models/estate_property_type.py b/estate/models/estate_property_type.py new file mode 100644 index 00000000000..2d0c7448643 --- /dev/null +++ b/estate/models/estate_property_type.py @@ -0,0 +1,37 @@ +from odoo import fields, models, api + + +class EstatePropertyType(models.Model): + _name = 'estate.property.type' + _description = "Estate property type" + + _order = 'sequence asc' + + name = fields.Char(required=True, string="Title") + property_ids = fields.One2many('estate.property', 'property_type_id', string="Properties") + sequence = fields.Integer() + offer_ids = fields.One2many('estate.property.offer', 'property_type_id', string="Offers") + offer_count = fields.Integer(compute='_compute_offer_count') + + _name_unique_idx = models.UniqueIndex( + '(name)', + "The title of the property type must be unique." + ) + + @api.depends('offer_ids') + def _compute_offer_count(self): + for record in self: + record.offer_count = len(record.offer_ids) + + def action_show_offers(self): + self.ensure_one() + + return { + 'name': self.env._("Offers"), + 'view_mode': 'list,form', + 'res_model': 'estate.property.offer', + 'type': 'ir.actions.act_window', + 'context': {'create': False, 'delete': False, 'edit': False}, + 'domain': [('property_type_id', 'in', self.id)], + 'target': 'current', + } diff --git a/estate/models/res_users.py b/estate/models/res_users.py new file mode 100644 index 00000000000..472cc6c1f75 --- /dev/null +++ b/estate/models/res_users.py @@ -0,0 +1,9 @@ +from odoo import fields, models + + +class ResUsers(models.Model): + _inherit = 'res.users' + + property_ids = fields.One2many('estate.property', 'seller_id', string="Available Properties", + domain=[('state', 'in', {'new', 'offer_received'})] + ) diff --git a/estate/security/ir.model.access.csv b/estate/security/ir.model.access.csv new file mode 100644 index 00000000000..5d860c91af0 --- /dev/null +++ b/estate/security/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,access_estate_property_user,model_estate_property,base.group_user,1,1,1,1 +access_estate_property_type,access_estate_property_type_user,model_estate_property_type,base.group_user,1,1,1,1 +access_estate_property_tag,access_estate_property_tag_user,model_estate_property_tag,base.group_user,1,1,1,1 +access_estate_property_offer,access_estate_property_offer_user,model_estate_property_offer,base.group_user,1,1,1,1 diff --git a/estate/static/description/icon.png b/estate/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..24887121b131937a4bfe5185b161aa93e85d29b8 GIT binary patch literal 96701 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�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 TB7?#}B74g&9+lW@|BnA3jk@lx literal 0 HcmV?d00001 diff --git a/estate/views/estate_menu_views.xml b/estate/views/estate_menu_views.xml new file mode 100644 index 00000000000..f38e53f4881 --- /dev/null +++ b/estate/views/estate_menu_views.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/estate/views/estate_property_offer_views.xml b/estate/views/estate_property_offer_views.xml new file mode 100644 index 00000000000..612d80a0304 --- /dev/null +++ b/estate/views/estate_property_offer_views.xml @@ -0,0 +1,37 @@ + + + + estate.property.offer.view.list + estate.property.offer + + + + + + + + +
+ +
+
+

+ +

+
+
+ + + + + + + + + + + + + + + + + + + Estate Property Type + 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 new file mode 100644 index 00000000000..e61b3e25d58 --- /dev/null +++ b/estate/views/estate_property_views.xml @@ -0,0 +1,180 @@ + + + + estate.property.view.list + estate.property + + + + + + + + + + + + + + + + + + estate.property.search + estate.property + + + + + + + + + + + + + + + + + + + + + + + + estate.property.view.form + estate.property + +
+
+
+ + +
+ +
+ + + + + +
+
+

+ +

+
+ +
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + estate.property.view.kanban + estate.property + + + + + + + + +
+ + +
+ Expected Price: +
+
+ Best Price: +
+
+ Selling Price: +
+
+ +
+
+
+ + +
+ Edit + Delete + + +
+
+
+
+
+
+ + + Estate Property + estate.property + list,form,kanban + {'search_default_available': True} + +
\ No newline at end of file diff --git a/estate/views/res_users_views.xml b/estate/views/res_users_views.xml new file mode 100644 index 00000000000..e2643ebd2b6 --- /dev/null +++ b/estate/views/res_users_views.xml @@ -0,0 +1,15 @@ + + + + res.users.form.inherit.properties + res.users + + + + + + + + + + \ No newline at end of file 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..99bc9c7560d --- /dev/null +++ b/estate_account/__manifest__.py @@ -0,0 +1,15 @@ +{ + 'name': 'Real Estate Accounting', + 'description': 'Real Estate - Accounting', + 'category': 'Sales/CRM', + 'version': '1.0', + 'depends': ['estate', 'account'], + 'author': 'Odoo S.A.', + 'license': 'LGPL-3', + 'data': [ + 'views/estate_property_views.xml' + ], + 'assets': {}, + 'application': True, + 'installable': True, +} diff --git a/estate_account/models/__init__.py b/estate_account/models/__init__.py new file mode 100644 index 00000000000..49e424d7342 --- /dev/null +++ b/estate_account/models/__init__.py @@ -0,0 +1,2 @@ +from . import estate_property +from . import estate_property_offer diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py new file mode 100644 index 00000000000..d61f33b0436 --- /dev/null +++ b/estate_account/models/estate_property.py @@ -0,0 +1,46 @@ +from odoo import models, Command, fields + + +class EstateProperty(models.Model): + _inherit = ['estate.property'] + _name = 'estate.property' + + invoice_count = fields.Integer(compute='_compute_invoice_count', string="Invoice Count") + + def action_mark_as_sold(self): + # Keep it at the beginning to trigger the validation first + result = super().action_mark_as_sold() + + for record in self: + # It's valid to assume that there is one accepted offer (validated by the inherited entity) + accepted_offer = record.offer_ids.filtered(lambda offer: offer.status == 'accepted')[0] + + account_move = self.env['account.move'].create({ + 'partner_id': record.buyer_id.id, + 'move_type': 'out_invoice', + 'invoice_line_ids': [ + Command.create({ + 'name': "6% of selling price", + 'quantity': 1, + 'price_unit': record.selling_price * 0.06, + }), + Command.create({ + 'name': "Administrative fees", + 'quantity': 1, + 'price_unit': 100.0, + }), + ], + }) + + accepted_offer.account_move_id = account_move + + return result + + def _compute_invoice_count(self): + for record in self: + record.invoice_count = len(record.offer_ids.account_move_id) + + def action_view_invoices(self): + self.ensure_one() + invoices = self.offer_ids.account_move_id + return invoices._get_records_action() diff --git a/estate_account/models/estate_property_offer.py b/estate_account/models/estate_property_offer.py new file mode 100644 index 00000000000..5e0e47b2b63 --- /dev/null +++ b/estate_account/models/estate_property_offer.py @@ -0,0 +1,8 @@ +from odoo import models, fields + + +class EstatePropertyOffer(models.Model): + _inherit = ['estate.property.offer'] + _name = 'estate.property.offer' + + account_move_id = fields.Many2one('account.move', string="Invoice count") diff --git a/estate_account/static/description/icon.png b/estate_account/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f30bf4ac371cc7daa3d7262dc4c13cb39d8b0b14 GIT binary patch literal 87487 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 k{ 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 >_XK54sI`o_gGC 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 u0Hj#J6PgYTpuxXU&S)5nrUR + + + estate.property.view.form.inherit + estate.property + + + + + + + + + + estate.property.view.kanban.inherit + estate.property + + + + + Invoices + + + + \ No newline at end of file From 7f8c0f606f2978202577a40446295530c234e5b5 Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Tue, 24 Mar 2026 11:21:42 +0100 Subject: [PATCH 02/11] [IMP] estate: adding chatter to estate_property --- estate/__manifest__.py | 2 +- estate/models/estate_property.py | 2 ++ estate/views/estate_property_views.xml | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index e07c1a9917d..e32c2578ef6 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -3,7 +3,7 @@ 'description': 'Real Estate - Management', 'category': 'Sales/CRM', 'version': '1.0', - 'depends': ['base'], + 'depends': ['base', 'mail'], 'author': 'Odoo S.A.', 'license': 'LGPL-3', 'data': [ diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 1ec47f9b859..0b7f5d6f97f 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -11,6 +11,8 @@ class EstateProperty(models.Model): _order = 'id desc' + _inherit = ['mail.thread', 'mail.activity.mixin'] + name = fields.Char(required=True, string="Title") description = fields.Text(string="Description") postcode = fields.Char(required=True, string="Postcode") diff --git a/estate/views/estate_property_views.xml b/estate/views/estate_property_views.xml index e61b3e25d58..43c3045d472 100644 --- a/estate/views/estate_property_views.xml +++ b/estate/views/estate_property_views.xml @@ -123,6 +123,8 @@ + + From dfd5eee4747f9b2f6fb1cadaba7f769655e0129e Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Tue, 24 Mar 2026 11:27:49 +0100 Subject: [PATCH 03/11] [IMP] estate: tracking fields in estate_property --- estate/models/estate_property.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0b7f5d6f97f..0c8269dc265 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -13,11 +13,11 @@ class EstateProperty(models.Model): _inherit = ['mail.thread', 'mail.activity.mixin'] - name = fields.Char(required=True, string="Title") - description = fields.Text(string="Description") - postcode = fields.Char(required=True, string="Postcode") + name = fields.Char(required=True, string="Title", tracking=True) + description = fields.Text(string="Description", tracking=True) + postcode = fields.Char(required=True, string="Postcode", tracking=True) date_availability = fields.Date(required=True, default=lambda self: fields.Date.today() + relativedelta(months=+3), copy=False, string="Available From") - expected_price = fields.Float(required=True, string="Expected Price") + expected_price = fields.Float(required=True, string="Expected Price", tracking=True) selling_price = fields.Float(readonly=True, copy=False, string="Selling Price") bedrooms = fields.Integer(required=True, default=2, string="Bedrooms") living_area = fields.Integer(required=True, string="Living Area (sqm)", help="Living area with a ceiling height of minimum 4 feet") @@ -31,7 +31,7 @@ class EstateProperty(models.Model): total_area = fields.Integer(store=True, compute='_compute_total_area', string="Total Area (sqm)") state = fields.Selection([ ('new', "New"), ('offer_received', "Offer Received"), ('offer_accepted', "Offer Accepted"), ('sold', "Sold"), ('canceled', "Canceled") - ], string="State", default='new', required=True, readonly=True) + ], string="State", default='new', required=True, readonly=True, tracking=True) active = fields.Boolean(default=True) property_type_id = fields.Many2one('estate.property.type', string="Property Type") buyer_id = fields.Many2one('res.partner', string="Buyer", copy=False) From 92cc1790df44c0ec6a5d9ad84de5d027618f3b5c Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Tue, 24 Mar 2026 14:44:27 +0100 Subject: [PATCH 04/11] [CLN] estate_account: removing unnecessary inheritance --- estate_account/models/estate_property.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/estate_account/models/estate_property.py b/estate_account/models/estate_property.py index d61f33b0436..c555f217028 100644 --- a/estate_account/models/estate_property.py +++ b/estate_account/models/estate_property.py @@ -2,8 +2,7 @@ class EstateProperty(models.Model): - _inherit = ['estate.property'] - _name = 'estate.property' + _inherit = 'estate.property' invoice_count = fields.Integer(compute='_compute_invoice_count', string="Invoice Count") From 7ba0a24583c15f33407b2c2e3c4da9921d005fe1 Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Tue, 24 Mar 2026 17:09:30 +0100 Subject: [PATCH 05/11] [IMP] estate: fixing missing @api.depends --- estate/models/estate_property.py | 1 + 1 file changed, 1 insertion(+) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 0c8269dc265..5779fa844da 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -110,6 +110,7 @@ def compute_new_offer(self): if record.state == 'new': record.state = 'offer_received' + @api.depends('state') def _compute_available_for_offers(self): for record in self: record.available_for_offers = record.state in {'new', 'offer_received'} From 92eefca7369a21769f5f484195f7933209e8d2a7 Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Tue, 24 Mar 2026 17:19:58 +0100 Subject: [PATCH 06/11] [IMP] estate: adding tests --- estate/tests/__init__.py | 3 + estate/tests/common.py | 64 ++++++++++++++++++++++ estate/tests/test_estate_property.py | 39 +++++++++++++ estate/tests/test_estate_property_offer.py | 26 +++++++++ 4 files changed, 132 insertions(+) create mode 100644 estate/tests/__init__.py create mode 100644 estate/tests/common.py create mode 100644 estate/tests/test_estate_property.py create mode 100644 estate/tests/test_estate_property_offer.py diff --git a/estate/tests/__init__.py b/estate/tests/__init__.py new file mode 100644 index 00000000000..755d413272d --- /dev/null +++ b/estate/tests/__init__.py @@ -0,0 +1,3 @@ +from . import common +from . import test_estate_property +from . import test_estate_property_offer diff --git a/estate/tests/common.py b/estate/tests/common.py new file mode 100644 index 00000000000..48f6400ad57 --- /dev/null +++ b/estate/tests/common.py @@ -0,0 +1,64 @@ +import datetime + +from odoo.addons.base.tests.common import BaseCommon + + +class EstateTestCommon(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.partner_a = cls.env['res.partner'].create({ + 'name': 'Test Partner A', + }) + + cls.property_type_house = cls.env['estate.property.type'].create({ + 'name': 'House', + }) + cls.property_type_apartment = cls.env['estate.property.type'].create({ + 'name': 'Apartment', + }) + + def create_property(self, state, **kwargs): + vals = { + 'name': 'Property ' + str(datetime.datetime.now()), + 'postcode': '12345', + 'date_availability': datetime.datetime.now() + datetime.timedelta(days=7), + 'expected_price': 100000, + 'bedrooms': 2, + 'living_area': 120, + 'facades': 2, + 'property_type_id': self.property_type_house.id, + 'state': 'new' + } + + vals.update(kwargs) + + estate_property = self.env['estate.property'].create(vals) + + if state in {'offer_received', 'offer_accepted', 'sold', 'canceled'}: + offer_a = self.create_offer(estate_property, 5000, partner_id=self.partner.id, validity=7) + offer_b = self.create_offer(estate_property, 10000, partner_id=self.partner_a.id, validity=14) + + if state in {'offer_accepted', 'sold'}: + offer_a.action_mark_as_refused() + offer_b.action_mark_as_accepted() + + if state == 'sold': + estate_property.action_mark_as_sold() + elif state == 'canceled': + estate_property.action_mark_as_canceled() + + return estate_property + + def create_offer(self, estate_property, price_increase=0, **kwargs): + vals = { + 'price': estate_property.expected_price + price_increase, + 'partner_id': self.partner.id, + 'property_id': estate_property.id, + 'validity': 7, + } + + vals.update(kwargs) + + return self.env['estate.property.offer'].create(vals) diff --git a/estate/tests/test_estate_property.py b/estate/tests/test_estate_property.py new file mode 100644 index 00000000000..d03713061ff --- /dev/null +++ b/estate/tests/test_estate_property.py @@ -0,0 +1,39 @@ +from odoo.addons.estate.tests.common import EstateTestCommon +from odoo.exceptions import UserError +from odoo.tests import tagged, Form + + +@tagged('post_install', '-at_install') +class EstatePropertyTestCase(EstateTestCommon): + def test_sell_property_with_no_offers(self): + estate_property = self.create_property('offer_received') + + with self.assertRaises(UserError): + estate_property.action_mark_as_sold() + + def test_sell_property_on_state_new(self): + estate_property = self.create_property('new') + + with self.assertRaises(UserError): + estate_property.action_mark_as_sold() + + def test_garden_reactivity(self): + estate_property = self.create_property('new') + + with Form(estate_property) as form_estate_property: + form_estate_property.garden = True + form_estate_property.save() + self.assertEqual(estate_property.garden_area, 10) + self.assertEqual(estate_property.garden_orientation, 'north') + + form_estate_property.garden = False + form_estate_property.save() + self.assertEqual(estate_property.garden_area, 0) + self.assertEqual(estate_property.garden_orientation, False) + + form_estate_property.garden = True + form_estate_property.garden_area = 100 + form_estate_property.garden_orientation = 'south' + form_estate_property.save() + self.assertEqual(estate_property.garden_area, 100) + self.assertEqual(estate_property.garden_orientation, 'south') diff --git a/estate/tests/test_estate_property_offer.py b/estate/tests/test_estate_property_offer.py new file mode 100644 index 00000000000..6ee041b16c9 --- /dev/null +++ b/estate/tests/test_estate_property_offer.py @@ -0,0 +1,26 @@ +from odoo.addons.estate.tests.common import EstateTestCommon +from odoo.exceptions import UserError +from odoo.tests import tagged + + +@tagged('post_install', '-at_install') +class EstatePropertyOfferTestCase(EstateTestCommon): + def test_stop_offer_creation_on_sold_property(self): + estate_property = self.create_property('sold') + + with self.assertRaises(UserError): + self.create_offer(estate_property, 50000) + + def test_offer_price_too_low_compared_to_other_offers(self): + estate_property = self.create_property('offer_received') + + with self.assertRaises(UserError): + self.create_offer(estate_property, 0) + + def test_offer_price_too_low_compared_to_expected_price(self): + estate_property = self.create_property('new') + offer = self.create_offer(estate_property, -estate_property.expected_price / 2) + + with self.assertRaises(UserError): + offer.action_mark_as_accepted() + estate_property.action_mark_as_sold() From a682270a787894d5dc40c52d47e1c8aed31a4513 Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Wed, 25 Mar 2026 09:25:18 +0100 Subject: [PATCH 07/11] [CLN] estate: simplify EstateTestCommon create_* methods --- estate/tests/common.py | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/estate/tests/common.py b/estate/tests/common.py index 48f6400ad57..4842e46b199 100644 --- a/estate/tests/common.py +++ b/estate/tests/common.py @@ -20,7 +20,7 @@ def setUpClass(cls): }) def create_property(self, state, **kwargs): - vals = { + estate_property = self.env['estate.property'].create({ 'name': 'Property ' + str(datetime.datetime.now()), 'postcode': '12345', 'date_availability': datetime.datetime.now() + datetime.timedelta(days=7), @@ -29,36 +29,30 @@ def create_property(self, state, **kwargs): 'living_area': 120, 'facades': 2, 'property_type_id': self.property_type_house.id, - 'state': 'new' - } - - vals.update(kwargs) - - estate_property = self.env['estate.property'].create(vals) + 'state': 'new', + **kwargs, + }) if state in {'offer_received', 'offer_accepted', 'sold', 'canceled'}: offer_a = self.create_offer(estate_property, 5000, partner_id=self.partner.id, validity=7) offer_b = self.create_offer(estate_property, 10000, partner_id=self.partner_a.id, validity=14) - if state in {'offer_accepted', 'sold'}: - offer_a.action_mark_as_refused() - offer_b.action_mark_as_accepted() + if state in {'offer_accepted', 'sold'}: + offer_a.action_mark_as_refused() + offer_b.action_mark_as_accepted() - if state == 'sold': - estate_property.action_mark_as_sold() - elif state == 'canceled': - estate_property.action_mark_as_canceled() + if state == 'sold': + estate_property.action_mark_as_sold() + elif state == 'canceled': + estate_property.action_mark_as_canceled() return estate_property def create_offer(self, estate_property, price_increase=0, **kwargs): - vals = { + return self.env['estate.property.offer'].create({ 'price': estate_property.expected_price + price_increase, 'partner_id': self.partner.id, 'property_id': estate_property.id, 'validity': 7, - } - - vals.update(kwargs) - - return self.env['estate.property.offer'].create(vals) + **kwargs, + }) From d919a3d8fb7e1b3f7c96d6f7105362ea99487da6 Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Thu, 26 Mar 2026 09:39:46 +0100 Subject: [PATCH 08/11] [FIX] estate: count of offer_ids in _check_prices --- estate/models/estate_property.py | 2 +- estate/tests/common.py | 9 ++++++--- estate/tests/test_estate_property.py | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/estate/models/estate_property.py b/estate/models/estate_property.py index 5779fa844da..3494a4009e9 100644 --- a/estate/models/estate_property.py +++ b/estate/models/estate_property.py @@ -118,7 +118,7 @@ def _compute_available_for_offers(self): @api.constrains('selling_price', 'expected_price') def _check_prices(self): for record in self: - if len(record.offer_ids) > 0 and float_compare(record.selling_price, record.expected_price * 0.9, 2) == -1: + if len(record.offer_ids.filtered(lambda x: x.status == 'accepted')) > 0 and float_compare(record.selling_price, record.expected_price * 0.9, 2) == -1: raise UserError(self.env._("The selling price must be at least 90% of the expected price")) @api.ondelete(at_uninstall=False) diff --git a/estate/tests/common.py b/estate/tests/common.py index 4842e46b199..9c79a8770e8 100644 --- a/estate/tests/common.py +++ b/estate/tests/common.py @@ -19,8 +19,8 @@ def setUpClass(cls): 'name': 'Apartment', }) - def create_property(self, state, **kwargs): - estate_property = self.env['estate.property'].create({ + def get_create_property_kwargs(self, **kwargs): + return { 'name': 'Property ' + str(datetime.datetime.now()), 'postcode': '12345', 'date_availability': datetime.datetime.now() + datetime.timedelta(days=7), @@ -31,7 +31,10 @@ def create_property(self, state, **kwargs): 'property_type_id': self.property_type_house.id, 'state': 'new', **kwargs, - }) + } + + def create_property(self, state, **kwargs): + estate_property = self.env['estate.property'].create(self.get_create_property_kwargs(**kwargs)) if state in {'offer_received', 'offer_accepted', 'sold', 'canceled'}: offer_a = self.create_offer(estate_property, 5000, partner_id=self.partner.id, validity=7) diff --git a/estate/tests/test_estate_property.py b/estate/tests/test_estate_property.py index d03713061ff..ca78dbc6c35 100644 --- a/estate/tests/test_estate_property.py +++ b/estate/tests/test_estate_property.py @@ -1,3 +1,4 @@ +from odoo import Command from odoo.addons.estate.tests.common import EstateTestCommon from odoo.exceptions import UserError from odoo.tests import tagged, Form @@ -37,3 +38,16 @@ def test_garden_reactivity(self): form_estate_property.save() self.assertEqual(estate_property.garden_area, 100) self.assertEqual(estate_property.garden_orientation, 'south') + + def test_create_property_with_offers(self): + vals = self.get_create_property_kwargs() + + vals['offer_ids'] = [ + Command.create({ + 'price': vals['expected_price'] + 5000, + 'partner_id': self.partner.id, + 'validity': 7, + }), + ] + + self.env['estate.property'].create(vals) \ No newline at end of file From dc2ea9ad8cad2663673b4960e9497d81079d4f27 Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Thu, 26 Mar 2026 09:41:06 +0100 Subject: [PATCH 09/11] [IMP] estate: master and demo data --- estate/__manifest__.py | 7 +++ estate/data/estate_property_type_data.xml | 19 +++++++ estate/demo/estate_property_demo.xml | 58 ++++++++++++++++++++++ estate/demo/estate_property_offer_demo.xml | 21 ++++++++ estate/demo/estate_property_type_demo.xml | 11 ++++ estate/demo/estate_workflow_demo.xml | 16 ++++++ 6 files changed, 132 insertions(+) create mode 100644 estate/data/estate_property_type_data.xml create mode 100644 estate/demo/estate_property_demo.xml create mode 100644 estate/demo/estate_property_offer_demo.xml create mode 100644 estate/demo/estate_property_type_demo.xml create mode 100644 estate/demo/estate_workflow_demo.xml diff --git a/estate/__manifest__.py b/estate/__manifest__.py index e32c2578ef6..fd36d9347bc 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -14,6 +14,13 @@ 'views/estate_property_tag_views.xml', 'views/res_users_views.xml', 'views/estate_menu_views.xml', + 'data/estate_property_type_data.xml', + ], + 'demo': [ + 'demo/estate_property_type_demo.xml', + 'demo/estate_property_demo.xml', + 'demo/estate_property_offer_demo.xml', + 'demo/estate_workflow_demo.xml', ], 'assets': {}, 'application': True, diff --git a/estate/data/estate_property_type_data.xml b/estate/data/estate_property_type_data.xml new file mode 100644 index 00000000000..45165faad99 --- /dev/null +++ b/estate/data/estate_property_type_data.xml @@ -0,0 +1,19 @@ + + + + 1 + Residential + + + 2 + Commercial + + + 3 + Industrial + + + 4 + Land + + \ No newline at end of file diff --git a/estate/demo/estate_property_demo.xml b/estate/demo/estate_property_demo.xml new file mode 100644 index 00000000000..c812e3e079c --- /dev/null +++ b/estate/demo/estate_property_demo.xml @@ -0,0 +1,58 @@ + + + + Big Villa + new + A nice and big villa + 12345 + 2020-02-02 + 1600000 + 0 + 6 + 100 + 4 + True + True + 100000 + south + + + + Trailer home + canceled + Home in a trailer park + 54321 + 1970-01-01 + 100000 + 120000 + 1 + 10 + 4 + False + False + + + + The White House + new + A spot in the oval office + 99999 + 2027-01-01 + 100000000 + 42 + 10000 + 12 + True + True + 1000000 + south + + + + \ No newline at end of file diff --git a/estate/demo/estate_property_offer_demo.xml b/estate/demo/estate_property_offer_demo.xml new file mode 100644 index 00000000000..29f22377180 --- /dev/null +++ b/estate/demo/estate_property_offer_demo.xml @@ -0,0 +1,21 @@ + + + + + + 10000 + + + + + + 1500000 + + + + + + 1500001 + + + \ No newline at end of file diff --git a/estate/demo/estate_property_type_demo.xml b/estate/demo/estate_property_type_demo.xml new file mode 100644 index 00000000000..5220dbc5cb6 --- /dev/null +++ b/estate/demo/estate_property_type_demo.xml @@ -0,0 +1,11 @@ + + + + 10 + House + + + 11 + Apartment + + \ No newline at end of file diff --git a/estate/demo/estate_workflow_demo.xml b/estate/demo/estate_workflow_demo.xml new file mode 100644 index 00000000000..e4b8f9a6a7f --- /dev/null +++ b/estate/demo/estate_workflow_demo.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file From 05e5ae4586f3dfb9a5c1238ad4016ac0545ef8dd Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Fri, 27 Mar 2026 09:49:31 +0100 Subject: [PATCH 10/11] [IMP] estate: version --- estate/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/estate/__manifest__.py b/estate/__manifest__.py index fd36d9347bc..bdf6ecb69ec 100644 --- a/estate/__manifest__.py +++ b/estate/__manifest__.py @@ -2,7 +2,7 @@ 'name': 'Real Estate', 'description': 'Real Estate - Management', 'category': 'Sales/CRM', - 'version': '1.0', + 'version': '1.1', 'depends': ['base', 'mail'], 'author': 'Odoo S.A.', 'license': 'LGPL-3', From 6b0865b9ad66b62d15a36647cb505d23b2daff2c Mon Sep 17 00:00:00 2001 From: jucop-odoo Date: Fri, 27 Mar 2026 09:53:30 +0100 Subject: [PATCH 11/11] [LINT] awesome_dashboard,awesome_kanban,estate: linting --- awesome_dashboard/controllers/__init__.py | 2 +- awesome_dashboard/controllers/controllers.py | 3 +-- .../components/pie_chart/pie_chart.js | 18 +++++++------- .../awesome_dashboard_dashboard_views.xml | 12 +++++----- .../views/awesome_dashboard_views.xml | 8 +++---- awesome_kanban/views/views.xml | 24 +++++++++---------- estate/tests/test_estate_property.py | 2 +- 7 files changed, 34 insertions(+), 35 deletions(-) diff --git a/awesome_dashboard/controllers/__init__.py b/awesome_dashboard/controllers/__init__.py index 457bae27e11..b0f26a9a602 100644 --- a/awesome_dashboard/controllers/__init__.py +++ b/awesome_dashboard/controllers/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import controllers \ No newline at end of file +from . import controllers diff --git a/awesome_dashboard/controllers/controllers.py b/awesome_dashboard/controllers/controllers.py index 05977d3bd7f..d6532913243 100644 --- a/awesome_dashboard/controllers/controllers.py +++ b/awesome_dashboard/controllers/controllers.py @@ -4,10 +4,10 @@ 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='jsonrpc', auth='user') def get_statistics(self): @@ -33,4 +33,3 @@ def get_statistics(self): }, 'total_amount': random.randint(100, 1000) } - diff --git a/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.js b/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.js index a7a20ca4a59..e9f69ebe7bb 100644 --- a/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.js +++ b/awesome_dashboard/static/src/dashboard/components/pie_chart/pie_chart.js @@ -66,16 +66,16 @@ export class PieChart extends Component { events: ['click'], }, plugins: [{ - id: 'customEventCatcher', - beforeEvent: (chart, args) => { - if (args?.event.type === 'click') { - const [activeElement] = chart.getElementsAtEventForMode(args.event, 'nearest', {intersect: true}, true); - const index = activeElement.index; + id: 'customEventCatcher', + beforeEvent: (chart, args) => { + if (args?.event.type === 'click') { + const [activeElement] = chart.getElementsAtEventForMode(args.event, 'nearest', {intersect: true}, true); + const index = activeElement.index; - if (this.props.onClick) { - this.props.onClick(this.action, Object.keys(this.props.data)[index]); - } - } + if (this.props.onClick) { + this.props.onClick(this.action, Object.keys(this.props.data)[index]); + } + } } }], } diff --git a/awesome_dashboard/views/awesome_dashboard_dashboard_views.xml b/awesome_dashboard/views/awesome_dashboard_dashboard_views.xml index bb0b9d8292a..a5f30479ca6 100644 --- a/awesome_dashboard/views/awesome_dashboard_dashboard_views.xml +++ b/awesome_dashboard/views/awesome_dashboard_dashboard_views.xml @@ -1,8 +1,8 @@ - - - Dashboard - awesome_dashboard.dashboard - - + + + Dashboard + awesome_dashboard.dashboard + + diff --git a/awesome_dashboard/views/awesome_dashboard_views.xml b/awesome_dashboard/views/awesome_dashboard_views.xml index e45dd6c9da4..c7ea22cb334 100644 --- a/awesome_dashboard/views/awesome_dashboard_views.xml +++ b/awesome_dashboard/views/awesome_dashboard_views.xml @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/awesome_kanban/views/views.xml b/awesome_kanban/views/views.xml index 548b2907b6e..48bce63344d 100644 --- a/awesome_kanban/views/views.xml +++ b/awesome_kanban/views/views.xml @@ -1,15 +1,15 @@ - - - crm.lead.kanban.lead.awesome_gallery - crm.lead - - - - awesome_kanban - - - - + + + crm.lead.kanban.lead.awesome_gallery + crm.lead + + + + awesome_kanban + + + + diff --git a/estate/tests/test_estate_property.py b/estate/tests/test_estate_property.py index ca78dbc6c35..7e5c66a816b 100644 --- a/estate/tests/test_estate_property.py +++ b/estate/tests/test_estate_property.py @@ -50,4 +50,4 @@ def test_create_property_with_offers(self): }), ] - self.env['estate.property'].create(vals) \ No newline at end of file + self.env['estate.property'].create(vals)