Skip to content
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

[ADD] estate: a new module to manage estate properties #163

Open
wants to merge 27 commits into
base: 18.0
Choose a base branch
from

Conversation

malm-odoo
Copy link

@malm-odoo malm-odoo commented Oct 21, 2024

Module managing estate properties allowing addition of properties alongside their details (type, area, etc.). Keeps track of the offers for each property, prices for each offer, and the buyer.

@aboo-odoo
Copy link

Ping

Lucky you I'm your reviewer :D

@malm-odoo malm-odoo force-pushed the 18.0-fix-invoices-malm branch 2 times, most recently from c929665 to d55d464 Compare October 22, 2024 07:40
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.

Very good work, mainly styling comments 👍

Two last comments I can make so far are:

  • Could you rename your PR to something like [ADD] estate: a new module to manage estate properties and embellish a bit your description
  • Mind that commit messages (and PR title) must be as followed [TAG] module(s): short description. The body of your commit message is as important as the title. I advice you to build that body use the following three sections:
    1. Problem
    2. Objective
    3. Solution
      and end your commit message with a reference to the task your assigned to (here there is none so you can put task-xxxxxx). Check the commit message I pushed on your branch for inspiration.

Lastly, have all your .idea diffs in the PRs, could you make those disappear ? Thanks a lot.

Once again, good work ! Keep it up 🥇

from odoo import models, fields
from dateutil.relativedelta import relativedelta

Choose a reason for hiding this comment

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

Regarding imports, we try to order them as followed:

  1. Python imports
  2. Other imports
  3. Odoo imports
Suggested change
from odoo import models, fields
from dateutil.relativedelta import relativedelta
from dateutil.relativedelta import relativedelta
from odoo import fields, models



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"

_name = "estate_property"
_description = "Create real estate properties and keep track of status"

name = fields.Char(required=True)

Choose a reason for hiding this comment

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

Comment that also applies to all other fields: also add the string parameter so that the field can get translated to other languages 👍

garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string='Type',

Choose a reason for hiding this comment

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

Quote convention is

  • '' for strings the users do not see
  • "" for strings the users see
Suggested change
string='Type',
string="Type",

active = fields.Boolean('Active', default=True)
state = fields.Selection(
string='State',
selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', "Cancelled")]

Choose a reason for hiding this comment

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

Since the selection is a bit long here, it would be nice to indent it to make it easier to read and manage in the future

Suggested change
selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', "Cancelled")]
selection=[
('new', "New"),
('offer_received', "Offer Received"),
('offer_accepted', "Offer Accepted"),
('sold', "Sold"),
('cancelled', "Cancelled"),
]

</field>
</record>

<record id="estate_property_list_view_search" 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_list_view_search" model="ir.ui.view">
<record id="estate_property_view_search" model="ir.ui.view">

Comment on lines 36 to 38
<filter string="Available" name="state" 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.

Easier to read

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

<filter string="Available" name="state" domain="['|',
('state', '=', 'new'),
('state', '=', 'offer_received')]"/>
<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.

Not needed, try without and you'll see that the group_by section shows

Comment on lines 56 to 67
<group>
<field name="postcode"/>
</group>
<group>
<field name="expected_price"/>
</group>
<group>
<field name="date_availability"/>
</group>
<group>
<field name="selling_price"/>
</group>

Choose a reason for hiding this comment

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

No need to create a different group for each

Suggested change
<group>
<field name="postcode"/>
</group>
<group>
<field name="expected_price"/>
</group>
<group>
<field name="date_availability"/>
</group>
<group>
<field name="selling_price"/>
</group>
<group>
<field name="postcode"/>
<field name="expected_price"/>
<field name="date_availability"/>
<field name="selling_price"/>
</group>

</form>
</field>
</record>
</odoo>

Choose a reason for hiding this comment

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

Line break

name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(default=fields.Datetime.today() + relativedelta(months=3), copy=False)

Choose a reason for hiding this comment

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

The default value here won't work, the reason being the, currently, it is compute once when you start your server and then the compute default value will remain unchanged until you next stop-start your server. That means that, currently, if you start a server today that will run forever, the default value will be 22/01 forever.

To solve the issue you need to use a lambda function

@malm-odoo malm-odoo force-pushed the 18.0-fix-invoices-malm branch 2 times, most recently from 042bf07 to 37b90b6 Compare October 23, 2024 12:58
@malm-odoo malm-odoo force-pushed the 18.0-fix-invoices-malm branch 2 times, most recently from 6567d13 to e8020f0 Compare October 24, 2024 13:04
malm-odoo and others added 19 commits October 24, 2024 15:21
Problem
---------
Users may want to cancel several houses and flats at once, currently
this is not possible; they have to do it one by one.

Objective
---------
Add a way to cancel several estates in one go.

Solution
---------
Add a server action in the demo data so that it is available for new
users. Old users can create the same server action on their database.

task-123456
Problem
---------
Users may want to see the total area after adding the garden area and the living area, as well as the best offer after adding multiple offers
to the property. They also might want to either enter a validity number of days or a deadline, and the other should be computed automatically.

