Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0eec948
App 'Real Estate' create
JeromeVerkyndt Sep 15, 2026
5b98124
Chapter 3 Create models and basic field
JeromeVerkyndt Sep 15, 2026
7fa6834
Chapter 4 Add access rights
JeromeVerkyndt Sep 15, 2026
fbaf256
Chapter 5 Add UI and new fields
JeromeVerkyndt Sep 16, 2026
c432853
Chapter 6 create basic view and filter
JeromeVerkyndt Sep 17, 2026
0d0f86b
[FIX] estate: Use of a lambda for the 'date_availability' default par…
JeromeVerkyndt Sep 17, 2026
f76bb43
[IMP] estate: add type, tag, and offer.
JeromeVerkyndt Sep 18, 2026
94b80ec
[IMP] estate: add computed field for 'total_area'.
JeromeVerkyndt Sep 18, 2026
c4ecea2
[CLN] estate: Correct code style
JeromeVerkyndt Sep 18, 2026
49a295c
[IMP] estate: add cumputed field for 'best_price'.
JeromeVerkyndt Sep 18, 2026
65fb4b8
[IMP] estate: add validity et date_deadline field with compute and in…
JeromeVerkyndt Sep 18, 2026
63ff040
[IMP] estate: add a onchange on 'garden' for set 'garden_area' and 'o…
JeromeVerkyndt Sep 18, 2026
8189ece
[IMP] estate: add button cancel and sold for property.
JeromeVerkyndt Sep 18, 2026
4e7758c
[IMP] estate: add button 'Accept' and 'Refuse' for property offer, an…
JeromeVerkyndt Sep 18, 2026
90ca7b1
[IMP] estate: add constraints SQL for type and prices, and python for…
JeromeVerkyndt Sep 21, 2026
0f31345
[IMP] estate: add list of properties in type property.
JeromeVerkyndt Sep 21, 2026
c614f66
[IMP] estate: add default order for each model.
JeromeVerkyndt Sep 21, 2026
d019a8f
[IMP] estate: adding important file
lost-odoo Sep 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions estate/DO_NOT_TOUCH.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file should not be removed.
1 change: 1 addition & 0 deletions estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
17 changes: 17 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
'name': 'Real Estate',
'version': '1.9',
'depends': [
'base',
],
'installable': True,
'application': True,
'data': [
'security/ir.model.access.csv',
'views/estate_property_offer_view.xml',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_menus.xml',
],
}
4 changes: 4 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
109 changes: 109 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_is_zero, float_compare


class Property(models.Model):
_name = "estate.property"
_description = "Estate property model"
_order = "id desc"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do not forget to make you runbot green, here ci/style would complain

_check_expected_price = models.Constraint(
'CHECK(expected_price > 0)',
'The expected price of a property must be positive.'
)
_check_selling_price = models.Constraint(
'CHECK(selling_price > 0)',
'The selling price of a property must be positive.'
)

name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(copy=False, default=lambda self: fields.Date.add(fields.Date.today(), months=3))
expected_price = fields.Float(required=True)
selling_price = fields.Float(copy=False, readonly=True)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string='Orientation',
selection=[
('north', 'North'),
('south', 'South'),
('east', 'East'),
('west', 'West')
],
help="Orientation is used to define the orientation of the garden"
)
active = fields.Boolean("Active", default=True)
state = fields.Selection(
string='State',
selection=[
('new', 'New'),
('offer_received', 'Offer Received'),
('offer_accepted', 'Offer Accepted'),
('sold', 'Sold'),
('canceled', 'Canceled')
],
required=True,
default="new"
)
property_type_id = fields.Many2one("estate.property.type", string="Property Types")
salesman = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user)
buyer = fields.Many2one('res.partner', string='Buyer')
tag_ids = fields.Many2many("estate.property.tag", string="Property Tag")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")
total_area = fields.Integer(compute="_compute_area", string="Total Area (sqm)")
best_price = fields.Float(compute="_compute_best_price", string="Best Offer")

@api.depends('living_area', 'garden_area')
def _compute_area(self):
for record in self:
record.total_area = record.living_area + record.garden_area

@api.depends('offer_ids')
def _compute_best_price(self):
for record in self:
if record.offer_ids:
record.best_price = max(record.offer_ids.mapped('price'))
else:
record.best_price = 0

@api.onchange("garden")
def _onchange_partner_id(self):
if self.garden:
self.garden_area = 10
self.garden_orientation = "north"
else:
self.garden_area = 0
self.garden_orientation = None

def action_set_cancel(self):
for record in self:
if record.state == "sold":
raise UserError("Sold properties cannot be canceled")
else:
record.state = "canceled"
return True

def action_set_sold(self):
for record in self:
if record.state == "canceled":
raise UserError("Canceled properties cannot be sold")
else:
record.state = "sold"
return True

@api.constrains('selling_price')
def check_selling_price(self):
for record in self:
if (
not float_is_zero(record.selling_price, 2)
and (float_compare(record.selling_price, (record.expected_price * 0.90), 2) < 0)
):
raise ValidationError("The selling price cannot be below 90 pourcent of the expected price.")

