Skip to content

aryep - Technical training - #1389

Open
aryep-odoo wants to merge 21 commits into
odoo:19.0from
odoo-dev:19.0-tutorial-aryep
Open

aryep - Technical training #1389
aryep-odoo wants to merge 21 commits into
odoo:19.0from
odoo-dev:19.0-tutorial-aryep

Conversation

@aryep-odoo

Copy link
Copy Markdown

No description provided.

@robodoo

robodoo commented Aug 17, 2026

Copy link
Copy Markdown

Pull request status dashboard

@aryep-odoo aryep-odoo self-assigned this Aug 17, 2026

@qucol-odoo qucol-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 great work 🙏 I left a few comments, try to apply the suggestions if you agree with them :)

Could you try to rename your commits to match our guideline?

Comment thread estate/models/__init__.py
Comment thread estate/models/estate_property.py Outdated
Comment thread estate/views/estate_property_views.xml Outdated
Comment thread estate/views/estate_property_views.xml Outdated
Comment thread estate/views/estate_property_views.xml Outdated
Comment thread estate/__manifest__.py
Comment thread estate/models/estate_property.py Outdated
Based on the instructions from the tutorial's Chapter 2, the first steps for the new app for Real Estate management was achieved
Based on instructions on tutorial's Chapter 3, it was added a modal to represent the Estate Properties that will be managed
Based on the tutorial's Chapter 4, some basic security rules were implemented to keep a more tight user access control
…pulation

Based on the tutorial's Chapter 5, several improvements were made to certain fields of the Estate Property model while also creating some customize basic views (as templates) to gain more control for future developments over the app
Based on the tutorial's Chapter 6, new views where added to adjust to the app's purpose
…d access

Based on the tutorial's Chapter 7, in order to give more shape to our data, new models and the relations between them were started being specified
Based on the tutorial's Chapter 8, it was introduced several computed and onchange fields to serve as entry point for business logic that will help solve the business' problems
Based on the comments left by one of the mentors and the documentation, several fixes and improvements have been applied to the whole tutorial new module
@aryep-odoo
aryep-odoo force-pushed the 19.0-tutorial-aryep branch from 442d916 to 9da69bc Compare August 18, 2026 14:51
@aryep-odoo

aryep-odoo commented Aug 18, 2026

Copy link
Copy Markdown
Author

@qucol-odoo thank you so much for your first batch of comments! There were definitely several things and details I had missed initially but most of them should have been resolved by now

I left some follow-ups in some of your recommendations with some questions. Let me know what you think, and once again, thanks for taking the time to check the PR

Based on the tutorial's Chapter 9, new actions and their UI elements were added. This will allow to have a better handling for property offers, adapting better to business needs
Based on the tutorial's Chapter 10, several SQL and Python constraints have been added, with the purpose of continuing giving shape to the app given the business requirements
Based on tutorial's Chapter 11, several new UI and business logic changes have been made in order to be able to access more easily to the related/connected information
Based on tutorial's Chapter 13, custom fields and changes to models were added to continue connecting and improving the relation between all modules to achieve a better user experience
There was an error that made it impossible for the project to run due to a conflicting declaration of a menuitem
…counting apps

Based on the tutorial's Chapter 13, a new app was created to modularize the logic that connects the new estate and accounting apps, in a way that will allow to users to select it separately if their business needs requires it.
Pass of linting to the whole estate app, based on the rules set up for Ruff to keep coding guidelines in the project
Pass of linting to the whole estate app, based on the rules set up for Ruff to keep coding guidelines in the project
@aryep-odoo
aryep-odoo force-pushed the 19.0-tutorial-aryep branch from 7f5d538 to 17b56ef Compare August 20, 2026 08:40
Based on tutorial's Chapter 14, a Kanban view was added in order to have a more options to display the module's data
Runbot is presenting an error in the 'tutorial' (build) pipeline that is due to the data import order in the manifest of the estate app
Some behaviors of the estate were enhanced by adding validations done server side, besides the client ones already implemented. This will allow to have an extra layer of security when handling the data
Based on the tutorial's 'Safeguard your code with unit tests', several unit tests were added to the estate module to keep behavior in check

@qucol-odoo qucol-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.

Very nice work, good job 👌 I left a few comments :)

Regarding your commit history, I have two comments to share:

  1. You should only have two [ADD] commits, because you're only adding two modules: estate and estate_account. All the other commits that are not adding new modules should (probably) be [IMP]. As a reminder, you can see all the tags here.
  2. In a real case scenario, it wouldn't make much sense to have commits fixing logic/syntax issues from other commits belonging to the same PR, because it means that you are introducing an issue and fixing it simultaneously: it would be better to simply not introduce it at all. It would be a great exercise for you to try to get rid of all the [FIX] commits in your PR (hint: you should probably use git rebase -i). Ultimately you could also squash commits to have one per chapter, or even squash everything as one big [ADD] commit, it's kinda up to you, as long as you experiment a bit with the interactive rebasing (mostly the edit and squash options) :)

