Skip to content

[ADD] estate: basic module creation#1245

Open
simal-odoo wants to merge 12 commits intoodoo:19.0from
odoo-dev:19.0-technical-training-simal
Open

[ADD] estate: basic module creation#1245
simal-odoo wants to merge 12 commits intoodoo:19.0from
odoo-dev:19.0-technical-training-simal

Conversation

@simal-odoo
Copy link
Copy Markdown

Created init and manifest files

Created init and manifest files
@robodoo
Copy link
Copy Markdown

robodoo commented Apr 22, 2026

Pull request status dashboard

@simal-odoo simal-odoo closed this Apr 22, 2026
@simal-odoo simal-odoo reopened this Apr 22, 2026
@Mathilde411 Mathilde411 self-requested a review April 22, 2026 11:08
Added fields in __manifest__.py
Created models folder, EstateProperty model with the required fields and imported it in __manifest__.py files
Created data folder and file ir.model.access.csv inside it giving read, write, create and unlink permissions to the group base.group_user
Added views and actions for estate.property.
Added a 3 level menu with the basic action
Added and improved attributes
Copy link
Copy Markdown

@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 job !
I was kinda nitpicky but this is a good job overall :)

Comment thread estate/data/estate_menus.xml Outdated
Comment thread estate/data/estate_menus.xml Outdated
Comment thread estate/data/ir.model.access.csv Outdated
Comment thread estate/data/ir.model.access.csv Outdated
Comment thread estate/data/ir.model.access.csv Outdated
Comment thread estate/__manifest__.py Outdated
Comment thread estate/__manifest__.py Outdated
Comment thread estate/__manifest__.py Outdated
Comment thread estate/__manifest__.py Outdated
Comment thread estate/__manifest__.py Outdated
- Added newlines to the relevant files
- Changed license and version
- Moved menus form data to views folder
- Now follows formatting rules
Added list views to main menu
Added form view when creating property
Added search views, filters and group
@simal-odoo simal-odoo force-pushed the 19.0-technical-training-simal branch from 0b858e1 to 70fa394 Compare April 23, 2026 13:05
Added property types and its menu and views
Added property tags and its menu and views
Added property offers and its views
Added necessary fields and links
Added total area (garden area + living area)
Added best price
Added validity date
Added onchanges on garden fields
Copy link
Copy Markdown

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

Pretty good :D

Comment thread estate/models/estate_property.py Outdated
Comment on lines +7 to +17
name = fields.Char('Property Name', required=True)
description = fields.Text('Description')
postcode = fields.Char('Postcode')
date_availability = fields.Date('Availability Date', copy=False, default=lambda self: fields.Date.add(fields.Date.today(), months=3))
expected_price = fields.Float('Expected Price', required=True)
selling_price = fields.Float('Selling Price', readonly=True, copy=False)
bedrooms = fields.Integer('Bedrooms', default=2)
living_area = fields.Integer('Living Area')
facades = fields.Integer('Facades')
garage = fields.Boolean('Garage')
garden = fields.Boolean('Garden')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Strings are not needed in most of those. The default would work.

Comment thread estate/models/estate_property.py Outdated
Comment on lines +19 to +64
@api.onchange('garden')
def _onchange_garden(self):
if not self.garden:
self.garden_area = 0
self.garden_orientation = False
else:
self.garden_area = 10
self.garden_orientation = 'north'

garden_area = fields.Integer('Garden Area')
garden_orientation = fields.Selection(
selection=[
('north', 'North'),
('south', 'South'),
('east', 'East'),
('west', 'West'),
],
)
state = fields.Selection(
selection=[
('new', 'New'),
('offer received', 'Offer Received'),
('offer accepted', 'Offer Accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled'),
],
default='new',
required=True,
)
property_type_id = fields.Many2one('estate.property.type', string="Property Type")
buyer_id = fields.Many2one('res.partner', string="Buyer", copy=False)
salesperson_id = fields.Many2one('res.users', string="Salesperson", default=lambda self: self.env.uid)
tag_ids = fields.Many2many('estate.property.tag', string="Property Tag")
offer_ids = fields.One2many('estate.property.offer', 'property_id', string="Offer")

