Skip to content

orma - Technical Training #723

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

Open
wants to merge 16 commits into
base: 18.0
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ celerybeat.pid
# Environments
.env
.venv
.vscode
env/
venv/
ENV/
Expand Down
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
18 changes: 18 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
'name': 'Real Estate',
'category': 'Tutorials/Real Estate',
'version': '0.1',
'application': True,
'installable': True,
'depends': ['base'],
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_offer_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_menus.xml',
'views/res_users_views.xml',
],
'license': 'LGPL-3',
}
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_offer
from . import estate_property_tag
from . import estate_property_type
from . import res_users
120 changes: 120 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from dateutil.relativedelta import relativedelta

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


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

name = fields.Char('Title', required=True)
active = fields.Boolean('Active', default=True)
description = fields.Text()
postcode = fields.Char('Postcode')
date_availability = fields.Date(
'Available From',
copy=False,
default=lambda __: fields.Date.today() + relativedelta(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 (sqm)')
facades = fields.Integer()
has_garage = fields.Boolean()
has_garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string='Orientation',
selection=[
('north', 'North'),
('south', 'South'),
('east', 'East'),
('west', 'West'),
],
)
state = fields.Selection(
string='State',
selection=[
('new', 'New'),
('received', 'Offer Received'),
('accepted', 'Offer Accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled'),
],
default='new',
required=True,
copy=False,
)
property_type_id = fields.Many2one('estate.property.type', string='Property Type')
salesman_id = fields.Many2one(
'res.users',
'Salesman',
default=lambda self: self.env.user,
)
buyer_id = fields.Many2one('res.partner', 'Buyer', copy=False)
tag_ids = fields.Many2many('estate.property.tag', string='Tags')
offer_ids = fields.One2many('estate.property.offer', 'property_id')
total_area = fields.Integer(compute='_compute_total')
best_price = fields.Float('Best Offer', compute='_compute_best_price')

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

@api.ondelete(at_uninstall=False)
def _unlink_if_state_new_cancelled(self):
if any(r_property.state not in ('new', 'cancelled') for r_property in self):
raise UserError(_("Can only delete a new or cancelled property"))

@api.constrains('selling_price')
def _check_selling_price(self):
for r_property in self:
if (
r_property.selling_price
and float_compare(r_property.selling_price, r_property.expected_price * 0.9, precision_digits=3) == -1
):
raise ValidationError(_("The selling price cannot be lower than 90% of the expected price"))

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

@api.depends('offer_ids.price')
def _compute_best_price(self):
for r_property in self:
r_property.best_price = max(r_property.offer_ids.mapped('price'), default=0.0)

@api.onchange('has_garden')
def _onchange_garden(self):
if self.has_garden:
self.garden_area = 10
self.garden_orientation = 'north'
else:
self.garden_area = 0
self.garden_orientation = None

def action_state_sold(self):
if 'cancelled' in self.mapped('state'):
raise UserError(_("A cancelled property cannot be set as sold"))
self.write({'state': 'sold'})
return True

def action_state_cancelled(self):
if 'sold' in self.mapped('state'):
raise UserError(_("A sold property cannot be set as cancelled"))
self.state = 'cancelled'
return True
78 changes: 78 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from dateutil.relativedelta import relativedelta

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


class EstatePropertyOffer(models.Model):
_name = 'estate.property.offer'
_description = 'Property Offers'
_order = 'price desc'

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

_sql_constraints = [
(
'check_price',
'CHECK(price > 0)',
'An offer price must be strictly positive.',
),
]

@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
property_id = self.env['estate.property'].browse(vals['property_id'])
if property_id.best_price and (float_compare(property_id.best_price, vals['price'], precision_digits=3) > 0):
raise UserError(_("Can't create an offer with a lower amount than an other offer"))
property_id.write({'state': 'received',})
return super().create(vals_list)

@api.depends('validity')
def _compute_deadline(self):
for r_offer in self:
base_date = r_offer.create_date or fields.Date.today()
r_offer.date_deadline = base_date + relativedelta(days=r_offer.validity)

def _inverse_deadline(self):
for r_offer in self:
base_date = r_offer.create_date or fields.Date.today()
r_offer.validity = (r_offer.date_deadline - base_date.date()).days

def action_state_accept(self):
for r_offer in self:
if r_offer.property_id.state == 'accepted':
raise UserError(_('Only one offer can be accepted for a given property'))
r_offer.property_id.offer_ids.status = 'refused'
r_offer.status = 'accepted'
r_offer.property_id.write(
{
'selling_price': r_offer.price,
'buyer_id': r_offer.partner_id,
'accepted': r_offer.property_id.state
}
)
return True

def action_state_refuse(self):
for r_offer in self:
r_offer.status = 'refused'
return True
16 changes: 16 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from random import randrange

from odoo import fields, models


class EstatePropertyTag(models.Model):
_name = 'estate.property.tag'
_description = 'Property Tags'
_order = 'name'

name = fields.Char('Name', required=True)
color = fields.Integer('Color', default=lambda __: randrange(1,12))

_sql_constraints = [
('name_uniq', 'unique (name)', 'Each name must be unique.'),
]
22 changes: 22 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from odoo import api, fields, models


class EstatePropertyType(models.Model):
_name = 'estate.property.type'
_description = 'Property Types'
_order = 'sequence, name'

name = fields.Char('Name', required=True)
property_ids = fields.One2many('estate.property', 'property_type_id')
sequence = fields.Integer('Sequence', default=1)
offer_ids = fields.One2many('estate.property.offer', 'property_type_id')
offer_count = fields.Integer(compute='_compute_offer_count')

_sql_constraints = [
('name_uniq', 'unique (name)', 'Each name must be unique.'),
]

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


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

property_ids = fields.One2many("estate.property", "salesman_id", domain=[("state", "in", ["new", "received"])])
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
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
12 changes: 12 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<odoo>
<menuitem id="estate_menu_root" name="Real Estate">
<menuitem id="test_first_level_menu" name="Advertisements">
<menuitem id="estate_property_menu_property" action="estate_property" />
</menuitem>
<menuitem id="settings_first_level_menu" name="Settings">
<menuitem id="estate_property_menu_type" action="estate_type" />
<menuitem id="estate_property_menu_tag" action="estate_tag" />
</menuitem>
</menuitem>
</odoo>
45 changes: 45 additions & 0 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_offer" model="ir.actions.act_window">
<field name="name">Property offers</field>
<field name="res_model">estate.property.offer</field>
<field name="view_mode">list,form</field>
<field name="domain">[('property_type_id', '=', active_id)]</field>
</record>

<record id="estate_property_offer_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="Offers" editable="top" decoration-success="status in ('accepted',)"
decoration-danger="status in ('refused',)">
<field name="partner_id" />
<field name="price" />
<button name="action_state_accept" type="object" title="check" icon="fa-check"
invisible="status in ('accepted', 'refused')" />
<button name="action_state_refuse" type="object" title="times" icon="fa-times"
invisible="status in ('accepted', 'refused')" />
</list>
</field>
</record>

<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="Form">
<sheet>
<group>
<group>
<field name="price" />
<field name="partner_id" />
<field name="status" />
<field name="validity" />
<field name="date_deadline" />
</group>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
20 changes: 20 additions & 0 deletions estate/views/estate_property_tag_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_tag" model="ir.actions.act_window">
<field name="name">Property Tags</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">list,form</field>
</record>

<record
id="estate_property_tag_view_list" model="ir.ui.view">
<field name="name">estate.property.tag.list</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<list string="Tags" editable="bottom">
<field name="name" />
<field name="color" />
</list>
</field>
</record>
</odoo>
Loading