Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
a54dcce
[ADD] Define an empty "Estate" module
THDES-odoo Jan 19, 2026
0cb3bd2
[CLN] Add missing license
THDES-odoo Jan 19, 2026
2869fe7
[IMP] Define estate property model
THDES-odoo Jan 19, 2026
a172ae5
[IMP] Add fields to EstateProperty
THDES-odoo Jan 19, 2026
d1024dc
[IMP] Add Orientation selection to EstateProperty model
THDES-odoo Jan 20, 2026
abd84f9
[CLN] Solve Spacing issues
THDES-odoo Jan 20, 2026
8f66f31
[IMP] Add security rules for EstateProperty model
THDES-odoo Jan 20, 2026
5de3951
[CLN] Fix styling issues
THDES-odoo Jan 20, 2026
17c8281
[IMP] Display a 3-level menu for the Real Estate app
THDES-odoo Jan 20, 2026
432f849
[IMP] Set readonly and non copied field in EstateProperty
THDES-odoo Jan 20, 2026
e9ee754
[IMP] Add default values for EstateProperty fields
THDES-odoo Jan 20, 2026
7925fa1
[IMP] Add active and state fields to EstateProperty
THDES-odoo Jan 20, 2026
7ed9bc6
[CLN] Fix style
THDES-odoo Jan 20, 2026
209216c
[IMP] Configure list view of EstateProperty
THDES-odoo Jan 20, 2026
f500b45
[IMP] Configure form view of EstateProperty
THDES-odoo Jan 20, 2026
e2fda31
[CLN] Adopt naming conventions
THDES-odoo Jan 21, 2026
2b7febc
[IMP] Add search feature to Properties view
THDES-odoo Jan 21, 2026
015c7c7
[IMP] Create Property Type
THDES-odoo Jan 21, 2026
f64e80a
[IMP] Link Property type to Property
THDES-odoo Jan 21, 2026
81048be
[CLN] Fix style once again
THDES-odoo Jan 21, 2026
3ebe471
[IMP] Add Salesperson and Buyer in EstateProperty
THDES-odoo Jan 21, 2026
6eb5ff8
[IMP] Define PropertyTag
THDES-odoo Jan 21, 2026
544cced
[CLN] Move parameters on their own line when too long
THDES-odoo Jan 21, 2026
4323150
[IMP] Link tags to property offers
THDES-odoo Jan 21, 2026
31e97bd
[IMP] Create offers and links them to properties
THDES-odoo Jan 21, 2026
bd02b3c
[IMP] Compute total area from living + garden area
THDES-odoo Jan 21, 2026
8eb9b2b
[IMP] Compute best offer
THDES-odoo Jan 21, 2026
9432698
[IMP] Compute offer deadline from validity
THDES-odoo Jan 21, 2026
86c8830
[IMP] Compute offer validity from deadline
THDES-odoo Jan 21, 2026
edc57f0
[IMP] Update garden area and orientation garden is (un)ticked
THDES-odoo Jan 21, 2026
2d1d4a6
[IMP] Display offer status in form header
THDES-odoo Jan 22, 2026
a795ef3
[IMP] Add buttons to sell and cancel properties
THDES-odoo Jan 22, 2026
cabf687
[IMP] Accept and refuse offers from list view
THDES-odoo Jan 22, 2026
78a1090
[IMP] Set selling price and buyer when an offer is accepted
THDES-odoo Jan 22, 2026
51af4e3
[IMP] Refuse other offers when an offer is accepted
THDES-odoo Jan 22, 2026
c021e8f
[IMP] Synchronise property state with offers
THDES-odoo Jan 22, 2026
1233759
[CLN] Add string to offer action buttons
THDES-odoo Jan 22, 2026
4d5a374
[IMP] Add database constaint of fields
THDES-odoo Jan 22, 2026
40e3e24
[IMP] Constrain selling price to be >90% of expected price
THDES-odoo Jan 22, 2026
e53bc72
[FIX] Update property attributes when refusing an accepted offer
THDES-odoo Jan 22, 2026
86bc031
[FIX] Change attribute update order on offer refuse
THDES-odoo Jan 22, 2026
7548c2c
[IMP] Display matching properties in property type form
THDES-odoo Jan 22, 2026
3123c25
[IMP] Hide Canceled state from statusbar
THDES-odoo Jan 22, 2026
891028f
[IMP] Order views
THDES-odoo Jan 22, 2026
cb8033d
[IMP] Add colors to tags
THDES-odoo Jan 22, 2026
ceea61e
[IMP] Hide buttons when they should not be used
THDES-odoo Jan 22, 2026
a73f049
[CLN] Inline content of a function
THDES-odoo Jan 22, 2026
68f5bcd
[IMP] Make some list views editable
THDES-odoo Jan 22, 2026
bc8cfc0
[IMP] Hide availability by default in property list view
THDES-odoo Jan 22, 2026
1fc06bf
[IMP] Decorate list views
THDES-odoo Jan 23, 2026
6de573e
[CLN] Fix style
THDES-odoo Jan 23, 2026
30dd266
[IMP] Filter properties on availability by default
THDES-odoo Jan 23, 2026
fd3e31c
[IMP] Filter on living area greater than entered value
THDES-odoo Jan 23, 2026
06683b1
[IMP] Implement Offer smart button on property type
THDES-odoo Jan 23, 2026
7014b47
[IMP] Prevent deletion of properties
THDES-odoo Jan 26, 2026
69f19b9
[REF] Update property state when an offer is received
THDES-odoo Jan 26, 2026
568965f
[CLN] Fix typos and style
THDES-odoo Jan 26, 2026
9351f9a
[IMP] Prevent creation of offers below the best offer
THDES-odoo Jan 26, 2026
b5d6e74
[IMP] Display user's properties in form view
THDES-odoo Jan 26, 2026
0802f61
[IMP] create empty link module between estate and account
THDES-odoo Jan 26, 2026
3ec48db
[FIX] Correct res_user names
THDES-odoo Jan 26, 2026
451c789
[IMP] inherit estate_property in estate_account
THDES-odoo Jan 26, 2026
53cb6c4
[IMP] Create an invoice when a property is sold
THDES-odoo Jan 26, 2026
05f00f9
[IMP] Add basic kanban view on properties
THDES-odoo Jan 26, 2026
4547d55
[IMP] estate: Make Kanban view of properties useable
THDES-odoo Jan 26, 2026
1eb520f
[CLN] Follow coding guideline in xml
THDES-odoo Jan 27, 2026
8605554
[CLN] Follow model attributes conventions
THDES-odoo Jan 27, 2026
6fb997a
[IMP] Implement chapter 1: 1-5 of web framework
THDES-odoo Jan 27, 2026
f0571da
[IMP] Add a bit of styling
THDES-odoo Jan 28, 2026
1dcf4a5
[IMP] Display the sum of counters
THDES-odoo Jan 28, 2026
49f56ff
[IMP] Add todo list in playground
THDES-odoo Jan 28, 2026
ad75ad7
[IMP] Use slots and toggle button in Card
THDES-odoo Jan 28, 2026
d56be0c
[IMP] Add navigation buttons to dashboard
THDES-odoo Jan 28, 2026
b0c79be
[CLN] Fix format with prettier
THDES-odoo Jan 28, 2026
4e4773c
[IMP] Define DashboardItem
THDES-odoo Jan 28, 2026
e398922
[IMP] Fill dashboard with response of rpc
THDES-odoo Jan 28, 2026
ca446b1
[IMP] Memoize rpc results
THDES-odoo Jan 28, 2026
b9b15eb
[IMP] Build a reactive pie chart
THDES-odoo Jan 28, 2026
f639f10
[MOV] awesome_dashboard: Move dashboard assets in dedicated folder
THDES-odoo Jan 29, 2026
f1052e9
[PERF] Lazy load the AwesomeDashboard
THDES-odoo Jan 29, 2026
c70c039
[CLN] Remove useless empty lines
THDES-odoo Jan 29, 2026
22cd15c
[IMP] awesome_dashboard: Make dashboard generic
THDES-odoo Jan 29, 2026
d0be616
[IMP] awesome_dashboard: Make the dashboard extensible
THDES-odoo Jan 29, 2026
673268f
[IMP] awesome_dashboard: Add and remove dashboard items
THDES-odoo Jan 29, 2026
0042e71
[CLN] estate: Apply formatting and fix import order
THDES-odoo Jan 30, 2026
044fe15
[IMP] estate: Prevent non relevant actions
THDES-odoo Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions awesome_dashboard/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@
'assets': {
'web.assets_backend': [
'awesome_dashboard/static/src/**/*',
('remove', 'awesome_dashboard/static/src/dashboard/*'),
],
'awesome_dashboard.dashboard': [
'awesome_dashboard/static/src/dashboard/**/*',
]
},
'license': 'AGPL-3'
}
8 changes: 0 additions & 8 deletions awesome_dashboard/static/src/dashboard.js

