-
Notifications
You must be signed in to change notification settings - Fork 2.4k
[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
Conversation
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.
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
estate/__init__.py
Outdated
@@ -0,0 +1 @@ | |||
from . import models |
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.
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
estate/__manifest__.py
Outdated
@@ -0,0 +1,14 @@ | |||
{ | |||
'name': '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.
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.
estate/models/estate_property.py
Outdated
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)) |
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.
90 days is not quite 3 months 😄
estate/models/estate_property.py
Outdated
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") |
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 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_
estate/models/estate_property.py
Outdated
from datetime import date, timedelta | ||
|
||
class EstateProperty(models.Model): | ||
_name = "estate.property" |
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 = "estate.property" | |
_name = 'estate.property' |
estate/views/estate_menus.xml
Outdated
<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"/> |
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 you did works, but placing them in the file means you can do this
<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"> |
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 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"> |
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.
Convention for action id is <model_name>_action
</form> | ||
</field> | ||
</record> | ||
<record id="estate_property_offer_view_tree" model="ir.ui.view"> |
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.
<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
<form string="Estate Property"> | ||
</form> |
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.
Empty Form ?
Hey, could you do the fix and push your latest change at some point today so that I can review your extra work ? Thanks |
be996c2
to
ba0ce5c
Compare
Utils:
|
ba0ce5c
to
6626196
Compare
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.
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 👍
estate/models/estate_property.py
Outdated
if record.offer_ids: | ||
record.best_offer = max(record.offer_ids.mapped("price")) | ||
else: | ||
record.best_offer = 0.0 |
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.
This can be simplified into
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) |
estate/models/estate_property.py
Outdated
if record.state != 'sold': | ||
record.state = 'cancelled' | ||
else: | ||
raise UserError("You cannot cancel a sold property.") |
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.
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
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): |
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.
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) |
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.
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 |
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.
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) |
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 you did is correct, but relational fields allow for some simplifaction like
record.offer_counts = sum(len(prop.offer_ids) for prop in record.property_id) | |
record.offer_counts = len(record.property_id.offer_ids) |
<header> | ||
<button type="action" |
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.
This a lot of indent 👀
<header> | |
<button type="action" | |
<header> | |
<button type="action" |
_inherit = 'estate.property' | ||
|
||
def sold_property_button(self): | ||
res = super().sold_property_button() |
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.
Your sold_property_button
in estate does not return anything. Thus, this one should not either
res = super().sold_property_button() | |
super().sold_property_button() |
], | ||
}) | ||
|
||
return res |
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.
return res |
def create(self, vals_list): | ||
for vals in vals_list: | ||
property_id = vals.get('property_id') | ||
price = vals.get('price') |
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.
.get
defaults to False
when not found which will trigger an error later when you'll compare a float to a bool
price = vals.get('price') | |
price = vals.get('price', default=0.0) |
Now that your onboarding is done, could you close the PR ? 😄 |
No description provided.