Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
89d0e10
add first model
sdelbeke Sep 16, 2025
ef3ffe2
move model and add security
sdelbeke Sep 16, 2025
fb082f0
add menu
sdelbeke Sep 16, 2025
5919952
add menus
sdelbeke Sep 16, 2025
fbb721c
fix view
sdelbeke Sep 16, 2025
6655a5b
quick fix
sdelbeke Sep 16, 2025
2f326e6
update fields
sdelbeke Sep 16, 2025
f0ae949
add custom list view
sdelbeke Sep 16, 2025
74cf761
add postcode
sdelbeke Sep 16, 2025
13dc8ca
quick fix
sdelbeke Sep 16, 2025
2ca4778
quick fix
sdelbeke Sep 16, 2025
5b8bf31
add form view
sdelbeke Sep 16, 2025
3a3329b
quick fix
sdelbeke Sep 16, 2025
7f5c676
quick fix
sdelbeke Sep 16, 2025
0f4f245
quick fix
sdelbeke Sep 16, 2025
5b208ba
quick fix
sdelbeke Sep 16, 2025
36dcfaf
add search
sdelbeke Sep 16, 2025
b9b5768
quick fix
sdelbeke Sep 16, 2025
aa3c1b1
add search
sdelbeke Sep 16, 2025
c2cbe7c
quick fix
sdelbeke Sep 16, 2025
113a741
quick fix
sdelbeke Sep 16, 2025
4441197
quick fix
sdelbeke Sep 16, 2025
24d90e5
add property type
sdelbeke Sep 16, 2025
d775d85
add property type
sdelbeke Sep 16, 2025
8eff844
add property type
sdelbeke Sep 16, 2025
020b6fc
add property type
sdelbeke Sep 16, 2025
c94fbd2
fix access property type
sdelbeke Sep 16, 2025
c7266fd
add link to User or Partner
sdelbeke Sep 16, 2025
921977e
add property tag
sdelbeke Sep 16, 2025
92cf054
fix tag
sdelbeke Sep 16, 2025
cb1fdc8
add offers
sdelbeke Sep 16, 2025
ae9ac0b
add offers
sdelbeke Sep 17, 2025
541451e
message computed fields
sdelbeke Sep 17, 2025
cb8d83b
some actions
sdelbeke Sep 17, 2025
de02acb
add constraints
sdelbeke Sep 17, 2025
9ce8222
wip sprinkles
sdelbeke Sep 17, 2025
b3f8bd9
add inheritance
sdelbeke Sep 17, 2025
f760799
add inheritance
sdelbeke Sep 17, 2025
d54c802
type manual ordering
sdelbeke Sep 17, 2025
aac5dfc
more sprinkles
sdelbeke Sep 17, 2025
18314f1
finish add the sprinkles
sdelbeke Sep 22, 2025
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
9 changes: 7 additions & 2 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
{
"name": "Estate", # The name that will appear in the App list
"version": "16.0.0", # Version
"version": "18.0.1.0.0", # Version
"application": True, # This line says the module is an App, and not a module
"depends": ["base"], # dependencies
"data": [

'security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_menu.xml',
'views/res_users_views.xml'
],
"installable": True,
'license': 'LGPL-3',
Expand Down
Empty file removed estate/models.py
Empty file.
5 changes: 5 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
from . import res_users
92 changes: 92 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from email.policy import default

from odoo import fields, models, api, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_is_zero, float_compare

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

name = fields.Char(required=True)
description = fields.Text()
date_availability = fields.Date(copy=False, default=lambda self: fields.Date.add(fields.date.today(), months=3))
expected_price = fields.Float()
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
garden = fields.Boolean()
garden_orientation = fields.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")],
default="new",
copy=False,
required=True)
postcode = fields.Char()
facades = fields.Integer()
garage = fields.Boolean()
garden_area = fields.Integer()
living_area = fields.Integer()
property_type_id = fields.Many2one("estate.property.type", string="Property type")
salesperson_id = fields.Many2one("res.users", default=lambda self: self.env.user)
buyer_id = fields.Many2one("res.partner", copy=False)
tag_ids = fields.Many2many("estate.property.tag")
offer_ids = fields.One2many("estate.property.offer", "property_id")
best_price = fields.Float(compute="_compute_best_price")
total_area = fields.Integer(compute="_compute_total_area")

_sql_constraints = [
("check_expected_price", "CHECK(expected_price > 0)",
'The expected price must be strictly positive'),
("check_selling_price", "CHECK(selling_price >= 0)",
'The selling price must be positive'),
]

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

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

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


def action_cancel_property(self):
for property in self:
property.state = "cancelled"

def action_sell_property(self):
for property in self:
if property.state == "cancelled":
raise UserError(_("Cancelled properties cannot be sold"))
property.state = "sold"


@api.constrains('selling_price','expected_price')
def _check_selling_price(self):
for property in self:
if (not float_is_zero(property.selling_price, precision_rounding = 0.01) and
float_compare(property.selling_price, 0.9 * property.expected_price, precision_rounding=0.01) < 0
):
raise ValidationError(_(''))

@api.ondelete(at_uninstall=False)
def _unlink_if_new_canceled(self):
for property in self:
if property.state not in ("new", "cancelled"):
raise UserError(_("Only new or canceiled property can be deleted"))
44 changes: 44 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from odoo import fields, models, api


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