This file was deleted.

8 changes: 0 additions & 8 deletions awesome_dashboard/static/src/dashboard.xml

This file was deleted.

89 changes: 89 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Component, useState } from "@odoo/owl";
import { CheckBox } from "@web/core/checkbox/checkbox";
import { Dialog } from "@web/core/dialog/dialog";
import { browser } from "@web/core/browser/browser";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Layout } from "@web/search/layout";
import { DashboardItem } from "./dashboard_item/dashboard_item";
import { NumberCard } from "./number_card/number_card";
import { PieChartCard } from "./pie_chart_card/pie_chart_card";

class AwesomeDashboard extends Component {
static template = "awesome_dashboard.AwesomeDashboard";
static components = { Layout, DashboardItem, NumberCard, PieChartCard };

setup() {
this.items = registry.category("awesome_dashboard").getAll();
this.action = useService("action");
this.stats = useState(useService("awesome_dashboard.statistics"));
this.dialog = useService("dialog");
this.hiddenItemIds = useState(
browser.localStorage.getItem("hiddenDashboardItemIds")?.split(",") || [],
);
}

openCustomers() {
this.action.doAction("base.action_partner_form");
}

async openLeads() {
this.action.doAction({
type: "ir.actions.act_window",
name: "All Leads",
res_model: "crm.lead",
views: [
[false, "list"],
[false, "form"],
],
});
}

openConfig() {
this.dialog.add(ConfigDialog, {
items: this.items,
hiddenItemIds: this.hiddenItemIds,
onUpdateConfig: this.updateConfig.bind(this),
});
}

updateConfig(newHiddenItemIds) {
this.hiddenItemIds = newHiddenItemIds;
}
}