def _check_selling_price(self):
for record in self:
if record.selling_price and float_compare(record.selling_price, 0.9 * record.expected_price, precision_digits=2) < 0:
raise ValidationError(_("Selling price cannot be lower than 90% of the expected 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.

Good job finding the translation function! It is a better practice to use it as self.env._(...) than simply _, as explained in its docstring, for the reasons explained in this commit.

Comment on lines +70 to +82
def action_accept(self):
for record in self:
record.status = "accepted"
record.property_id.state = "offer_accepted"
record.property_id.partner_id = record.partner_id
record.property_id.selling_price = record.price

# Refuse other offers for the same property
other_offers = self.search([
("property_id", "=", record.property_id.id),
("id", "!=", record.id),
])
other_offers.write({"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.

Few things to say here:

  1. An action should (usually) only operate on one record, so you don't need to loop over self, and instead you should call self.ensure_one() at the very start of the action.
  2. When updating multiple fields of a same record, you can batch the writes using the \write method (as you did further down the method actually)
  3. You should avoid using search in a loop. In this case it ends up being fine because we're removing the loop, but otherwise it would have been a bad practice. An alternative is to use _read_group and then to loop over the result (example)
Suggested change
def action_accept(self):
for record in self:
record.status = "accepted"
record.property_id.state = "offer_accepted"
record.property_id.partner_id = record.partner_id
record.property_id.selling_price = record.price
# Refuse other offers for the same property
other_offers = self.search([
("property_id", "=", record.property_id.id),
("id", "!=", record.id),
])
other_offers.write({"status": "refused"})
def action_accept(self):
self.ensure_one()
self.status = "accepted"
self.property_id.write({
'state': 'offer_accepted',
'partner_id': self.partner_id.id,
'selling_price': self.price,
})
# Refuse other offers for the same property
self.search([
("property_id", "=", self.property_id.id),
("id", "!=", record.id),
]).write({"status": "refused"})

other_offers.write({"status": "refused"})

def action_refuse(self):
for record in self:

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
for record in self:
self.ensure_one()

Comment thread estate/tests/common.py
Comment on lines +10 to +17
cls.estate_property_types = cls.env["estate.property.type"].create([
{"name": "House"},
{"name": "Apartment"},
])
cls.estate_property_tags = cls.env["estate.property.tag"].create([
{"name": "tag1"},
{"name": "tag2"},
])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If you don't use them, do not create them 👌

Comment thread estate/tests/common.py
{"name": "tag1"},
{"name": "tag2"},
])
cls.estate_properties = cls.env["estate.property"].create([

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Creating in batch is a great practice, but do you really need two properties? Their only difference is the price, which I don't think really changes anything in your tests. I would either:

  • Create only one property
  • Create two properties that have significant differences (for example, one with a garden and one without)

As a side note, keeping the two properties together doesn't sound like the best idea here, since it forces you to call `search' at the start of each test, which is unnecessarily costly. Instead, you could do the following:

Suggested change
cls.estate_properties = cls.env["estate.property"].create([
cls.property_1, cls.property_2 = cls.env["estate.property"].create([

with more appropriate names if your properties have significant differences (e.g., property_with_garden, ...).

Comment on lines +13 to +17
main_property = cls.estate_properties.search([("name", "=", "Property 1")])
main_property.living_area = 90
main_property.garden = True
main_property.garden_area = 100
main_property.garden_orientation = "south"

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 could just create a property with those values from the start, instead of searching and editing an existing one

offer.unlink()

with self.assertRaises(ValidationError):
self._sell_main_property()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The idea of the _sell_main_property is good, but here you search for the main_property twice because of it. You can fix that by applying my proposition above (separate the properties at creation), or by updating the _sell_main_property to something like _sell_property(property).

Comment on lines +50 to +52
for offer in self.estate_property_offers:
if offer.property_id.id == main_property.id:
offer.unlink()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Couldn't you just use the other property, that doesn't have any offers? Unless you want to test the deletion of offers, but then you should precise it in the docstring.

<field name="postcode"/>
<field name="expected_price"/>
<field name="bedrooms"/>
<field name="living_area" filter_domain="[('living_area', '>=', self)]"/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Some characters can be misinterpreted by the XML parser, it's always better to be cautious

Suggested change
<field name="living_area" filter_domain="[('living_area', '>=', self)]"/>
<field name="living_area" filter_domain="[('living_area', '&gt;=', self)]"/>

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