60 changes: 60 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from odoo import api, fields, models


class PropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Estate property offer"
_order = "price desc"


_check_price = models.Constraint(
'CHECK(price > 0)',
'The offer price of a property must be positive.'
)

price = fields.Float(string="Price")
status = fields.Selection(
string="Status",
copy=False,
selection=[
('accepted', 'Accepted'),
('refused', 'Refused')
]
)
partner_id = fields.Many2one("res.partner", string="Partner", required=True)
property_id = fields.Many2one("estate.property", string="Property", required=True)
validity = fields.Integer(default=7, string="Validity (days)")
date_deadline = fields.Date(string="Deadline", compute="_compute_deadline", inverse="_inverse_deadline")

@api.depends('validity')
def _compute_deadline(self):
for record in self:
create_date = fields.Date.to_date(record.create_date) if record.create_date else fields.Date.today()
record.date_deadline = fields.Date.add(create_date, days=record.validity)

def _inverse_deadline(self):
for record in self:
create_date = fields.Date.to_date(record.create_date) if record.create_date else fields.Date.today()
if record.date_deadline:
record.validity = (
record.date_deadline - fields.Date.to_date(create_date)
).days
else:
record.validity = 0

def action_set_accepted(self):
for record in self:
record.status = "accepted"
record.property_id.selling_price = record.price
record.property_id.buyer = record.partner_id

for offer in record.property_id.offer_ids:
if offer != record:
offer.status = "refused"
return True

def action_set_refused(self):
for record in self:
record.status = "refused"
return True

14 changes: 14 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from odoo import fields, models


class PropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Estate property tag"
_order = "name"

name = fields.Char(required=True)

_uniq_name = models.Constraint(
'UNIQUE(name)',
"This tag name is already taken"
)
15 changes: 15 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from odoo import fields, models


class PropertyType(models.Model):
_name = "estate.property.type"
_description = "Estate property type"
_order = "name"

name = fields.Char(required=True)
property_ids = fields.One2many("estate.property", "property_type_id", "Property")

_uniq_name = models.Constraint(
'UNIQUE(name)',
"This type name is already taken"
)
5 changes: 5 additions & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_estate_model,access_estate_model,model_estate_property,base.group_user,1,1,1,1
access_estate_type_model,access_estate_type_model,model_estate_property_type,base.group_user,1,1,1,1
access_estate_tag_model,access_estate_tag_model,model_estate_property_tag,base.group_user,1,1,1,1
access_estate_offer_model,access_estate_offer_model,model_estate_property_offer,base.group_user,1,1,1,1
13 changes: 13 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<odoo>
<menuitem id="estate_menu_root" name="Real Estate">
<menuitem id="estate_menu_first_level" name="Home">
<menuitem id="estate_model_menu_action" action="estate_property_model_action" />
</menuitem>
<menuitem id="estate_settings_menu_first_level" name="Settings">
<menuitem id="estate_property_type_model_menu_action" action="estate_property_type_model_action" />
<menuitem id="estate_property_tag_model_menu_action" action="estate_property_tag_model_action" />
</menuitem>
</menuitem>

</odoo>
38 changes: 38 additions & 0 deletions estate/views/estate_property_offer_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="estate_property_offer_view_form" model="ir.ui.view">
<field name="name">estate_property_offer.form</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<form string="Estate Property">
<sheet>
<group>
<field name="price" />
<field name="partner_id" />
<field name="validity"/>
<field name="date_deadline"/>
<field name="status"/>
</group>
</sheet>
</form>
</field>
</record>

<record id="estate_property_offer_model_view_list" model="ir.ui.view">
<field name="name">estate_property_offer.list</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<list string="Properties">
<field name="price" />
<field name="partner_id" />
<field name="validity" />
<field name="date_deadline"/>
<button name="action_set_accepted" title="Accept" type="object" icon="fa-check"/>
<button name="action_set_refused" title="Refuse" type="object" icon="fa-close"/>
<field name="status"/>
</list>
</field>
</record>


</odoo>
8 changes: 8 additions & 0 deletions estate/views/estate_property_tag_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="estate_property_tag_model_action" model="ir.actions.act_window">
<field name="name">Property Tag</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">list,form</field>
</record>
</odoo>
36 changes: 36 additions & 0 deletions estate/views/estate_property_type_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="estate_property_type_model_action" model="ir.actions.act_window">
<field name="name">Property Types</field>
<field name="res_model">estate.property.type</field>
<field name="view_mode">list,form</field>
</record>

<record id="estate_property_type_view_form" model="ir.ui.view">
<field name="name">estate_property_type.form</field>
<field name="model">estate.property.type</field>
<field name="arch" type="xml">
<form>
<sheet>
<h1>
<field name="name"/>
</h1>

<notebook>
<page string="Properties">
<field name="property_ids">
<list>
<field name="name"/>
<field name="expected_price"/>
<field name="state"/>
</list>
</field>
</page>
</notebook>

</sheet>
</form>
</field>

</record>
</odoo>
Loading