Skip to content

[ADD] estate: implement core real estate property management module #758

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 24 commits into
base: 18.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8fdb212
[ADD] estate: implement core real estate property management module
shib-odoo May 7, 2025
e88c3e6
[IMP] estate: enforce data integrity with constraints and fix field p…
shib-odoo May 8, 2025
f677441
[IMP] estate: fix stat button context and enhance property type UI
shib-odoo May 9, 2025
4f85ce4
[FIX] estate: apply code review suggestions and improve logic consist…
shib-odoo May 12, 2025
438c1ab
[FIX] estate: github checklist style changes
shib-odoo May 12, 2025
a82f1f8
[FIX] estate: git style checklist issues fixed
shib-odoo May 12, 2025
eb6dbab
[FIX] estate: git style checklist issues fixed
shib-odoo May 12, 2025
404ce4c
[ADD] estate: added new estate_account module and demo property data …
shib-odoo May 14, 2025
38832c5
[ADD] estate: web-controller , report pdf , unit test
shib-odoo May 20, 2025
315ff41
[IMP] awesome_owl: improve Card and TodoList components interactivity
shib-odoo May 22, 2025
eaa545f
[IMP] awesome_owl: improve Card and TodoList components interactivity
shib-odoo May 22, 2025
976b95e
[IMP] awesome_owl: improve Card and TodoList components interactivity
shib-odoo May 23, 2025
5bdea03
[IMP] estate: refactor offer creation logic for better validation
shib-odoo May 23, 2025
97a447b
[FIX] estate: validate strictly positive offer price in create method
shib-odoo May 23, 2025
b8abf07
[FIX] estate: validate strictly positive offer price in create method
shib-odoo May 23, 2025
1c075f9
[FIX] estate: validate strictly positive offer price in create method
shib-odoo May 23, 2025
e57afa8
[FIX] estate: validate strictly positive offer price in create method
shib-odoo May 23, 2025
f06043a
[IMP] awesome_dashboard: add customizable settings dialog and enhance UI
shib-odoo May 26, 2025
7b1a4a1
[IMP] awesome_dashboard: add customizable settings dialog and enhance UI
shib-odoo May 26, 2025
aa4da65
[IMP] sale, account: show pricelist price (book price) on sales and i…
shib-odoo May 27, 2025
a727be5
[FIX] sale, account: fix styling issue in __init__.py file
shib-odoo May 27, 2025
646c280
[ADD] add_pricelist_price: unit tests for book price computation
shib-odoo May 28, 2025
42fcb40
[FIX] add_pricelist_price: fix styling issue
shib-odoo May 28, 2025
d98e685
[FIX] add_pricelist_price: fix styling issue
shib-odoo May 28, 2025
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
1 change: 1 addition & 0 deletions add_pricelist_price/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
13 changes: 13 additions & 0 deletions add_pricelist_price/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "Add Pricelist Price",
"description": """
Add Pricelist Price
""",
"depends": ["sale_management"],
"data": [
"views/account_move_line.xml",
"views/sale_order_line.xml",
],
"installable": True,
"license": "LGPL-3",
}
2 changes: 2 additions & 0 deletions add_pricelist_price/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import account_move_line
from . import sale_order_line
21 changes: 21 additions & 0 deletions add_pricelist_price/models/account_move_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from odoo import api, fields, models


class AccountMoveLine(models.Model):
_inherit = "account.move.line"

book_price = fields.Float(string="Book Price", compute="_compute_book_price")

@api.depends("product_id", "quantity", "sale_line_ids.order_id.pricelist_id")
def _compute_book_price(self):
for line in self:
sale_order = line.sale_line_ids.order_id
pricelist = sale_order.pricelist_id if sale_order else None
if pricelist:
line.book_price = pricelist._get_product_price(
line.product_id,
line.quantity,
line.product_uom_id,
)
else:
line.book_price = line.product_id.lst_price
22 changes: 22 additions & 0 deletions add_pricelist_price/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from odoo import api, fields, models


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

book_price = fields.Float(string="Book Price", compute="_compute_book_price")

@api.depends("product_id", "product_uom_qty", "order_id.pricelist_id")
def _compute_book_price(self):
for line in self:
if line.product_id and line.order_id.pricelist_id:
pricelist = line.order_id.pricelist_id
product = line.product_id
price = pricelist._get_product_price(
product,
quantity=line.product_uom_qty,
uom=line.product_uom,
)
line.book_price = price if price else line.product_id.lst_price
else:
line.book_price = line.product_id.lst_price
1 change: 1 addition & 0 deletions add_pricelist_price/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_book_price
118 changes: 118 additions & 0 deletions add_pricelist_price/tests/test_book_price.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
from odoo.tests.common import TransactionCase
from odoo.fields import Command


class TestBookPrice(TransactionCase):

def setUp(self):
super().setup()
Product = self.env['product.product']
PriceList = self.env['product.pricelist']

self.product = Product.create({
'name': 'Test Book',
'list_price': 100.0,
})

self.pricelist = PriceList.create({
'name': 'Test Pricelist',
'item_ids': [(Command.create({
'applied_on': '0_product_variant',
'product_id': self.product.id,
'compute_price': 'formula',
'base': 'list_price',
'price_discount': 20.0
}))],
})
self.partner = self.env['res.partner'].create({
'name': 'Test Partner',
})

