-
Notifications
You must be signed in to change notification settings - Fork 2.3k
[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
base: 18.0
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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 🙂
estate/__manifest__.py
Outdated
'name': 'Estate', | ||
'installable': True, | ||
'application': True, | ||
'auto_install': False, |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copyright info is missing
Refer this https://github.com/odoo/odoo/blob/cc5d7f3e068ca37488b0d716efacf36727401704/addons/account/__init__.py#L2
Apples through the diff
estate/models/estate_property.py
Outdated
|
||
_order = "id desc" |
There was a problem hiding this comment.
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
estate/models/estate_property.py
Outdated
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") |
There was a problem hiding this comment.
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 ?
|
||
status = fields.Selection( | ||
selection=[ | ||
('new', 'New'), | ||
('sold', 'Sold'), | ||
('cancelled', 'Cancelled') | ||
], | ||
default='new', | ||
copy=False | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here as above.
|
||
_sql_constraints = [ | ||
('property_type_unique', 'unique(name)', 'property type should be unique') | ||
] | ||
|
||
offer_count = fields.Integer(string="Offer Count", compute="_compute_offer_count") |
There was a problem hiding this comment.
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
estate/models/inherited_model.py
Outdated
class InheritedFields(models.Model): | ||
_inherit = 'res.users' | ||
|
||
property_ids = fields.One2many('estate.property', 'salesman_id', domain=[('state', 'not in', ['sold', 'cancelled'])]) |
There was a problem hiding this comment.
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.
estate/views/estate_menu.xml
Outdated
|
||
</menuitem> | ||
|
||
</odoo> |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EOF line missing
<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> |
There was a problem hiding this comment.
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
6bcd24a
to
a90e48d
Compare
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.
94d0037
to
1cc16a4
Compare
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.
1cc16a4
to
a287a58
Compare
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.
a287a58
to
4fe8d68
Compare
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.
4fe8d68
to
24d3a4f
Compare
inherited the estate.property model and override the action_sold method to generate the invoice using account_move.
27fc785
to
53a7fb6
Compare
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.
53a7fb6
to
69b4f2a
Compare
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.
69b4f2a
to
67da7db
Compare
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.
67da7db
to
5317525
Compare
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.
5317525
to
8770246
Compare
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.
3f88705
to
64be323
Compare
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.
There was a problem hiding this 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"; |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static components = { Layout, DashboardItem, PieChart}; | |
static components = { Layout, DashboardItem, PieChart }; |
.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; | ||
} |
There was a problem hiding this comment.
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
import { NumberCard } from "./numbercard/numbercard"; | ||
import { PieChartCard } from "./pieChartCard/pie_chart_card"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolute path
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); |
There was a problem hiding this comment.
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 ?....
awesome_owl/static/src/card/card.xml
Outdated
<div class="card-body"> | ||
<h5 class="card-title"> | ||
<t t-out="props.title" /> | ||
<!-- <t t-out="props.content" /> --> |
There was a problem hiding this comment.
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
awesome_owl/static/src/playground.js
Outdated
content1 = "<div class='text-primary'>some content</div>" | ||
content2 = markup("<div class='text-primary'>some content</div>") |
There was a problem hiding this comment.
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 ?
awesome_owl/static/src/playground.js
Outdated
setup(){ | ||
this.state = useState({sum:2}); | ||
} | ||
incrementSum(value){ |
There was a problem hiding this comment.
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
estate/tests/test_estate_property.py
Outdated
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, | ||
} | ||
) |
There was a problem hiding this comment.
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.
*=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.