Skip to content

[ADD] estate: create initial module structure and manifest - #1416

Open
mawat-odoo wants to merge 11 commits into
odoo:19.0from
odoo-dev:19.0-Technical-Training-mawat
Open

mawat-odoo wants to merge 11 commits into
odoo:19.0from
odoo-dev:19.0-Technical-Training-mawat

Conversation

@mawat-odoo

Copy link
Copy Markdown

Initialize the estate module with its basic directory structure and manifest file as required for the technical onboarding training.

task-6573564

@robodoo

robodoo commented Sep 15, 2026

Copy link
Copy Markdown

Pull request status dashboard

Initialize the estate module with its basic directory structure and manifest file as required for the technical onboarding training.

task-6573564
@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch from 2e4eb5e to 55b2599 Compare September 15, 2026 11:36

@msho-odoo msho-odoo left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nice job on the commit message format
I left you a comment, feel free to ping me here when yo push for the next improvement in the chapters 🔥

Comment thread .gitignore Outdated
@@ -1,3 +1,9 @@
# Created by https://www.toptal.com/developers/gitignore/api/python,odoo

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ops, I shouldn't see that file :)
Environmental files (usually starts with .) shouldn't be pushed or seen here, you might have a quick search on how to avoid this :)

@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch from c8728d7 to f34bcf9 Compare September 16, 2026 08:29
- Create models package and __init__.py files
- Define estate.property model with basic fields:
  * name, description, postcode
  * date_availability, expected_price, selling_price
  * bedrooms, living_area, facades, garage
  * garden, garden_area, garden_orientation

task-6573564
@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch 2 times, most recently from cd8d2d9 to 06d81bc Compare September 16, 2026 11:11
- Add security/ir.model.access.csv with full permissions for internal users
- Declare security file in __manifest__.py

task-6573564
@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch 2 times, most recently from 669949c to 97a5918 Compare September 16, 2026 22:46
…elds

- Create estate_property_views.xml with act_window for estate.property model
- Create estate_menus.xml with 3-level menu hierarchy
- Register XML files sequentially in __manifest__.py
- Set selling_price as readonly and non-copyable
- Prevent copy on date_availability and set default to 3 months from today
- Set default bedrooms to 2
- Add reserved active field (Boolean) with default=True
- Add required state field (Selection: New, Offer Received, Offer Accepted, Sold, Cancelled) with default='new' and copy=False

task-6573564
@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch from 97a5918 to 32a2e25 Compare September 16, 2026 22:51
Add list, form, and search views for estate.property model.
Search view includes custom filter for available properties and grouping by postcode.

- Add list view for property model
- Add form view with sheet, group, and notebook tags
- Add search view with domain filter and group_by context

Task ID: 6573564
@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch from f83d668 to 74647bf Compare September 17, 2026 07:12
@mawat-odoo

Copy link
Copy Markdown
Author

@msho-odoo some stuffs to review when you have time:) thank you!

@msho-odoo msho-odoo left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thank you for the good work (and the green runbot :))

Regarding the commit message, we only use [ADD] tag when adding a brand new module but adding fields, views, logic, etc is considered an improvement so we use [IMP] tag.

I also left you a couple of comments to address,
please ping me when you finish the next chapter 😉
Thanks!

Comment thread estate/models/estate_property.py Outdated
@@ -0,0 +1,34 @@
from odoo.tools import date_utils

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nitpick: you can delete that empty line

Comment thread estate/models/estate_property.py Outdated
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(copy=False, default=lambda self: date_utils.add(fields.Date.today(), months=3))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can import the add directly from odoo.tools.date_utils and use it instead of importing the whole thing

Comment thread estate/models/estate_property.py Outdated
Comment on lines +28 to +29
garage = fields.Boolean()
garden = fields.Boolean()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

it's better to have boolean fields named is_something or has_something so may call these has_garden and has_garage and then you can give them UI names using the string attribute or just put a string as the first parameter in Boolean()

