Skip to content

Conversation

@anden-odoo
Copy link

Creation of a new module estate that covers Real Estate Advertisement. There is no module that answer this business case by default.

Creation of a new module estate that covers Real Estate Advertisement.
There is no module that answer this business case by default.
@anden-odoo anden-odoo requested a review from artn-odoo January 19, 2026 14:53
@robodoo
Copy link

robodoo commented Jan 19, 2026

Pull request status dashboard

This adds property model to the estate module with
the objective to store the informations related to
the property.
This adds fields to store information related to
the property (name, description, price,
living area…)
Copy link

@artn-odoo artn-odoo left a comment

Choose a reason for hiding this comment

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

Nice job so far !
We always try to add a new line at the end of every file, for it to be easier to read.
This can be done automatically if you are using vscode by turning on the Insert Final Newline option in the settings.

Comment on lines 4 to 5
_name = "estate_property"
_description = "estate property"

Choose a reason for hiding this comment

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

You don't have to change it all, but we prefer using single quotes in Python files unless using double is required for some semantic reasons.

Comment on lines +2 to +3

class EstateProperty(models.Model):

Choose a reason for hiding this comment

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

We prefer adding 2 blank lines before declaring a class.

Suggested change
class EstateProperty(models.Model):
class EstateProperty(models.Model):

This add a new line at the end of every file to be
easier to read. This changes strings defined by
double quotes to strings with single  quotes.
This also uses two blank lines instead of
one before declaring a class
This gives access rights to the group
base.group_user. This is needed because no access
rights was defined so no users can access the data.
This creates a 3 levels architecture with an action
to interact with property model.
This changes attributes of selling price to read-only
and prevents the copy of availability date and selling
price fields.
This adds a default value for the bedrooms and the
availability date. This also add an active and a
state field.
This cleans the code based on robodoo recommendations
by removing some lines and correcting wrong indentations.
This adds new column names to the list view and
a new form view according to the required specifications.
This creates new search fields, a filter for available
properties and the possibility to group the properties
by postcode.
This creates a new module for the type of a property.
The type of a property record can be observerd in list
and form views.
This adds add salesperson and buyer as field of the
property module. This also adds salesperson and buyer
in the property form.
This creates a new model for tags and adds a
many2many field to property model. This new field
is visible in list and form views.
This creates an offer model and adds it as a
one2many field for the property model.
This creates computed fields (total_area and
best_offer) that are visible in the form view of
the estate_property model.
This creates two new fields to compute deadline from
validity and the inverse way.
Copy link

@artn-odoo artn-odoo left a comment

Choose a reason for hiding this comment

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

Looking good ! Just a few nitpicks

garden_area = fields.Integer(string='Garden Area (sqm)')
garden_orientation = fields.Selection(selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')])
active = fields.Boolean(default=True)
state = fields.Selection(selection=[('new', 'New'), ('offer received', 'Offer Received'), ('offer accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')], required=True, copy=False, default='new')

Choose a reason for hiding this comment

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

This could be broken down into multiple lines as it's quite long

Suggested change
state = fields.Selection(selection=[('new', 'New'), ('offer received', 'Offer Received'), ('offer accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')], required=True, copy=False, default='new')
state = fields.Selection(
selection=[
('new', 'New'),
('offer received', 'Offer Received'),
('offer accepted', 'Offer Accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled')
],
required=True,
copy=False,
default='new'
)

Comment on lines 23 to 33

property_type_id = fields.Many2one('estate_property_type', string='Property Type')

salesperson = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user)
buyer = fields.Many2one('res.partner', copy=False)

property_tag_id = fields.Many2many('estate_property_tag', string='Property Tag')

property_offer_id = fields.One2many('estate_property_offer', 'property_id', string='Property Offer')

total_area = fields.Float(string="Total Area (sqm)", compute='_compute_area')

Choose a reason for hiding this comment

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

The newlines seem to be written kind of randomly, I think you can delete them

