Skip to content

[ADD] estate,*: implement real estate management with accounting integration #842

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 15 commits into
base: 18.0
Choose a base branch
from

Conversation

arkp-odoo
Copy link

@arkp-odoo arkp-odoo commented Jul 9, 2025

*=estate_account

This pull request includes the development of two related modules: estate for managing real estate operations, and estate_account for integrating real estate with Odoo’s accounting system.

The estate module allows users to create and manage real estate properties. Each property can have details like a name, description, expected price, selling price, property type, tags, buyer, and salesperson. Users can also create offers on properties, which can be accepted or rejected. Business logic ensures offers must be at least 90% of the expected price to be accepted. Once an offer is accepted, the property can be marked as sold. A property that is sold cannot be cancelled, and a cancelled property cannot be sold. These rules help enforce consistent and realistic workflows for property sales.

The estate_account module extends the functionality of the real estate system by automatically generating an invoice when a property is marked as sold. This is done by linking the estate module with Odoo’s built-in account module, ensuring that financial records are kept in sync with property sales.

Through this implementation, I learned how to structure custom Odoo modules, define models and views, apply business logic using Python, and connect two modules using dependencies. It also helped me understand how to use computed fields, constraints, and XML views in a real-world business scenario.

@robodoo
Copy link

robodoo commented Jul 9, 2025

Pull request status dashboard

Copy link

@maad-odoo maad-odoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass 🚀.
The PR commits contains a lot of unnecessary diffs left without any reason please avoid that 🙏
Thank you 🙂

'name': 'Estate',
'installable': True,
'application': True,
'auto_install': False,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need auto_install if you have to set it to false?

@@ -0,0 +1,5 @@
from . import estate_property

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


_order = "id desc"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to leave extra line to define the _order property

Comment on lines 51 to 58
total_area = fields.Float(compute="_compute_total")

@api.depends("garden_area", "living_area")
def _compute_total(self):
for record in self:
record.total_area = record.garden_area + record.living_area

best_offer = fields.Float(compute="_find_best")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why there is compute method defined in between the field definition?
Any specific reason ?

Comment on lines +76 to +73

status = fields.Selection(
selection=[
('new', 'New'),
('sold', 'Sold'),
('cancelled', 'Cancelled')
],
default='new',
copy=False
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as above.

Comment on lines 15 to 16

_sql_constraints = [
('property_type_unique', 'unique(name)', 'property type should be unique')
]

offer_count = fields.Integer(string="Offer Count", compute="_compute_offer_count")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Defining SQL constrains in between field definition ?

please avoid this

Comment on lines 4 to 7
class InheritedFields(models.Model):
_inherit = 'res.users'

property_ids = fields.One2many('estate.property', 'salesman_id', domain=[('state', 'not in', ['sold', 'cancelled'])])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The model name is very unusual here.
The name of the model should be pick in such a way it justifies its purpose.


</menuitem>

</odoo>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

End of File Line is missing

</list>
</field>
</record>
</odoo>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EOF line missing

Comment on lines 29 to 59
<record id = "estate_property_type_form" model = "ir.ui.view">
<field name = "name">estate.property.type.form</field>
<field name = "model">estate.property.type</field>
<field name = "arch" type="xml">
<form>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="%(action_property_offers)d"
type="action"
class="oe_stat_button"
icon="fa-money">
<field name="offer_count" widget="statinfo" string="Offers"/>
</button>
</div>
<h1 class="mb32">
<field name="name" class="mb16"/>
</h1>

<notebook>
<page string="Properties">

<field name ="property_ids">
<list>
<field name ="name"/>
<field name = "expected_price" />
<field name ="state" />
</list>
</field>

</page>
</notebook>
</sheet>
</form>
</field>
</record>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diff contains a bunch of unnecessary line left in between. please resolve this

@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 6bcd24a to a90e48d Compare July 14, 2025 10:40
created the init and manifest files and created the models for schema definition,
security for the assigning access rights, created the views folder, and made the three-level
menuitems along with the list and form view.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 94d0037 to 1cc16a4 Compare July 16, 2025 06:37
Added access rights, groups, and record rules to secure estate records.
Enhanced form, tree views for better usability and layout.
These changes ensure proper data protection and improve user experience.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 1cc16a4 to a287a58 Compare July 16, 2025 06:46
Implemented SQL constraints to enforce data integrity and added computed fields
with inverse methods for key model attributes.These changes ensure better
consistency in business logic, such as automatic computation of property fields
and proper validation of constraints like expected price and selling price.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from a287a58 to 4fe8d68 Compare July 16, 2025 06:57
Introduced state management for property offers to support workflows like
accepted and refused states. Added a smart button to the property form view
for quick access to related offers.  Also  conditional field visibility
and button behavior based on offer status.  These improvements enhance usability
and align the offer logic more closely with real business scenarios.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 4fe8d68 to 24d3a4f Compare July 16, 2025 07:10
inherited the estate.property model and override the action_sold method to
generate the invoice using account_move.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch 2 times, most recently from 27fc785 to 53a7fb6 Compare July 16, 2025 07:21
Cleaned up code formatting by correcting indentation and removing unnecessary
whitespaces.These changes improve code readability and maintain consistency
with Odoo's coding standards, without affecting functionality.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 53a7fb6 to 69b4f2a Compare July 16, 2025 07:27
Resolved XML warnings by adding meaningful 'string' attributes to icon-only
buttons.Odoo requires 'string' for button validation and accessibility, even
with icons.This improves code compliance with Odoo's UI and view standards.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 69b4f2a to 67da7db Compare July 16, 2025 07:37
Reorganized all  files to follow Odoo coding conventions.Grouped private
attributes, fields, compute methods, and selection definitionsin the recommended
order.Also reordered imports: external libraries first, thenOdoo modules.This
improves readability, consistency, and maintainability across the module.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 67da7db to 5317525 Compare July 16, 2025 07:45
Cleaned up whitespaces and ensured a single newline at the end of each file.
These changes resolve common linting warnings and align with Odoo’s
coding standards for clean and consistent formatting.
Added new fields to various models within the estate module to support extended
functionality and data capture. These additions improve the module’s ability to
store and manage detailed property-related information in alignment with
business requirements.
…erson

Enhanced the estate module by introducing a Kanban view to improve the visual
representation of properties and their statuses. Additionally, implemented PDF
report generation for both individual properties and salespersons, allowing
users to export key details in a printable format. These features improve user
experience and support better reporting and communication.
This commit adds test cases to validate the key workflows in the estate module,
including property creation, status transitions, and offer handling.These tests
ensure better coverage and help prevent regressions during future development.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 5317525 to 8770246 Compare July 16, 2025 08:00
@arkp-odoo arkp-odoo changed the title [ADD] estate: Added Estate module [ADD] estate,*: implement real estate management with accounting integration Jul 16, 2025
Implemented awesome_owl to render dynamic frontend components within the Odoo
environment.The module includes an OWL component, JS logic, and integration
with QWeb templates. It demonstrates how to use client-side rendering with OWL
in a modular and reusable way and serves as a foundational example for building
interactive UI features using OWL in Odoo.
@arkp-odoo arkp-odoo force-pushed the 18.0-training-arkp branch from 3f88705 to 64be323 Compare July 18, 2025 05:15
Developed a custom dashboard module using OWL and JavaScript to display
key business metrics in a dynamic and user-friendly interface.
The dashboard integrates with backend models to show real-time data using
cards and charts.This module demonstrates how to build interactive dashboards
in Odoo using client-side rendering and custom components.
Copy link

@maad-odoo maad-odoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hii 🙂
Some comments.
There are a lot of unnecessary diff and linting issues that are still present after mentioning them in previous review, please remove those.
Also adapt the current comments as well .
Thanks 🙏

import { registry } from "@web/core/registry";
import { Layout } from "@web/search/layout";
import { useService } from "@web/core/utils/hooks";
import { DashboardItem } from "./dashboardItem/dashboarditem";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try using absolute path

import { useService } from "@web/core/utils/hooks";
import { DashboardItem } from "./dashboardItem/dashboarditem";
import { rpc } from "@web/core/network/rpc";
import { PieChart } from "./pieChart/piechart";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static components = { Layout, DashboardItem, PieChart};
static components = { Layout, DashboardItem, PieChart };

Comment on lines 1 to 39
.o_dashboard{
background-color: gray;
}
.o_dashboard_stat_block {
text-align: center;
margin-bottom: 24px;
}

.o_dashboard_stat_label {
font-weight: normal;
margin-bottom: 10px;
display: block;
}

.o_dashboard_stat_value {
font-size: 48px;
color: #228B22;
font-weight: bold;
}
.o_dashboard_item {
background: #fff;
border-radius: 0.75rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.07);
padding: 1rem;
margin: 1rem;
display: inline-flex;
justify-content: center;
vertical-align: top;
min-height: 3rem;
}