@api.depends('living_area', 'garden_area')
def _compute_total_area(self):
for line in self:
line.total_area = line.living_area + line.garden_area
total_area = fields.Integer('Total Area', compute="_compute_total_area")

@api.depends('offer_ids.price')
def _compute_best_price(self):
for line in self:
line.best_price = max(line.mapped('offer_ids.price') or [0])
best_price = fields.Float('Best Offer', compute="_compute_best_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.

Be careful about attribute ordering in a model. fields should be before the rest and every type of method should be appearing in a set order

Comment thread estate/models/estate_property.py Outdated
@api.depends('offer_ids.price')
def _compute_best_price(self):
for line in self:
line.best_price = max(line.mapped('offer_ids.price') or [0])
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
line.best_price = max(line.mapped('offer_ids.price') or [0])
line.best_price = max(line.mapped('offer_ids.price'), default=0)

a little bit better :)

Comment thread estate/models/estate_property_offer.py Outdated
Comment on lines +16 to +17
partner_id = fields.Many2one("res.partner", string="Partner", required=True)
property_id = fields.Many2one("estate.property", string="Property", required=True)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

On a many2one default string attribute is what comes before _id capitalized, so here it'd be the same, so you don't need to add it :)
Also works with many2many and one2many but with _ids instead of _id :D

Comment thread estate/models/estate_property_offer.py Outdated
Comment on lines +20 to +29
@api.depends('create_date', 'validity')
def _compute_date_deadline(self):
for offer in self:
if offer.validity:
if offer.create_date:
offer.date_deadline = offer.create_date + timedelta(days=offer.validity)
else:
offer.date_deadline = fields.Date.today() + timedelta(days=offer.validity)
else:
offer.date_deadline = False
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Validity is an integer field, it always has a value

Suggested change
@api.depends('create_date', 'validity')
def _compute_date_deadline(self):
for offer in self:
if offer.validity:
if offer.create_date:
offer.date_deadline = offer.create_date + timedelta(days=offer.validity)
else:
offer.date_deadline = fields.Date.today() + timedelta(days=offer.validity)
else:
offer.date_deadline = False
@api.depends('create_date', 'validity')
def _compute_date_deadline(self):
for offer in self:
offer.date_deadline = (offer.create_date or fields.Date.today()) + timedelta(days=offer.validity)

Comment on lines +31 to +39
def _inverse_date_deadline(self):
for offer in self:
if offer.date_deadline:
if offer.create_date:
offer.validity = (offer.date_deadline - offer.create_date).days
else:
offer.validity = (offer.date_deadline - fields.Date.today()).days
else:
offer.validity = 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Same here

Comment thread estate/models/estate_property_offer.py Outdated
else:
offer.validity = 0

date_deadline = fields.Date("Deadline", compute="_compute_date_deadline", inverse="_inverse_date_deadline", store=True)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Attribute ordering here too

Comment on lines +25 to +27
<field name="price" width="200px"/>
<field name="partner_id" width="240px" />
<field name="validity" width="240px" />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why the widths here ? Would this work correctly on all screen sizes or just yours ?

simal-odoo and others added 4 commits April 27, 2026 09:56
Added action to cancel or set a property as sold
Added action to accept or refuse offer and set selling price and buyer
- Implemented required changes from PR
- Fixed bug with deadline and validity of offers
- Added SQL constraints on expected price, offer price, tags and types
- Added python constraint on selling price
This commit is here to introduce the testing framework of Odoo. Try running the
tests using `--test-tags :TestEstateProperty`.

Doc:
https://www.odoo.com/documentation/18.0/developer/reference/backend/testing.html?highlight=tests#invocation

The tests were made such that the first one should work but the second one
should fail. Your job is to ensure both tests pass in the end. You should update
the behaviour of the appropriate models.

If you want, you can also add a small test of your own to get a feel for it.
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.

4 participants