-
Notifications
You must be signed in to change notification settings - Fork 2.8k
19.0 real estate tutorial jakan #1109
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: 19.0
Are you sure you want to change the base?
Conversation
- Real Estate module created - Basic structure and required fields added - CHAPTER 1, 2, and 3
- Added basic security access for estate records - Fixed missing whitespace and formatting issues - Covers CHAPTER 4
- Added form and list views for estate records - Added menu and action to access the module - Covers CHAPTER 5
- Added list, form, and search views - Added domains and group by options - Covers CHAPTER 6
mash-odoo
left a comment
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.
Hello @jakan-odoo,
Good start on the PR,
Please view the comments and apply the needed changes.
estate/models/estate_property.py
Outdated
| from odoo import fields, models | ||
| from dateutil.relativedelta import relativedelta |
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.
For ordering of imports, refer this: https://www.odoo.com/documentation/19.0/contributing/development/coding_guidelines.html#imports
| <record id="estate_property_list_view" model="ir.ui.view"> | ||
| <field name="name">estate.property.list</field> |
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.
For naming a view: <model_name>_view_<view_type>, where view_type is kanban, form, list, search, …
You can refer this guideline: https://www.odoo.com/documentation/19.0/contributing/development/coding_guidelines.html#xml-ids-and-naming
| <record id="estate_property_form_view" model="ir.ui.view"> | ||
| <field name="name">estate.property.form</field> |
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.
Do same as suggested above
- Added Many2one relation between models - Improves data linking between records - Covers CHAPTER 7
- Added One2many and Many2many relations between models - Improves linking and management of related records - Covers CHAPTER 7
- Added computed fields for automatic values - Added onchange methods to update fields - Covers CHAPTER 8
- Created Sell, Cancel, Accept, and Refuse buttons - Buttons call related methods on the estate models - Buyer and selling price are set when an offer is accepted - Covers CHAPTER 9
mash-odoo
left a comment
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.
Hello,
Good going on the task..
Here are some suggestions..
| <field name="bedrooms"/> | ||
| <field name="living_area"/> | ||
| <field name="selling_price"/> | ||
| <filter string="Available Properties" name="available properties" domain="['|', ('state', '=', 'new'), ('state', '=', 'offer_received')]"/> |
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 write this filter in any other way?
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.
Yes ma'am,
We can write filter like this
<filter string="Available Properties" name="available_properties" domain="[('state', 'in', ('new', 'offer_received'))]"/>
| </form> | ||
| </field> | ||
| </record> | ||
| </odoo> No newline at end of file |
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.
Leave a line at the end of file
estate/models/estate_property.py
Outdated
| ("north", "North"), | ||
| ("south", "South"), | ||
| ("east", "East"), | ||
| ("west", "West")]) |
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.
| ("north", "North"), | |
| ("south", "South"), | |
| ("east", "East"), | |
| ("west", "West")]) | |
| ('north', "North"), | |
| ('south', "South"), | |
| ('east', "East"), | |
| ('west', "West")]) |
estate/models/estate_property.py
Outdated
| ('new', 'New'), | ||
| ('offer_received', 'Offer Received'), | ||
| ('offer_accepted', 'Offer Accepted'), | ||
| ('sold', 'Sold'), | ||
| ('cancelled', '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.
| ('new', 'New'), | |
| ('offer_received', 'Offer Received'), | |
| ('offer_accepted', 'Offer Accepted'), | |
| ('sold', 'Sold'), | |
| ('cancelled', 'Cancelled'), | |
| ('new', "New"), | |
| ('offer_received', "Offer Received"), | |
| ('offer_accepted', "Offer Accepted"), | |
| ('sold', "Sold"), | |
| ('cancelled', '"Cancelled"), |
estate/__manifest__.py
Outdated
| @@ -0,0 +1,20 @@ | |||
| { | |||
| 'name': 'Real Estate', | |||
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.
| 'name': 'Real Estate', | |
| 'name': "Real Estate", |
estate/__manifest__.py
Outdated
| 'version': '1.0', | ||
| 'category': 'Real Estate', | ||
| 'summary': 'Manage real estate properties', | ||
| 'description': 'This module allows managing properties.', |
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.
| 'description': 'This module allows managing properties.', | |
| 'description': "This module allows managing properties.", |
estate/models/estate_property.py
Outdated
| from dateutil.relativedelta import relativedelta | ||
| from odoo import fields, models, api |
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.
For ordering of imports, refer this: https://www.odoo.com/documentation/19.0/contributing/development/coding_guidelines.html#imports
- Added SQL constraints to ensure data validity - Added Python constraints for business rules - Covers CHAPTER 10
- Added inline views for related records - Applied widgets to improve user interface - Set default ordering using _order - CHAPTER 11
- Added maintenance request model with title, cost, and status - Ensured approved requests have a positive cost - Displayed total maintenance cost on the property - Prevented property sale when maintenance is not completed
- Replaced direct cost check with float_is_zero - Ensures correct validation for decimal precision
- Added manual ordering and widget options - Applied conditional button visibility using invisible - Made list views editable and some fields optional - Added decorations, default filters, and stat buttons - Covers CHAPTER 11
mash-odoo
left a comment
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.
Hello!
Thank you for your work..
Here are some suggestions and questions..
| def action_accept(self): | ||
| for offer in self: | ||
| if offer.property_id.buyer_id: | ||
| raise UserError("Property already accepted") | ||
|
|
||
| other_offer = offer.property_id.offer_ids - offer | ||
| other_offer.write({'status': 'refused'}) | ||
|
|
||
| offer.status = "accepted" | ||
| offer.property_id.buyer_id = offer.partner_id | ||
| offer.property_id.selling_price = offer.price | ||
| return True |
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 you optimize this?
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.
Yes ma'am I can optimize using write,
def action_accept(self):
for offer in self:
property_rec = offer.property_id
if property_rec.buyer_id:
raise UserError("Property already accepted")
other_offer = property_rec.offer_ids - offer
other_offer.write({'status': 'refused'})
offer.status = "accepted"
property_rec.write({
'buyer_id': offer.partner_id.id,
'selling_price': offer.price,
'state': 'sold',
'active': False,
})
return True
| offer.status = "refused" | ||
| return True | ||
|
|
||
| _check_offer_price_positive = models.Constraint( |
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.
Follow this conventions while declaring fields, constraints, methods or CRUD operations.
| _order = 'name' | ||
|
|
||
| name = fields.Char(required=True) | ||
|
|
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.
| <field name="garden_area"/> | ||
| <field name="garden_orientation"/> | ||
| <field name="state"/> | ||
| <field name="total_area" readonly="1"/> |
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.
Is there any need to add readonly here?
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 ma'am no need to add readonly because total_area is a compute method and compute method are by default readonly.
| <field name="bedrooms"/> | ||
| <field name="living_area"/> | ||
| <field name="selling_price"/> | ||
| <filter string="Available Properties" name="available_properties" domain="[('state', 'in', ('new', 'offer_received'))]"/> |
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.
What are required params in the filter? How to add a default filter?
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.
A filter requires a string, a unique name, and domain or a context. To apply a filter by default, we can use default="1" on the filter or set search_default_<filter_name> in the action context.
| <notebook> | ||
| <page string="Properties"> | ||
| <field name="property_ids"> | ||
| <list> |
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.
What is the purpose of explicitly defining a list (tree) view here, even though the field is a One2many?
Won't it work just fine even if you write just this <field name="property_ids">?
| def action_sold(self): | ||
| for property in self: | ||
| if property.state == 'cancelled': | ||
| raise UserError("Sold property cannot be 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.
How can you make this translatable?
| property.state = 'cancelled' | ||
| return True | ||
|
|
||
| _check_expected_price_positive = models.Constraint( |
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.
Follow this conventions while declaring fields, constraints, methods or CRUD operations.
| ) | ||
| 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="Seller", default=lambda self: self.env.user) |
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.
What's the need to use this lambda function?
| <header> | ||
| <field name="state" widget="statusbar" statusbar_visible="new,offer_received,offer_accepted,sold"/> | ||
| </header> | ||
| <sheet> | ||
| <header> | ||
| <button name="action_sold" type="object" string="Sold" class="btn-primary" invisible="state in ('sold', 'cancelled')"/> | ||
| <button name="action_cancel" type="object" string="Cancel" class="btn-secondary" invisible="state in ('sold', 'cancelled')"/> | ||
| </header> |
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.
| <header> | |
| <field name="state" widget="statusbar" statusbar_visible="new,offer_received,offer_accepted,sold"/> | |
| </header> | |
| <sheet> | |
| <header> | |
| <button name="action_sold" type="object" string="Sold" class="btn-primary" invisible="state in ('sold', 'cancelled')"/> | |
| <button name="action_cancel" type="object" string="Cancel" class="btn-secondary" invisible="state in ('sold', 'cancelled')"/> | |
| </header> | |
| <header> | |
| <field name="state" widget="statusbar" statusbar_visible="new,offer_received,offer_accepted,sold"/> | |
| <button name="action_sold" type="object" string="Sold" class="btn-primary" invisible="state in ('sold', 'cancelled')"/> | |
| <button name="action_cancel" type="object" string="Cancel" class="btn-secondary" invisible="state in ('sold', 'cancelled')"/> | |
| </header> | |
| <sheet> |
You can add these buttons directly in the header.
- Used Python inheritance to extend existing logic - Applied model inheritance to add fields and behavior - Used view inheritance to update and extend UI - Covers CHAPTER 12

Chapter 4 is completed in this update.
Basic security access rules are added for the Estate module so users can access records correctly. Code style issues like missing white space and formatting problems are also fixed.