Comment thread estate/models/estate_property.py Outdated
Comment on lines +31 to +34
garden_orientation = fields.Selection(
string='Type',
selection=[('North', 'North'), ('South', 'South'), ('East', 'East'), ('West', 'West')]
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Having the selection list in one line is okay but when we have such multiple values we style it like this.
Also it's better to have the keys of the selection tuples to be all lower case letters to avoid confusion when used later in the code.

Suggested change
garden_orientation = fields.Selection(
string='Type',
selection=[('North', 'North'), ('South', 'South'), ('East', 'East'), ('West', 'West')]
)
garden_orientation = fields.Selection(
string='Garden Orientation',
selection=[
('north', 'North'),
('south', 'South'),
('east', 'East'),
('west', 'West')
]
)

Comment thread estate/views/estate_menus.xml Outdated
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem id="estate_menu_root" name="Real Estate">
<menuitem id="estatre_first_level_menu" name="Advertisements">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ids better be descriptive so other coders know what it mostly does by name only

Comment thread estate/views/estate_property_views.xml Outdated
Comment on lines +15 to +17
<filter name="state" string="Available Properties" domain="['|',
('state', '=', 'new'),
('state', '=', 'offer_received')]"/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

correct but this is more readable :)

Suggested change
<filter name="state" string="Available Properties" domain="['|',
('state', '=', 'new'),
('state', '=', 'offer_received')]"/>
<filter name="state" string="Available Properties" domain="[('state', 'in', ('new', 'offer_received'))]"/>

Comment thread estate/views/estate_property_views.xml Outdated
<group>
<field name="description"/>
<field name="bedrooms"/>
<field name="living_area" string="Living Area (sqm)"/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You can add the string attribute to the field definition in the python model, that way you wouldn't need to repeat it every time you use the field in a view (unless you want it different in a certain view)

Comment thread estate/__manifest__.py Outdated
@@ -0,0 +1,13 @@
# __manifest__.py
{ # noqa: B018
"author": "mawat",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

author is usually Odoo S.A. :)

Comment thread .gitignore
@@ -1,129 +0,0 @@
# Byte-compiled / optimized / DLL files

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

hmmm I still believe you should make this whole file disappear from the diff history.
Maybe get this file in a separate commit and then drop it using git rebase -i 🤔
Consider it a git challenge on how to remove a file from your git commit diff :)

Add estate.property.type, estate.property.tag, and estate.property.offer models along with their actions and views to handle property classification and offers.

- Create estate.property.type model with menu, action, and views
- Create estate.property.tag model with action and list/form views
- Create estate.property.offer model with list and form views
- Add Many2one links (property_type_id, buyer_id, user_id) to estate.property
- Add Many2many link (tag_ids) to estate.property
- Add One2many link (offer_ids) to estate.property

Task ID: 6573564
Address code review comments by improving field definitions, using lowercase selection keys, simplifying domains, and fixing PEP 8 blank line rules (E302).

- Fix E302 missing blank lines before class declarations
- Remove unused empty line and refine date_utils import in estate_property.py
- Convert selection keys to lowercase for garden_orientation
- Simplify domain filter syntax using in operator in estate_property_views.xml
- Move field string labels to Python definitions
- Fix typo in menu identifier estate_first_level_menu
- Change manifest author to Odoo S.A.

Task ID: 6573564
@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch 3 times, most recently from 0f630ff to d4dd647 Compare September 17, 2026 13:54
@mawat-odoo

Copy link
Copy Markdown
Author

@msho-odoo next chapter is here, following one is coming soon, thanks:) I think I've fixed all your previous remarks too

@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch 4 times, most recently from 49200ee to 69c5aeb Compare September 18, 2026 15:02

@msho-odoo msho-odoo left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thank you for your work!
I left you some comments but please before that, make sure your runbot is green :)
have a look at the ci/style and see why it's red and fix it.
Thanks, keep it up 🔥

Comment thread estate/models/estate_property.py Outdated
has_garden = fields.Boolean()
garden_area = fields.Integer(string="Garden Area (sqm)")
garden_orientation = fields.Selection(
string='Type',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
string='Type',
string='Garden Orientation',

('west', 'West')
]
)
property_type_id = fields.Many2one("estate.property.type", string="Property Type")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

stick to double quotes or single quotes :)

Comment thread estate/models/estate_property.py Outdated
@api.depends("offer_ids.price")
def _compute_best_offer(self):
for record in self:
prices = record.offer_ids.mapped("price")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

There will be an issue in case some properties don't have offer_ids.
The if statement you have should check on the offer_ids

Comment thread estate/models/estate_property.py Outdated
self.garden_orientation = "north"
else:
self.garden_area = 0
self.garden_orientation = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

okay, we conventionally use False

Comment thread estate/models/estate_property.py Outdated
def mark_order_as_sold(self):
for record in self:
if record.state == "cancelled":
raise UserError("Cancelled properties cannot be sold")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

add this _ imported from odoo to support translation as that string is shown to the user

Suggested change
raise UserError("Cancelled properties cannot be sold")
raise UserError(_("Cancelled properties cannot be sold"))

Comment thread estate/models/estate_property.py Outdated
Comment on lines +69 to +70
for record in self:
if record.state == "cancelled":

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When we can write logic on the whole recordset instead of looping over each record, we do that. It's better for reading and writing through the database. For example here you can check if any of self has a state cancelled and in that case you raise the error, you can also write the state to all of them at once.

Comment thread estate/models/estate_property.py Outdated
Comment on lines +77 to +82
for record in self:
if record.state == "sold":
raise UserError("Sold properties cannot be cancelled")
else:
record.state = "cancelled"
return True

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same comments apply here for recordset handling (instead of per record) and the translation thing :)

Comment thread estate/models/estate_property_offer.py Outdated
Comment on lines +41 to +44
else:
record.status = "accepted"
record.property_id.buyer = record.partner_id
record.property_id.selling_price = record.price

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can remove the else, the error raised if the condition is true will prevent the rest of the code from running

Suggested change
else:
record.status = "accepted"
record.property_id.buyer = record.partner_id
record.property_id.selling_price = record.price
record.status = "accepted"
record.property_id.buyer = record.partner_id
record.property_id.selling_price = record.price

Comment thread estate/models/estate_property_offer.py Outdated
Comment on lines +48 to +49
for record in self:
record.status = "refused"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can do self.state = "refused"

Comment thread estate/views/estate_property_views.xml Outdated
<field name="living_area"/>
<field name="facades"/>
<separator/>
<filter name="state" string="Available Properties" domain="['|', ('state', '=', 'new'), ('state', '=', 'offer_received')]"/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

correct but here is a more readable way

Suggested change
<filter name="state" string="Available Properties" domain="['|', ('state', '=', 'new'), ('state', '=', 'offer_received')]"/>
<filter name="state" string="Available Properties" domain="[('state', 'in', ('new', 'offer_received'))]"/>

Enforce data integrity rules on properties, offers, tags, and types to prevent invalid user input:
- Add SQL constraints to ensure expected_price and offer price are strictly positive (> 0), selling_price is positive (>= 0), and property tag/type names are unique.
- Add Python constraint _check_selling_price using float_compare and float_is_zero to prevent selling price from dropping below 90% of expected price once set.

task-6573564
@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch from 69c5aeb to 94e7959 Compare September 18, 2026 15:14
Addressing code review feedback to align with Odoo framework conventions and ensure CI/runbot checks pass:
- Use batch operations on recordsets in property state methods (mark_order_as_sold, mark_order_as_cancelled) and offer actions (refuse_offer) instead of looping with for-in to optimize SQL execution.
- Add translation wrapper _() around UserError error messages.
- Standardize string quotes and update field labels/defaults (e.g., False instead of None for empty selection/boolean defaults).
- Simplify XML search filter domain syntax using the 'in' operator for better readability.
- Check offer_ids presence before calling mapped/max in compute methods to avoid errors on empty recordsets.

task-6573564
@mawat-odoo
mawat-odoo force-pushed the 19.0-Technical-Training-mawat branch from f788745 to 876cda9 Compare September 20, 2026 19:51
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