Skip to content

Conversation

@jedel-odoo
Copy link

No description provided.

@robodoo
Copy link

robodoo commented Oct 20, 2025

Pull request status dashboard

@Mathilde411 Mathilde411 changed the title [ADD] Estate:createthe app JEDEL Onboarding Oct 20, 2025
@Mathilde411 Mathilde411 self-requested a review October 20, 2025 11:55
@Mathilde411 Mathilde411 self-assigned this Oct 20, 2025
Copy link

@Mathilde411 Mathilde411 left a comment

Choose a reason for hiding this comment

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

Small comments :)

@Mathilde411 Mathilde411 self-requested a review October 21, 2025 11:18
@Mathilde411 Mathilde411 removed their assignment Oct 21, 2025
Copy link

@Mathilde411 Mathilde411 left a comment

Choose a reason for hiding this comment

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

Good work here !
I see that your runbot is red because of style, so you may fix that.
Also be mindful about the naming of files/xmlids: https://www.odoo.com/documentation/19.0/contributing/development/coding_guidelines.html#xml-ids-and-naming

Comment on lines 82 to 87
@api.constrains('expected_price', 'selling_price')
def _check_selling_price(self):
for record in self:
if not float_is_zero(record.selling_price, precision_digits=3):
if float_compare(record.selling_price, record.expected_price*0.9, precision_digits=3) < 0:
raise UserError(r"The selling price must be at least 90% of the expected price !")

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

Still

Copy link
Author

Choose a reason for hiding this comment

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

I don't understand the change I have to make

estate.access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
estate.access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1
estate.access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1
estate.access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1 No newline at end of file

Choose a reason for hiding this comment

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

Missing newline

<field name="bedrooms"/>
<field name="living_area" string="Living Area (sqm)"/>
<field name="facades"/>
<filter string="Available" name="available" domain="['|', ('state', '=', 'new'), ('state', '=', 'offer_received')]"/>

Choose a reason for hiding this comment

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

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

Simpler way when comparing the same variable :)

Copy link

@Mathilde411 Mathilde411 left a comment

Choose a reason for hiding this comment

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

Very good work !
Still a few things to fix left from yesterday's pass :)


class EstateProperty(models.Model):
_name = "estate.property"
_description = "Property for the Real Estate app"

Choose a reason for hiding this comment

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

Suggested change
_description = "Property for the Real Estate app"
_description = "Estate Property"

_description contains a short human-readable name for the model

Comment on lines 82 to 87
@api.constrains('expected_price', 'selling_price')
def _check_selling_price(self):
for record in self:
if not float_is_zero(record.selling_price, precision_digits=3):
if float_compare(record.selling_price, record.expected_price*0.9, precision_digits=3) < 0:
raise UserError(r"The selling price must be at least 90% of the expected price !")

Choose a reason for hiding this comment

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

Still

Comment on lines 52 to 54
if len(offers) > 0:
if val['price'] < max(offers.mapped('price')):
raise UserError("You cannot create an offer with a lower amount than an existing offer !")

Choose a reason for hiding this comment

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

Suggested change
if len(offers) > 0:
if val['price'] < max(offers.mapped('price')):
raise UserError("You cannot create an offer with a lower amount than an existing offer !")
if offers and val['price'] < max(offers.mapped('price')):
raise UserError("You cannot create an offer with a lower amount than an existing offer !")

Cleaner that way :)If you have an empty recordset, it will automatically be cast to False

Copy link

@Mathilde411 Mathilde411 left a comment

Choose a reason for hiding this comment

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

Very good overall.
Could you just squash so that you have 1 commit per chapter ?

@@ -0,0 +1,3 @@
{
"files.insertFinalNewline": true
} No newline at end of file

Choose a reason for hiding this comment

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

Ironically, this is missing the newline :x

Comment on lines +99 to +103
@api.ondelete(at_uninstall=False)
def _unlike_if_stats_new_or_cancelled(self):
for record in self:
if record.state in ('new', 'cancelled'):
raise UserError("You cannot delete a new or cancelled property !")

Choose a reason for hiding this comment

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

Suggested change
@api.ondelete(at_uninstall=False)
def _unlike_if_stats_new_or_cancelled(self):
for record in self:
if record.state in ('new', 'cancelled'):
raise UserError("You cannot delete a new or cancelled property !")
@api.ondelete(at_uninstall=False)
def _unlink_if_stats_new_or_cancelled(self):
for record in self:
if record.state in ('new', 'cancelled'):
raise UserError("You cannot delete a new or cancelled property !")

Spelling.
Also method ordering ! This is one of the CRUD methods, it should go higher :)

Comment on lines +45 to +46
for offer in self:
offer.status = "refused"

Choose a reason for hiding this comment

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

Suggested change
for offer in self:
offer.status = "refused"
self.status = "refused"
return True

Assigning attributes on recordsets works !

Comment on lines +48 to +58
@api.model_create_multi
def create(self, vals_list):
for val in vals_list:
if self.env['estate.property'].browse(val['property_id']).state == 'sold':
raise UserError("You cannot create an offer for a sold property !")
offers = self.env['estate.property.offer'].search([('property_id', '=', val['property_id'])])
if offers and val['price'] < max(offers.mapped('price')):
raise UserError("You cannot create an offer with a lower amount than an existing offer !")
offers = super().create(vals_list)
offers.property_id.state = 'offer_received'
return offers

Choose a reason for hiding this comment

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

Suggested change
@api.model_create_multi
def create(self, vals_list):
for val in vals_list:
if self.env['estate.property'].browse(val['property_id']).state == 'sold':
raise UserError("You cannot create an offer for a sold property !")
offers = self.env['estate.property.offer'].search([('property_id', '=', val['property_id'])])
if offers and val['price'] < max(offers.mapped('price')):
raise UserError("You cannot create an offer with a lower amount than an existing offer !")
offers = super().create(vals_list)
offers.property_id.state = 'offer_received'
return offers
@api.model_create_multi
def create(self, vals_list):
if self.env['estate.property'].search_count([('id', 'in', [val.get('property_id') for val in vals_list]), ('state', '=', 'sold')]): # If the number of corresponding object != 0...
raise UserError("You cannot create an offer for a sold property !")
for val in vals_list:
offers = self.env['estate.property.offer'].search([('property_id', '=', val['property_id'])])
if offers and val['price'] < max(offers.mapped('price')):
raise UserError("You cannot create an offer with a lower amount than an existing offer !")
offers = super().create(vals_list)
offers.property_id.state = 'offer_received'
return offers

Doing browse in a loop is a BIG nono. It means you will do 1 SQL request per element of the recordset, which is extremely bad for performance. Prefer doing checks like this ouside of the loop, with things like this
Also this is a CRUD method so it should go hisher in the ordering

Choose a reason for hiding this comment

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

Also you should use float compare here.
In reality you'd have a currency attached to your price, and you'd use that for comparison.

Comment on lines +27 to +31
partner = self.env['res.partner'].create(
[{
'name': 'My Favorite Partner'
}]
)

Choose a reason for hiding this comment

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

Create this guy in the setUp. means the tests will run faster if you need a partner somewhere else in the tests

Comment on lines +15 to +29
Command.create({
'name': self.name,
'quantity': 1,
'price_unit': self.selling_price
}),
Command.create({
'name': 'Taxes',
'quantity': 1,
'price_unit': self.selling_price * 0.06
}),
Command.create({
'name': 'Administrative fees',
'quantity': 1,
'price_unit': 100.00
})

Choose a reason for hiding this comment

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

Just saw this but I think this is not what the exercise asks you to do.

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