aryep - Technical training - #1389
Conversation
qucol-odoo
left a comment
There was a problem hiding this comment.
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?
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
442d916 to
9da69bc
Compare
|
@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
7f5d538 to
17b56ef
Compare
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
left a comment
There was a problem hiding this comment.
Very nice work, good job 👌 I left a few comments :)
Regarding your commit history, I have two comments to share:
- You should only have two
[ADD]commits, because you're only adding two modules:estateandestate_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. - 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.")) |
There was a problem hiding this comment.
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.
| 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"}) |
There was a problem hiding this comment.
Few things to say here:
- An action should (usually) only operate on one record, so you don't need to loop over
self, and instead you should callself.ensure_one()at the very start of the action. - When updating multiple fields of a same record, you can batch the writes using the
\writemethod (as you did further down the method actually) - You should avoid using
searchin 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_groupand then to loop over the result (example)
| 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: |
There was a problem hiding this comment.
| for record in self: | |
| self.ensure_one() |
| 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"}, | ||
| ]) |
There was a problem hiding this comment.
If you don't use them, do not create them 👌
| {"name": "tag1"}, | ||
| {"name": "tag2"}, | ||
| ]) | ||
| cls.estate_properties = cls.env["estate.property"].create([ |
There was a problem hiding this comment.
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:
| 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, ...).
| 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" |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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).
| for offer in self.estate_property_offers: | ||
| if offer.property_id.id == main_property.id: | ||
| offer.unlink() |
There was a problem hiding this comment.
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)]"/> |
There was a problem hiding this comment.
Some characters can be misinterpreted by the XML parser, it's always better to be cautious
| <field name="living_area" filter_domain="[('living_area', '>=', self)]"/> | |
| <field name="living_area" filter_domain="[('living_area', '>=', self)]"/> |

No description provided.