Skip to content

[ADD] estate: new module to manage estate #822

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

Closed
wants to merge 4 commits into from

Conversation

taki-odoo
Copy link

No description provided.

@robodoo
Copy link

robodoo commented Jun 18, 2025

Pull request status dashboard

Copy link

@aboo-odoo aboo-odoo left a comment

Choose a reason for hiding this comment

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

First review for you! Don't hesitate to ask if you have question, our goal is teach you as much as possible before joining a team

@@ -0,0 +1 @@
from . import models

Choose a reason for hiding this comment

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

Always add an EOF character so that the next modification of the file made by someone else would not include this line

This comment applies several times in other files

@@ -0,0 +1,14 @@
{
'name': 'Estate',

Choose a reason for hiding this comment

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

In python, for quoting strings we try to follow the following convention:

single quote around technical terms
double quotes around display terms
=> ('north', "North")

Let's try to follow this during this tutorial, even though you'll find many counter examples in the existing code.

name = fields.Char(string="Title", required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(copy=False, default=lambda self: date.today() + timedelta(days=90))

Choose a reason for hiding this comment

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

90 days is not quite 3 months 😄

Comment on lines 30 to 34
property_type = fields.Many2one("estate.property.type", string="Property Type")
property_salesman = fields.Many2one("res.users", string="Salesman", default=lambda self: self.env.user)
property_buyer = fields.Many2one("res.partner", string="Buyer");
property_tags = fields.Many2many("estate.property.tag", string="Tags")
property_offers = fields.One2many("estate.property.offer", "property_id", string="Offers")

Choose a reason for hiding this comment

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

For relational fields, the convention is to suffix them by _id _ids to easily recognize them. And there is no need to prefix them by estate_

from datetime import date, timedelta

class EstateProperty(models.Model):
_name = "estate.property"

Choose a reason for hiding this comment

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

Suggested change
_name = "estate.property"
_name = 'estate.property'

Comment on lines 3 to 11
<menuitem id="Estate_menu_root" name="Estate"/>

<menuitem id="Estate_menu_Advertissements" name="Advertissement" parent="Estate_menu_root"/>
<menuitem id="Estate_menu_Properties" name="Property" parent="Estate_menu_Advertissements" action="estate_property_view"/>
<menuitem id="Estate_menu_Properties_offer" name="Offers" parent="Estate_menu_Advertissements" action="estate_property_offer_view"/>

<menuitem id="Estate_menu_settings" name="Settings" parent="Estate_menu_root" sequence="20"/>
<menuitem id="Estate_menu_Properties_type" name="Property Type" parent="Estate_menu_settings" action="estate_property_type_view"/>
<menuitem id="Estate_menu_Properties_tags" name="Property Tags" parent="Estate_menu_settings" action="estate_property_tag_view"/>

Choose a reason for hiding this comment

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

What you did works, but placing them in the file means you can do this

Suggested change
<menuitem id="Estate_menu_root" name="Estate"/>
<menuitem id="Estate_menu_Advertissements" name="Advertissement" parent="Estate_menu_root"/>
<menuitem id="Estate_menu_Properties" name="Property" parent="Estate_menu_Advertissements" action="estate_property_view"/>
<menuitem id="Estate_menu_Properties_offer" name="Offers" parent="Estate_menu_Advertissements" action="estate_property_offer_view"/>
<menuitem id="Estate_menu_settings" name="Settings" parent="Estate_menu_root" sequence="20"/>
<menuitem id="Estate_menu_Properties_type" name="Property Type" parent="Estate_menu_settings" action="estate_property_type_view"/>
<menuitem id="Estate_menu_Properties_tags" name="Property Tags" parent="Estate_menu_settings" action="estate_property_tag_view"/>
<menuitem id="estate_menu_root" name="Estate"/>
<menuitem id="estate_menu_Advertissements" name="Advertissement"/>
<menuitem id="estate_menu_Properties" name="Property" action="estate_property_view"/>
<menuitem id="estate_menu_Properties_offer" name="Offers" action="estate_property_offer_view"/>
</menuitem>
<menuitem id="estate_menu_settings" name="Settings"/>
<menuitem id="estate_menu_Properties_type" name="Property Type" action="estate_property_type_view"/>
<menuitem id="estate_menu_Properties_tags" name="Property Tags" action="estate_property_tag_view"/>
</menuitem>
</menuitem>

which is more readable and easier to manage.

Also, no upper case for ids in general.

<field name="facades"/>
<separator/>
<filter string="available" name="available" domain="[('available', '=', False)]"/>
<group expand="1" string="Group By">

Choose a reason for hiding this comment

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

What does expand 1 do in this case ? And why did you add the filter in a group ?

</form>
</field>
</record>
<record id="estate_property_tag_view" model="ir.actions.act_window">

Choose a reason for hiding this comment

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

Convention for action id is <model_name>_action

</form>
</field>
</record>
<record id="estate_property_offer_view_tree" model="ir.ui.view">

Choose a reason for hiding this comment

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

Suggested change
<record id="estate_property_offer_view_tree" model="ir.ui.view">
<record id="estate_property_offer_view_list" model="ir.ui.view">

tree is the old naming for list

Comment on lines 24 to 25
<form string="Estate Property">
</form>

Choose a reason for hiding this comment

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

Empty Form ?

@aboo-odoo
Copy link

Hey, could you do the fix and push your latest change at some point today so that I can review your extra work ? Thanks

@taki-odoo taki-odoo force-pushed the 18.0-tutorials-taki branch 2 times, most recently from be996c2 to ba0ce5c Compare June 26, 2025 08:59
@aboo-odoo
Copy link

  1. git add <files> - to add changes to your future commit
  2. git commit - to commit the change
  3. git fetch origin 18.0 - to fetch the latest change on the main branch
  4. git rebase 18.0 -i - to get the lastest change on your branch (may trigger conflicts) and squash all your commits into one
  5. git push --force-with-lease dev - to push on your branch

Utils:

  • git stash - to store current change in a stack
  • git stash pop - to add back the earliest change in the stack

git status - to help you overall

@taki-odoo taki-odoo force-pushed the 18.0-tutorials-taki branch from ba0ce5c to 6626196 Compare June 26, 2025 15:26
Copy link

@aboo-odoo aboo-odoo left a comment

Choose a reason for hiding this comment

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

Hey! Final review on the python part, it focuses on functionality, not style anymore 😄

By Monday, try to get your runbot green 🟢 and fix the comments from last time and this review please 👍

Comment on lines 68 to 71
if record.offer_ids:
record.best_offer = max(record.offer_ids.mapped("price"))
else:
record.best_offer = 0.0

Choose a reason for hiding this comment

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

This can be simplified into

Suggested change
if record.offer_ids:
record.best_offer = max(record.offer_ids.mapped("price"))
else:
record.best_offer = 0.0
record.best_offer = max(record.offer_ids.mapped("price"), default=0.0)

if record.state != 'sold':
record.state = 'cancelled'
else:
raise UserError("You cannot cancel a sold property.")

Choose a reason for hiding this comment

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

Most strings are automatically made translatable: field names, module and model names, etc. But this is not the case for error messages. To make them translatable, use the _() method like so

Suggested change
raise UserError("You cannot cancel a sold property.")
raise UserError(_("You cannot cancel a sold property."))

if float_compare(record.selling_price, min_acceptable_price, precision_digits=2) < 0:
raise ValidationError("The selling price cannot be lower than 90% of the expected price.")

def unlink(self):

Choose a reason for hiding this comment

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

This is called upon the module deletion, to avoid triggering the error when the module is deleted by a user, add the api.ondelete(at_uninstall=False)

@api.depends("validity")
def _compute_deadline(self):
for record in self:
record.deadline = date.today() + timedelta(days=record.validity)

Choose a reason for hiding this comment

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

Currently, your method does this:

Each time validity is updated, the deadline will be today + validity.

However, we'd prefer to have:

Each time validity is updated, the deadline is the day we created the announce + validity.

Meaning that currently, if I create an estate on the 7th of March with a validity of 7, the deadline is the 14th of March. On the 10th of March, I want to extend the validity to 10 days. I would expect to have the deadline on the 17th but I'll actually have the 20th.

def _inverse_deadline(self):
for record in self:
if record.deadline:
record.validity = (record.deadline - date.today()).days

Choose a reason for hiding this comment

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

This will also need adapting with regards to the comment above

@api.depends("property_id.offer_ids")
def _compute_offer_count(self):
for record in self:
record.offer_counts = sum(len(prop.offer_ids) for prop in record.property_id)

Choose a reason for hiding this comment

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

What you did is correct, but relational fields allow for some simplifaction like

Suggested change
record.offer_counts = sum(len(prop.offer_ids) for prop in record.property_id)
record.offer_counts = len(record.property_id.offer_ids)

Comment on lines +8 to +9
<header>
<button type="action"

Choose a reason for hiding this comment

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

This a lot of indent 👀

Suggested change
<header>
<button type="action"
<header>
<button type="action"

_inherit = 'estate.property'

def sold_property_button(self):
res = super().sold_property_button()

Choose a reason for hiding this comment

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

Your sold_property_button in estate does not return anything. Thus, this one should not either

Suggested change
res = super().sold_property_button()
super().sold_property_button()

],
})

return res

Choose a reason for hiding this comment

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

Suggested change
return res

def create(self, vals_list):
for vals in vals_list:
property_id = vals.get('property_id')
price = vals.get('price')

Choose a reason for hiding this comment

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

.get defaults to False when not found which will trigger an error later when you'll compare a float to a bool

Suggested change
price = vals.get('price')
price = vals.get('price', default=0.0)

@aboo-odoo
Copy link

Now that your onboarding is done, could you close the PR ? 😄

@taki-odoo taki-odoo closed this Jul 1, 2025
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