19.0 tutorials amtha - #1376
Conversation
amtha-odoo
commented
Jul 29, 2026
- Created Estate module and models
- Added list, form, and search views
- Configured menus, actions, and security
- Implemented model relationships (Many2one, One2many, Many2many)
- Added computed fields, inverse methods, and onchange logic
- Implemented property and offer business actions (Sold, Cancel, Accept, Refuse)
- Added validations and state management
- Updated property buyer and selling price on offer acceptance
Add the basic structure for the estate module as part of the Odoo Server Framework 101 training.
Introduce the initial user interface for the estate module by defining the window action and menu hierarchy required to access property records from the Odoo web client. This change makes the estate.property model accessible through the application, providing the foundation for interacting with records using the default list and form views before introducing custom views in later chapters. The property model is also improved by configuring field attributes and defaults. Read-only and copy behaviors are defined for system-managed fields, default values are added to reduce manual data entry, and the reserved active and state fields are introduced to support record archiving and future workflow-related features.
Introduce the initial user interface for the estate module by defining the window action and menu hierarchy required to access property records from the Odoo web client. This change makes the estate.property model accessible through the application, providing the foundation for interacting with records using the default list and form views before introducing custom views in later chapters. The property model is also improved by configuring field attributes and defaults. Read-only and copy behaviors are defined for system-managed fields, default values are added to reduce manual data entry, and the reserved active and state fields are introduced to support record archiving and future workflow-related features.
Replace the automatically generated views with custom list and form views to provide a clearer and more user-friendly interface for managing estate properties. The custom views organize the property information into logical groups, making property creation and editing easier while exposing the most important fields directly from the list view. This change prepares the module for future UI enhancements such as custom search views, buttons and advanced form layouts introduced in later chapters.
-compute the total-area of the property -compute the best-price of the property using private methods -compute the deadline from create_date and validity -implement an inverse method to update validity from the deadline -handle record creation by providing a fallback when create_date is not yet available -display the new fields in the offer tree and form views
…operty type and tag
…ice, use float_is_zero() and float_compare())
6c89568 to
26ccc80
Compare
mash-odoo
left a comment
There was a problem hiding this comment.
Hello,
Good start on the task.
I have added some comments and suggestions.
Some points to be looked at:
- Do not leave unnecessary lines between fields.
- Reformat the files according to the coding guidelines.
- Add description and chapter name while pushing the commits in order to understand the changes done for that particular commit.
- Update the PR title.
| # estate/models/__init__.py | ||
|
|
||
| from . import estate_property |
There was a problem hiding this comment.
| # estate/models/__init__.py | |
| from . import estate_property | |
| from . import estate_property |
| _order = "name asc" | ||
|
|
||
| name = fields.Char(required=True) | ||
| total_area = fields.Integer(compute="_compute_total_area") | ||
|
|
||
| @api.depends("living_area", "garden_area") | ||
| def _compute_total_area(self): | ||
| for record in self: | ||
| record.total_area = record.living_area + record.garden_area |
There was a problem hiding this comment.
Please refer to this for proper ordering of attributes in a model
| property_type_id = fields.Many2one( | ||
| "estate.property.type", | ||
| string="Property Type", | ||
| ) | ||
|
|
||
| buyer_id = fields.Many2one( | ||
| "res.partner", | ||
| string="Buyer", | ||
| copy=False, | ||
| ) | ||
| salesperson_id = fields.Many2one( | ||
| "res.users", | ||
| string="Salesperson", | ||
| default=lambda self: self.env.user, |
There was a problem hiding this comment.
You don't need to add string explicitly if you want it to be similar to the field name itself.
There was a problem hiding this comment.
Remove string attributes from fields where the default field label is already appropriate.
| garden_orientation = fields.Selection( | ||
| [ | ||
| ("north", "North"), | ||
| ("south", "South"), | ||
| ("east", "East"), | ||
| ("west", "West"), | ||
| ] | ||
| ) |
There was a problem hiding this comment.
| garden_orientation = fields.Selection( | |
| [ | |
| ("north", "North"), | |
| ("south", "South"), | |
| ("east", "East"), | |
| ("west", "West"), | |
| ] | |
| ) | |
| garden_orientation = fields.Selection( | |
| [ | |
| ('north', "North"), | |
| ('south', "South"), | |
| ('east', "East"), | |
| ('west', "West"), | |
| ] | |
| ) |
Try to keep the key i.e the technical strings in single quotes and the values which are to be displayed to the user in double quotes
| ) | ||
| < 0 | ||
| ): | ||
| raise ValidationError( |
There was a problem hiding this comment.
When would you raise UserError and ValidationError?
What is the difference between both of them?
There was a problem hiding this comment.
UserError : use when the user perform invalid action.usually use in button and action methods and stop the current operation
ValidationError : use when data violates a validation.prevents invalid data from being saved.
Both will show an error popup to the user, but UserError is used when a user performs an action that is not allowed, and ValidationError is used when entered or modified data fails a validation
| </field> | ||
| </record> | ||
|
|
||
| </odoo> No newline at end of file |
There was a problem hiding this comment.
Always leave an extra line at the end of the file.
| <field name="partner_id"/> | ||
| <field name="validity"/> | ||
| <field name="deadline"/> | ||
| <button name="action_confirm" string="Confirm" type="object" icon="fa-check"/> |
There was a problem hiding this comment.
When to use type object and type action?
There was a problem hiding this comment.
type = "object" : used when execute python method defined on the model.
type = "action" : used when execute an odoo action identified by its external ID.
| "depends": ["base"], | ||
| "application": True, | ||
| "category": "Tutorials", | ||
| "author": "Thakor Anish", |
There was a problem hiding this comment.
When you are working for a company, you should keep the author name as Odoo S.A. or just skip writing it.
| "application": True, | ||
| "category": "Tutorials", | ||
| "author": "Thakor Anish", | ||
| "license": "LGPL-3", |
There was a problem hiding this comment.
What other options are there for licensing?
There was a problem hiding this comment.
/home/odoo/odoo19/community/odoo/addons/base/models/ir_module.py == line no. : 296
GPL-2,GPL-3,AGPL-3,LGPL-3,OEEL-1,OPL-1
| best_price = fields.Float( | ||
| string="Best Offer", | ||
| compute="_compute_best_price", | ||
| store=True, |
There was a problem hiding this comment.
For a calculated field, use store=True to store the value in the database. but here we don't need to store this value here, so I removed store=True.
- Fix line spacing - Reorder model attributes and methods - Remove unnecessary field string attributes - Update selection technical values to single quotes - Add trailing newline at end of files - Update author name - Remove unnecessary store=True from best_price
2333d73 to
b00f3b5
Compare