@media (max-width: 426px) {
.o_dashboard_item {
width: 100% !important;
display: flex;
margin-left: 0.5rem;
margin-right: 0.5rem;
box-sizing: border-box;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could have used bootstrap classes directly in the template of the component

Comment on lines 3 to 4
import { NumberCard } from "./numbercard/numbercard";
import { PieChartCard } from "./pieChartCard/pie_chart_card";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolute path

Comment on lines 11 to 26
async function fetchStatistics() {
statistics.loading = true;
statistics.error = null;
try {
const response = await rpc("/awesome_dashboard/statistics");
statistics.data = response;
} catch (e) {
statistics.error = e;
} finally {
statistics.loading = false;
}
}

fetchStatistics();

setInterval(fetchStatistics, 600000);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this function recursively call itself using settimeout or maybe something else ?....

<div class="card-body">
<h5 class="card-title">
<t t-out="props.title" />
<!-- <t t-out="props.content" /> -->

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚒️ Remove this if not needed

Comment on lines 18 to 19
content1 = "<div class='text-primary'>some content</div>"
content2 = markup("<div class='text-primary'>some content</div>")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have use markup in one and not in another, any specific reason why ?

setup(){
this.state = useState({sum:2});
}
incrementSum(value){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the value has no use, then remove it altogether

Comment on lines 18 to 40
self.env["estate.property.offer"].create(
{
"price": 1500.00,
"partner_id": self.env.ref("base.res_partner_1").id,
"date_deadline": "2025-09-14",
"property_id": property.id,
"status": "accepted",
}
)

property.sold_button_action()

with self.assertRaises(
UserError, msg="Cannot create an offer for a sold property"
):
self.env["estate.property.offer"].create(
{
"price": 1500.00,
"partner_id": self.env.ref("base.res_partner_1").id,
"date_deadline": "2025-09-14",
"property_id": property.id,
}
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion message indicate that the property is marked as sold.
Though the internal logic might be able to justify it, but the testcase should be pretty much self explanatory

*=awesome_owl,estate
This commit improves the frontend code for the  module to
align with Odoo's modern best practices.

QWeb templates and OWL JS components have been refactored so that string
translations using  are handled exclusively in JavaScript code.
Getters such as  and  are used to expose
 translated strings to the templates, ensuring QWeb remains simple and
translations are performed in JS only.

Periodic server polling for statistics now uses a recursive
approach for efficient and reliable scheduling, preventing overlapping RPC calls
 that can occur with .

The PieChart component's click handler has been simplified: temporary
 variables are removed when values are only used once, resulting in cleaner and
more maintainable code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants