Skip to content

Real Estate Tutorial (raame)#1248

Open
raame-odoo wants to merge 9 commits intoodoo:19.0from
odoo-dev:19.0-tutorial-raame
Open

Real Estate Tutorial (raame)#1248
raame-odoo wants to merge 9 commits intoodoo:19.0from
odoo-dev:19.0-tutorial-raame

Conversation

@raame-odoo
Copy link
Copy Markdown

@raame-odoo raame-odoo commented Apr 22, 2026

PR for the Real Estate Module Tutorial

@raame-odoo raame-odoo requested a review from vandroogenbd April 22, 2026 12:29
@raame-odoo raame-odoo self-assigned this Apr 22, 2026
@robodoo
Copy link
Copy Markdown

robodoo commented Apr 22, 2026

Pull request status dashboard

@raame-odoo raame-odoo force-pushed the 19.0-tutorial-raame branch from 9f184e7 to 7371099 Compare April 22, 2026 13:06
…nd access rights

A new estate module was created. Property model was created as well and contains some fields. Access rights were added.
@raame-odoo raame-odoo force-pushed the 19.0-tutorial-raame branch from 7371099 to 6a48a78 Compare April 22, 2026 13:17
@raame-odoo raame-odoo changed the title Chapter 1-4 Chapter 1-4 (raame) Apr 23, 2026
Copy link
Copy Markdown

@vandroogenbd vandroogenbd left a comment

Choose a reason for hiding this comment

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

Not much to say, runbot is green... Keep going! 🚀

Comment thread estate/models/estate_property.py Outdated
Comment on lines +6 to +8
_description = "Properties of the estate"

name = fields.Char(required=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.

Good practice to separate attributes from fields 👍

Comment thread estate/security/ir.model.access.csv Outdated
@@ -0,0 +1,2 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_property_model,access_property_model,model_estate_property,base.group_user,1,1,1,1 No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Comment thread estate/__manifest__.py
{
'name': 'Real Estate',
'depends': [
'base'
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 is good practice to end such lines with a ,. This way, if someone later adds a line, they don't have to add the coma so the line stays unchanged, this keeps the git history cleaner!

Comment thread estate/__manifest__.py Outdated
'security/ir.model.access.csv'
],
'application': True,
'license': 'AGPL-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.

Same as above

@vandroogenbd
Copy link
Copy Markdown

Forgot to say, please add a description to the PR

Base users were able to unlink records although they're just plebs.
@raame-odoo raame-odoo changed the title Chapter 1-4 (raame) Real Estate Tutorial (raame) Apr 23, 2026
Added an xml file for actions. Also added the 3 levels of menu items and linked them to actions. Finally, some new fields were added like active and state. Some new attributes were added to new and existing fields.
@raame-odoo raame-odoo force-pushed the 19.0-tutorial-raame branch from 0acc010 to 9d4d467 Compare April 23, 2026 11:39
List view, form view, and search with filter were added in this Chapter
Many2one fields and One2many fields were added to represent types, tags, and offers
@raame-odoo raame-odoo force-pushed the 19.0-tutorial-raame branch from 8570dc0 to 7876339 Compare April 27, 2026 07:54
Added computed fields, inverse methods, and onchange methods
@raame-odoo raame-odoo force-pushed the 19.0-tutorial-raame branch 2 times, most recently from 577213f to 23e6402 Compare April 28, 2026 08:40
Added action buttons
@raame-odoo raame-odoo force-pushed the 19.0-tutorial-raame branch from 23e6402 to 88c7d39 Compare April 28, 2026 12:43
vandroogenbd and others added 2 commits April 28, 2026 15:33
This commit is here to introduce the testing framework of Odoo. Try running the
tests using `--test-tags :TestEstateProperty`.

Doc:
https://www.odoo.com/documentation/18.0/developer/reference/backend/testing.html?highlight=tests#invocation

The tests were made such that the first one should work but the second one
should fail. Your job is to ensure both tests pass in the end. You should update
the behaviour of the appropriate models.

If you want, you can also add a small test of your own to get a feel for it.
Added constraint and changed behaviour to pass the tests
@raame-odoo raame-odoo force-pushed the 19.0-tutorial-raame branch from 53100c5 to d1ca42c Compare April 29, 2026 07:13
Copy link
Copy Markdown

@vandroogenbd vandroogenbd left a comment

Choose a reason for hiding this comment

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

Don't hesitate to ask me if you have questions and if you don't, keep it up! 💪

garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(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.

I know we're not paid by line count, but it is sometimes worth using linebreaks for readability

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

garden_area = fields.Integer()
garden_orientation = fields.Selection(selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')])
active = fields.Boolean(default=True)
state = fields.Selection(selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')], required=True, copy=False, default='new')
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 here

Suggested change
state = fields.Selection(selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')], required=True, copy=False, default='new')
state = fields.Selection(
selection=[
('new', 'New'),
('offer_received', 'Offer Received'),
('offer_accepted', 'Offer Accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled'),
],
required=True,
copy=False,
default='new',
)

garden_orientation = fields.Selection(selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')])
active = fields.Boolean(default=True)
state = fields.Selection(selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')], required=True, copy=False, default='new')
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.

When the variable name is that explicit, you don't have to specify a string 😉

Comment on lines +32 to +35
_check_expected_price = models.Constraint(
'CHECK(expected_price > 0)',
'A property expected price must be strictly positive',
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Indentation is off here

Suggested change
_check_expected_price = models.Constraint(
'CHECK(expected_price > 0)',
'A property expected price must be strictly positive',
)
_check_expected_price = models.Constraint(
'CHECK(expected_price > 0)',
'A property expected price must be strictly positive',
)

Comment on lines +32 to +40
_check_expected_price = models.Constraint(
'CHECK(expected_price > 0)',
'A property expected price must be strictly positive',
)

_check_selling_price = models.Constraint(
'CHECK(selling_price >= 0)',
'A property selling price must be positive',
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Constraints should be after computes (see doc)

Comment on lines +3 to +11
<menuitem id="property_menu_root" name="Real Estate">
<menuitem id="property_first_level_menu" name="Advertisements">
<menuitem id="property_model_menu_action" action="property_model_action"/>
</menuitem>
<menuitem id="settings_first_level_menu" name="Settings">
<menuitem id="property_type_model_menu_action" action="property_type_model_action"/>
<menuitem id="property_tag_model_menu_action" action="property_tag_model_action"/>
</menuitem>
</menuitem>
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 are specific guidelines for xml (see doc)

Suggested change
<menuitem id="property_menu_root" name="Real Estate">
<menuitem id="property_first_level_menu" name="Advertisements">
<menuitem id="property_model_menu_action" action="property_model_action"/>
</menuitem>
<menuitem id="settings_first_level_menu" name="Settings">
<menuitem id="property_type_model_menu_action" action="property_type_model_action"/>
<menuitem id="property_tag_model_menu_action" action="property_tag_model_action"/>
</menuitem>
</menuitem>
<menuitem id="estate_menu_root" name="Real Estate">
<menuitem id="estate_property_menu" name="Advertisements">
<menuitem id="estate_property_menu_action" action="estate_property_action"/>
</menuitem>
<menuitem id="estate_settings_menu" name="Settings">
<menuitem id="estate_property_type_menu_action" action="estate_property_type_action"/>
<menuitem id="estate_property_tag_menu_action" action="estate_property_tag_action"/>
</menuitem>
</menuitem>

The idea is to match model names.

Comment on lines +3 to +19
<record id="property_model_action" model="ir.actions.act_window">
<field name="name">Properties</field>
<field name="res_model">estate.property</field>
<field name="view_mode">list,form</field>
</record>

<record id="property_type_model_action" model="ir.actions.act_window">
<field name="name">Property Types</field>
<field name="res_model">estate.property.type</field>
<field name="view_mode">list,form</field>
</record>

<record id="property_tag_model_action" model="ir.actions.act_window">
<field name="name">Property Tags</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">list,form</field>
</record>
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 will need to do some renaming here to match the changes from above

<field name="view_mode">list,form</field>
</record>

<record id="estate_property_view_list" model="ir.ui.view">
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Naming for the model views is great! 👍

Comment on lines +37 to +51
<record id="estate_property_offer_view_list" model="ir.ui.view">
<field name="name">estate.property.offer.list</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<list string="Offers">
<field name="price" width="400px"/>
<field name="partner_id" string="Buyer" width="100px"/>
<field name="validity" string="Validity (days)" width="100px"/>
<field name="date_deadline" string="Deadline" width="100px"/>
<button name="accept_offer" type="object" icon="fa-check"/>
<button name="action_refuse" type="object" icon="fa-times"/>
<field name="status" width="100px"/>
</list>
</field>
</record>
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 would make more sense to group the views by model, eg all of the estate_property views first, then all of the estate_property_offer views.

You don't have to, but you could also split them into 2 separate files to make it even cleaner (although here it is okay to keep them in the same file as it isn't too long).

Comment thread FETCH_HEAD
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why is this here? 👀

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