Suggested change
property_type_id = fields.Many2one('estate_property_type', string='Property Type')
salesperson = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user)
buyer = fields.Many2one('res.partner', copy=False)
property_tag_id = fields.Many2many('estate_property_tag', string='Property Tag')
property_offer_id = fields.One2many('estate_property_offer', 'property_id', string='Property Offer')
total_area = fields.Float(string="Total Area (sqm)", compute='_compute_area')
property_type_id = fields.Many2one('estate_property_type', string='Property Type')
salesperson = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user)
buyer = fields.Many2one('res.partner', copy=False)
property_tag_id = fields.Many2many('estate_property_tag', string='Property Tag')
property_offer_id = fields.One2many('estate_property_offer', 'property_id', string='Property Offer')
total_area = fields.Float(string="Total Area (sqm)", compute='_compute_area')

for record in self:
record.total_area = record.living_area + record.garden_area

best_price = fields.Float(string="Best offer", compute='_compute_best_price')

Choose a reason for hiding this comment

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

Could be defined in the same place as the other fields

from odoo import fields, models


class EstatePropertyType(models.Model):

Choose a reason for hiding this comment

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

Suggested change
class EstatePropertyType(models.Model):
class EstatePropertyTag(models.Model):

Looks like it should be tag

access_ir_group_user,ir_group_user,model_estate_property,base.group_user,1,1,1,1
access_ir_group_user_type,ir_group_user_type,model_estate_property_type,base.group_user,1,1,1,1
access_ir_group_user_tag,ir_group_user_tag,model_estate_property_tag,base.group_user,1,1,1,1
access_ir_group_user_offer,ir_group_user_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.

Don't forget the empty line at the end of a file

This cleans the fields declaration based on the
review.
This add sold and cancel button on the property form
view to change the status. This also add accept/refuse
button on the offer list view to change the status
of an offer (we can accept only if no other offers
are accepted).
This solves the problem that the selling price was
the same for every property.
Copy link

@artn-odoo artn-odoo left a comment

Choose a reason for hiding this comment

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

Looking good, nice one !

This adds constraints to expected price, selling price
offer price, tag name and price name. This also adds
the constraint selling_price <= expected_price.
This adds an inline list views in the type form.
This cleans the code to respect conventions.
This adds a new view of the state in the property form
view. This also adds automatic ordering to each model.
This also adds manual ordering of property based on
property type.
This changes the statusbar widget for the state depending
on the offers. If there is not offer, then new state.
If there are not offers, then offer received and if
an offer is accepted then offer accepted.
This adds a widget option to prevent "create and edit"
for the type in the property form. This also add color
to tags and a color picker in the form of a tag.
This adds new invisible and readonly fields based
on some conditions.
This adds a editable list views for the offers and
the tags. The field date_availability becomes optional
in the offer list view and is hidden by default.
This adds decorations in property and offer list views.
This adds the filter for available properties by default
and allows the search of properties with a living
area of at least a value.
Copy link

@artn-odoo artn-odoo left a comment

Choose a reason for hiding this comment

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

Small nitpick as always, otherwise it looks good to me !

Comment on lines 77 to 83
_check_expected_price = models.Constraint(
'CHECK(0 < expected_price)',
'A property expected price must be strictly positive')

_check_selling_price = models.Constraint(
'CHECK(0 <= selling_price)',
'A property selling price must be positive')

Choose a reason for hiding this comment

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

SQL constrains are usually declared right after the fields and before the function definition

Comment on lines +52 to +54
_check_offer_price = models.Constraint(
'CHECK(0 < price)',
'An offer price must be strictly positive')

Choose a reason for hiding this comment

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

Same here

This adds a stat button from the form view of a property
type to see the active offers linked to this type.
This prevents the deletion of a property if its state
is 'New' or 'Cancelled'. This also adds a raise when
we try to create an offer with a lower amount than
another offer.
This adds a 'Real Estate Properties' page to the user
form. This shows the properties for which the user
is the salesman.
This creates an invoice for the buyer when clicking
on the button 'Sold'.
This cleans a lost white space.
This adds Kanban view to the property views. The properties
are grouped by type by default and cannot be drag and
drop.
This adds a Counter class as a sub component to call
multiple counters in the playground.
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