def test_book_price_on_sale_order_line(self):
sale_order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'pricelist_id': self.pricelist.id,
'order_line': [Command.create({
'product_id': self.product.id,
'product_uom_qty': 1,
'price_unit': 100.0
})]
})

line = sale_order.order_line[0]
line._compute_price()
self.assertAlmostEqual(
line.book_price,
80.0,
places=2,
msg="Book price should be 80.0 after applying 20% discount on list price of 100.0"
)

def test_book_price_fallback_to_list_price(self):
sale_order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'order_line': [Command.create({
'product_id': self.product.id,
'product_umo_qty': 1,
'price_unit': 100.0,
})]
})
line = sale_order.order_line[0]
line._compute_price()
self.assertEqual(
line.book_price,
self.product.list_price,
"Book price should fallback to list price when no pricelist is applied"
)

def test_book_price_on_customer_invoice_line(self):
sale_order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'pricelist_id': self.pricelist.id,
'order_line': [Command.create({
'product_id': self.product.id,
'product_uom_qty': 2,
'price_unit': 200.0,
})]
})
sale_order.action_confirm()
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': self.partner.id,
'invoice_line_ids': [Command.create({
'product_id': self.product.id,
'invoice_line_ids': [Command.create({
'product_id': self.product.id,
'quantity': 2,
'price_unit': 200.0,
'sale_line_ids': [(6, 0, [sale_order.order_line.id])],
})]
})]
})
line = invoice.invoice_line_ids[0]
line._compute_book_price()
self.assertAlmostEqual(
line.book_price,
80.0,
places=2,
msg="Invoice line book price should come from the sale order pricelist"
)

def test_book_price_not_show_on_non_out_invoice(self):
invoice = self.env['account.move'].create({
'move_type': 'out_refund',
'partner_id': self.partner.id,
'invoice_line_ids': [(0, 0, {
'product_id': self.product.id,
'quantity': 1,
'price_unit': 100.0,
})],
})

line = invoice.invoice_line_ids[0]
line._compute_book_price()
self.assertEqual(
line.book_price,
self.product.list_price,
"Book price should be computed but hidden in UI for non-customer-invoice types"
)
13 changes: 13 additions & 0 deletions add_pricelist_price/views/account_move_line.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_invoice_form_inherit_book_price" model="ir.ui.view">
<field name="name">account.move.line.form.book.price</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='invoice_line_ids']/list/field[@name='quantity']" position="before">
<field name="book_price" readonly="1" column_invisible="parent.move_type != 'out_invoice'" />
</xpath>
</field>
</record>
</odoo>
13 changes: 13 additions & 0 deletions add_pricelist_price/views/sale_order_line.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_order_form_inherit_book_price" model="ir.ui.view">
<field name="name">sale.order.line.form.book.price</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']/list/field[@name='product_uom_qty']" position="before">
<field name="book_price" readonly="1"/>
</xpath>
</field>
</record>
</odoo>
10 changes: 0 additions & 10 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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Component} from '@odoo/owl';
export class DashboardItem extends Component{
static template = 'awesome_dashboard.DashboardItem';
static props={
size:{type :Number ,optional:true ,default:1},
slots: {
type: Object,
shape: {
default: Object,
},
},
}
get style(){
return `width: ${18 * this.props.size}rem;`;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.dashboard-item {
background-color: #ffffff;
border-radius: 0.75rem;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
min-height: 160px;
flex: 1 1 300px;
margin: 1rem;
text-align: center;
}

.dashboard-item:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<template xml:space="preserve">
<t t-name="awesome_dashboard.DashboardItem" owl="1">
<div class="dashboard-item card d-flex flex-column align-items-center justify-content-center shadow-sm p-4" t-att-style="style">
<t t-slot="default"/>
</div>
</t>
</template>
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, String],
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.number-card {
background-color: #f8f9fa;
border-radius: 0.75rem;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="awesome_dashboard.NumberCard" owl="1">
<div class="number-card text-center p-4">
<h6 class="text-muted text-uppercase mb-2"><t t-esc="props.title" /></h6>
<p class="display-6 fw-bold text-primary mb-0"><t t-esc="props.value" /></p>
</div>
</t>
</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Component, useRef, onWillStart, onMounted, onWillUpdateProps } 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('chartCanvas');
this.chart = null;

onWillStart(async () => {
await loadJS("/web/static/lib/Chart/Chart.js");
});

onMounted(() => {
this._renderChart();
});

onWillUpdateProps((nextProps) => {
if (this.chart) {
this._updateChart(nextProps.data);
}
});
}

_renderChart() {
const ctx = this.canvasRef.el.getContext('2d');
this.chart = new Chart(ctx, {
type: "pie",
data: {
labels: Object.keys(this.props.data),
datasets: [{
label: "Orders by T-shirt Size",
data: Object.values(this.props.data),
backgroundColor: [
"#3498db", "#2ecc71", "#f1c40f", "#e67e22", "#9b59b6"
],
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: "right"
}
}
}
});
}

_updateChart(newData) {
this.chart.data.labels = Object.keys(newData);
this.chart.data.datasets[0].data = Object.values(newData);
this.chart.update();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<template xml:space="preserve">
<t t-name="awesome_dashboard.PieChart">
<canvas t-ref="chartCanvas" width="400" height="400"/>
</t>

</template>
Loading