price = fields.Float()
status = fields.Selection([("accepted", "Accepted"), ("refused","Refused")], copy=False)
property_id = fields.Many2one("estate.property", required=True)
partner_id = fields.Many2one("res.partner", required=True)
validity = fields.Integer(default = 7)
date_deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline")
property_type_id = fields.Many2one(related="property_id.property_type_id")

_sql_constraints = [
("check_offer_price", "CHECK(price > 0)",
'The offer price must be strictly positive')
]


@api.depends('validity','create_date')
def _compute_deadline(self):
for offer in self:
create_date = offer.create_date or fields.Date.today()
offer.date_deadline = fields.Date.add(create_date, days=offer.validity)


def _inverse_deadline(self):
for offer in self:
offer.validity = (offer.date_deadline - fields.Date.to_date(offer.create_date)).days



def action_accept_offer(self):
self.status = "accepted"
self.property_id.selling_price = self.price
self.property_id.buyer_id = self.partner_id



def action_refuse_offer(self):
self.status = "refused"
17 changes: 17 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from odoo import fields, models


class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Estate Property Tag"
_order = "name desc"


name = fields.Char(required=True)
color = fields.Integer("Color Index")


_sql_constraints = [
("unique_name", "UNIQUE(name)",
'Tag name should be unique.')
]
23 changes: 23 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from odoo import fields, models, api


class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Estate Property Type"
_order = "sequence, name desc"


name = fields.Char(required=True)
property_ids = fields.One2many("estate.property","property_type_id")
sequence = fields.Integer('Sequence', default = 1, help="Use to order types")
offer_ids = fields.One2many("estate.property.offer", "property_type_id")
offer_count = fields.Integer(compute="_compute_offer_count")

_sql_constraints = [
('unique_name', 'UNIQUE(name)', 'The Type\'s name must be unique.')
]

@api.depends("offer_ids")
def _compute_offer_count(self):
for record in self:
record.offer_count = len(record.offer_ids)
6 changes: 6 additions & 0 deletions estate/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from odoo import fields, models

class ResUsers(models.Model):
_inherit = "res.users"

property_ids = fields.One2many("estate.property","salesperson_id", domain=[("state", "not in", ['sold', 'canceled'])])
6 changes: 6 additions & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1
estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1
estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1
estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1

13 changes: 13 additions & 0 deletions estate/views/estate_menu.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_advertisements_menu" name="Advertisements">
<menuitem id="estate_property_menu_action" name = "Properties" action="estate_property_action"/>
</menuitem>
<menuitem id="estate_settings_menu" name="Settings">
<menuitem id="estate_property_type_menu_action" name = "Property types" action="estate_property_type_action"/>
<menuitem id="estate_property_tag_menu_action" name = "Property tags" action="estate_property_tag_action"/>
</menuitem>

</menuitem>
</odoo>
24 changes: 24 additions & 0 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

<odoo>
<record id="estate.property_offer_view_list" model="ir.ui.view">
<field name="name">Offer List</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<list string="Properties" editable="top" decoration-success="status == 'accepted'" decoration-danger="status == 'refused'">
<field name="price" string="Price"/>
<field name="partner_id" string="Partner"/>
<field name="validity" string="Validity (days)"/>
<field name="date_deadline" string="Deadline"/>
<button name="accept_offer" string="" type="object" icon="fa-check" invisible="status in ['accepted','refused']"/>
<button name="decline_offer" string="" type="object" icon="oi-close" invisible="status in ['accepted','refused']"/>
<field name="status" string="Status"/>
</list>
</field>
</record>
<record id="estate.property_offer_model_action" model="ir.actions.act_window">
<field name="name">Offer</field>
<field name="res_model">estate.property.offer</field>
<field name="view_mode">list</field>
<field name="domain">[('property_type_id', '=', active_id)]</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"?>
<odoo>
<record id="estate_property_tag_action" model="ir.actions.act_window">
<field name="name">Estate property tag action</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">list,form</field>
</record>
</odoo>
57 changes: 57 additions & 0 deletions estate/views/estate_property_type_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_type_view_list" model="ir.ui.view">
<field name="name">estate.property.type.list</field>
<field name="model">estate.property.type</field>
<field name="arch" type="xml">
<list string="estate type list">
<field name="sequence" widget="handle"/>
<field name="name"/>
</list>
</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 string="properties list">
<sheet>
<!--

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@jnc-odoo what is wrong with this button ? Thanks

<header>
<button type="action" name="estate.property_offer_model_action" icon="fa-money" class="oe_stat_button">
<field name="offer_count" widget="statinfo" string=""/>
<h5>Offers</h5>
</button>
</header> -->
<group>
<h1>
<field name="name" />
</h1>
</group>
<notebook>
<page>
<field name="property_ids">
<list>
<field name="name"/>
<field name="expected_price"/>
<field name="state"/>
</list>
</field>
</page>

</notebook>
</sheet>
</form>
</field>
</record>





<record id="estate_property_type_action" model="ir.actions.act_window">
<field name="name">Estate property type action</field>
<field name="res_model">estate.property.type</field>
<field name="view_mode">list,form</field>
</record>
</odoo>
Loading