class ConfigDialog extends Component {
static template = "awesome_dashboard.ConfigDialog";
static components = { Dialog, CheckBox };
static props = {
close: Function,
items: { type: Array, elements: Object },
hiddenItemIds: { type: Array, elements: String },
onUpdateConfig: { type: Function, optional: true },
};

setup() {
this.items = useState(
this.props.items.map((item) => ({
...item,
displayed: !this.props.hiddenItemIds.includes(item.id),
})),
);
}

onChange(checked, itemToChange) {
itemToChange.displayed = checked;
const newHiddenItemIds = this.items
.filter((item) => !item.displayed)
.map((item) => item.id);

browser.localStorage.setItem("hiddenDashboardItemIds", newHiddenItemIds);
this.props.onUpdateConfig?.(newHiddenItemIds);
}

done() {
this.props.close();
}
}

registry.category("lazy_components").add("AwesomeDashboard", AwesomeDashboard);
3 changes: 3 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.o_dashboard {
background-color: gray;
}
40 changes: 40 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.AwesomeDashboard">
<Layout className="'o_dashboard h-100'" display="{controlPanel: {}}">
<t t-set-slot="layout-buttons">
<button class="btn btn-primary" t-on-click="openCustomers">Customers</button>
<button class="btn btn-primary" t-on-click="openLeads">Leads</button>
</t>
<t t-set-slot="control-panel-additional-actions">
<button t-on-click="openConfig" class="btn p-0 ms-1 border-0">
<i class="fa fa-cog"/>
</button>
</t>
<t t-if="stats.isReady">
<t t-foreach="items" t-as="item" t-key="item.id">
<DashboardItem size="item.size" t-if="!hiddenItemIds.includes(item.id)">
<t t-set="itemProp" t-value="item.props ? item.props(stats) : {'data': stats}"/>
<t t-component="item.Component" t-props="itemProp"/>
</DashboardItem>
</t>
</t>
</Layout>
</t>

<t t-name="awesome_dashboard.ConfigDialog">
<Dialog title="'Dashboard items configuration'">
<t t-foreach="items" t-as="item" t-key="item.id">
<CheckBox value="item.displayed" onChange.bind="(ev) => this.onChange(ev, item)">
<t t-esc="item.description"/>
</CheckBox>
</t>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="done">
Done
</button>
</t>
</Dialog>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Component } from "@odoo/owl";