Objective
---------
Add a field (total area) dependant on two fields (garden and living area). Add another field which is the maximum of all offer prices.
Add a relation between the validity date and the deadline fields (after creating them).

Solution
---------
Add a computed field for the total area depending on garden and living area fields. Add a computed field for the best price depending on the
offer prices, computing the maximum of them all. Add an computed field for the date deadline and add both a compute and inverse function for it.

task-xxxxxx
…de their actions.

Problem
---------
Users may want to modify the property status through buttons, and see their actions reflected in the data. Same applies to the offer status.

Objective
---------
Add a sold and cancel button for the property, modify the status based on their activity.
Add accept and refuse buttons for the offers, modify the status based on their activity.
Reflect the actions of offers on the property data.

Solution
---------
Add a sold and cancel button in the header part of the XML file, alongside their functions applied on press.
Add a accept and refuse button in the list view of the offers.
Based on which is clicked, modify the selling price and the buyer of the property as well as the status of the offer.

task-xxxxxx
Problem
---------
Users may enter incorrect data like a negative expected or offer price. Also, they may create multiple property types and tags
of the same name. The selling price of a property shouldn't be less than 90% of the expected price.

Objective
---------
Add constraints to the field boxes (expected, selling, and offer price). The property tags and types should have constraints too.
Add constraints to prevent selling prices from being lower than 90% of the expected price.

Solution
---------
Added an SQL constraint checking the expected, selling, and offer price to be positive.
Added an SQL constraint to check the uniqueness of the property tags and types.
Added a Python constraint checking the selling price to be not lower than 90% of the expected price.
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.

Could you update the PR title and add a lil description ? Thanks 🏅

Also, I have all your .idea stuff here, could you remove them from the diff ?

@@ -0,0 +1 @@
from . import estate_property, estate_property_type, estate_property_tag, estate_property_offer, users

Choose a reason for hiding this comment

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

Better indent

Suggested change
from . import estate_property, estate_property_type, estate_property_tag, estate_property_offer, users
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
from . import users

Comment on lines +40 to +46
property_type_id = fields.Many2one('estate.property.types', string="Property Type")
salesman_id = fields.Many2one('res.users', string="Salesman", default=lambda self: self.env.user)
buyer_id = fields.Many2one('res.partner', string="Buyer")
tag_ids = fields.Many2many('estate.property.tag', string="Property Tags")
offer_ids = fields.One2many('estate.property.offer', 'property_id', string="Offers")
total_area = fields.Float(compute="_compute_total_area", string="Total Area")
best_price = fields.Float(compute="_compute_best_price", string="Best Price")

Choose a reason for hiding this comment

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

Those are missing the string option, useful for translation 👍

Copy link
Author

Choose a reason for hiding this comment

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

Isn't it the second parameter in all of these? The string=""?

Comment on lines 57 to 58
for record in self:
if record.buyer_id and float_compare(record.selling_price, 0.9 * record.expected_price, 5) == -1:

Choose a reason for hiding this comment

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

Use of a meaningful variable name to ease the understanding of the method

Suggested change
for record in self:
if record.buyer_id and float_compare(record.selling_price, 0.9 * record.expected_price, 5) == -1:
for estate in self:
if estate.buyer_id and float_compare(record.selling_price, 0.9 * record.expected_price, 5) == -1:

def _compute_best_price(self):
for record in self:
prices = record.offer_ids.mapped('price')

Choose a reason for hiding this comment

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

No need of empty line here

for record in self:
prices = record.offer_ids.mapped('price')

record.best_price = max(prices) if len(prices) > 0 else 0

Choose a reason for hiding this comment

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

Lil python trick

Suggested change
record.best_price = max(prices) if len(prices) > 0 else 0
record.best_price = max(prices, default=0)

Comment on lines +21 to +22
partner_id = fields.Many2one('res.partner', string="Partner", required=True)
property_id = fields.Many2one('estate.property', string="Property", required=True)

Choose a reason for hiding this comment

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

String parameter

record.date_deadline = record.create_date + timedelta(days=record.validity)

def _inverse_date_deadline(self):
for record in self:

Choose a reason for hiding this comment

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

No placing it everywhere but all you looped method need this change hehe

Suggested change
for record in self:
for offer in self:

Comment on lines 13 to 14
offer_ids = One2many('estate.property.offer', 'property_type_id')
offer_count = fields.Integer(compute="_compute_offer_count", string="Offer Count")

Choose a reason for hiding this comment

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

String

def action_sold(self):
invoices = []
for record in self:
journal = self.env['account.journal'].search([('type', '=', 'sale')])

Choose a reason for hiding this comment

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

This could return several journal, add limit=1 to make sure you only get one 👍

Comment on lines +34 to +36
invoices.append(invoice)

self.env['account.move'].create(invoices)
Copy link

@aboo-odoo aboo-odoo Oct 25, 2024

Choose a reason for hiding this comment

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

Very nice to think of perf like that, honestly very impressed

It could be even nicer by placing journal = self.env[].search outside of the loop, just before it so that you only search once

@malm-odoo malm-odoo changed the title test [ADD] estate: a new module to manage estate properties Oct 25, 2024
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.

2 participants