export class DashboardItem extends Component {
static template = "awesome_dashboard.DashboardItem";
static props = {
size: { type: Number, optional: true },
slots: {
type: Object,
shape: { default: Object },
},
};

static defaultProps = {
size: 1,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.DashboardItem">
<div class="card d-inline-block m-2" t-attf-style="width: {{18*props.size}}rem;">
<div class="card-body">
<t t-slot="default"/>
</div>
</div>
</t>

</templates>
65 changes: 65 additions & 0 deletions awesome_dashboard/static/src/dashboard/dashboard_items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { registry } from "@web/core/registry";
import { NumberCard } from "./number_card/number_card";
import { PieChartCard } from "./pie_chart_card/pie_chart_card";

const items = [
{
id: "nb_new_orders",
description: "Number of new orders",
Component: NumberCard,
props: (data) => ({
title: "Number of new orders this month",
value: data.nb_new_orders,
}),
},
{
id: "total_amount",
description: "Total amount of new orders",
Component: NumberCard,
props: (data) => ({
title: "Total amount of new orders this month",
value: data.total_amount,
}),
},
{
id: "average_quantity",
description: "Average amount of T-shirt",
Component: NumberCard,
props: (data) => ({
title: "Average amount of T-shirt per order this month",
value: data.average_quantity,
}),
},
{
id: "nb_cancelled_orders",
description: "Number of canceled orders",
Component: NumberCard,
props: (data) => ({
title: "Number of canceled orders this month",
value: data.nb_cancelled_orders,
}),
},
{
id: "average_time",
description: "Average order time",
Component: NumberCard,
props: (data) => ({
title: "Average time for an order to go from 'new' to 'sent' or 'canceled'",
value: data.average_time,
}),
},
{
id: "orders_by_size",
description: "Orders by size",
Component: PieChartCard,
size: 2,
props: (data) => ({
title: "T-shirt orders by size",
stats: data.orders_by_size,
}),
},
];

items.forEach((item) => {
registry.category("awesome_dashboard").add(item.id, item);
});
Original file line number Diff line number Diff line change
@@ -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,
};
}
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard/number_card/number_card.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.NumberCard">
<b><t t-esc="props.title"/></b><br/>
<div style="color: green; font-size: 24pt; text-align: center;">
<t t-esc="props.value"/>
</div>
</t>

</templates>
28 changes: 28 additions & 0 deletions awesome_dashboard/static/src/dashboard/pie_chart/pie_chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Component, onWillStart, onWillUnmount, useEffect, useRef } from "@odoo/owl";
import { loadJS } from "@web/core/assets";

export class PieChart extends Component {
static template = "awesome_dashboard.PieChart";
static props = {
data: Object,
};

setup() {
this.canvasRef = useRef("canvas");
onWillStart(() => loadJS("/web/static/lib/Chart/Chart.js"));
useEffect(() => this.renderChart());
onWillUnmount(() => this.chart?.destroy());
}

renderChart() {
this.chart?.destroy();
const ctx = this.canvasRef.el;
this.chart = new Chart(ctx, {
type: "doughnut",
data: {
datasets: [{ data: Object.values(this.props.data) }],
labels: Object.keys(this.props.data),
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.PieChart">
<canvas t-ref="canvas"/>
</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component } from "@odoo/owl";
import { PieChart } from "../pie_chart/pie_chart";

export class PieChartCard extends Component {
static template = "awesome_dashboard.PieChartCard";
static components = { PieChart };
static props = {
title: String,
stats: Object,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.PieChartCard">
<b><t t-esc="props.title"/></b><br/>
<PieChart data="props.stats"/>
</t>

</templates>
10 changes: 10 additions & 0 deletions awesome_dashboard/static/src/dashboard_action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Component } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { LazyComponent } from "@web/core/assets";

class DashboardLoader extends Component {
static template = "awesome_dashboard.DashboardLoader";
static components = { LazyComponent };
}

registry.category("actions").add("awesome_dashboard.dashboard", DashboardLoader);
7 changes: 7 additions & 0 deletions awesome_dashboard/static/src/dashboard_action.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<templates xml:space="preserve">

<t t-name="awesome_dashboard.DashboardLoader">
<LazyComponent bundle="'awesome_dashboard.dashboard'" Component="'AwesomeDashboard'"/>
</t>

</templates>
19 changes: 19 additions & 0 deletions awesome_dashboard/static/src/statistics_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { reactive } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { rpc } from "@web/core/network/rpc";

const statisticsService = {
start() {
const statistics = reactive({ isReady: false });
async function loadStatistics() {
const updates = await rpc("/awesome_dashboard/statistics");
Object.assign(statistics, updates, { isReady: true });
}

setInterval(loadStatistics, 10000);
loadStatistics();
return statistics;
},
};

registry.category("services").add("awesome_dashboard.statistics", statisticsService);
20 changes: 20 additions & 0 deletions awesome_owl/static/src/card/card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component, useState } from "@odoo/owl";

export class Card extends Component {
static template = "awesome_owl.card";
static props = {
title: String,
slots: {
type: Object,
shape: {default: Object}
}
}

setup() {
this.state = useState({ open: true });
}

toggleOpen() {
this.state.open = !this.state.open;
}
}
Loading