Skip to content

18.0 Technical onboarding guja #359

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
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e887ceb
[ADD] estate: Odoo recognize the estate module
guja-odoo Feb 17, 2025
80e90d5
[IMP] estate: added installable and website field in manifest
guja-odoo Feb 17, 2025
6093589
[IMP] estate: (Chapter 3) added init file for the models and the esta…
guja-odoo Feb 17, 2025
71556f4
[IMP] estate: (Chapter 4) Created ir.model.access.csv and defined it …
guja-odoo Feb 17, 2025
3af347c
[IMP] estate: (Chapter 5) Default interaction with the UI
guja-odoo Feb 18, 2025
fcbf14a
[IMP] estate: (Chapter 6) customed list, form, search and search domains
guja-odoo Feb 19, 2025
449ccd1
[IMP] estate: (Chapter 7) added links between the properties, offers,…
guja-odoo Feb 19, 2025
51128a5
[IMP] estate: (chapter 8) added computation for the total area, the d…
guja-odoo Feb 19, 2025
04bffc8
[IMP] estate: WIP chapter 9
guja-odoo Feb 19, 2025
8223b68
[IMP] estate: (Chapter 9) added cancel and sold button for a property…
guja-odoo Feb 20, 2025
273086c
[IMP] estate: (Chapter 10) sql constaints and python contraints for e…
guja-odoo Feb 20, 2025
b0b9207
[IMP] estate: WIP chapter 11
guja-odoo Feb 20, 2025
082fda7
[IMP] estate: (Chapter 11) added inline views, widgets, ordered lists…
guja-odoo Feb 21, 2025
b77477f
[IMP] estate: (Chapter 12) delete and create conditions, added proper…
guja-odoo Feb 24, 2025
0382292
[ADD] estate_account: (Chapter 12) added estate_account, create invoi…
guja-odoo Feb 24, 2025
c76e9ef
[IMP] estate: (Chapter 14) added kanban view for the properties
guja-odoo Feb 24, 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
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
25 changes: 25 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

{
'name': "Real Estate",
'version': '1.0',
'depends': ['base'],
'author': "Odoo S.A.",
'category': 'Category',
'description': "Description text",
'website': 'https://www.odoo.com/page/estate',
'license': 'LGPL-3',

'data': [
'security/ir.model.access.csv',

'views/res_user_views.xml',
'views/estate_property_offers_views.xml',
'views/estate_property_types_views.xml',
'views/estate_property_tags_views.xml',
'views/estate_property_views.xml',
'views/estate_menus.xml'

],
'application': True,
'installable': True

Choose a reason for hiding this comment

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

You'll need to add a license to get rid of the runbot warning and turn it to green 🙂

}
1 change: 1 addition & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import estate_property, estate_property_type, estate_property_tag, estate_property_offer, res_users
114 changes: 114 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from odoo import fields, models, api, exceptions
from odoo.tools import float_is_zero, float_compare

Choose a reason for hiding this comment

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

Two lines between the imports and the class.

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

active = fields.Boolean(default=True)
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Datetime(copy=False, default=fields.Datetime.add(fields.Datetime.now(), months=+3))

Choose a reason for hiding this comment

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

We can inline this.

expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
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="What is the orientation of the facade of the property"
)
state = fields.Selection(
string='State',
selection=[
('new', 'New'),
('offer-received', 'Offer received'),
('offer-accepted', 'Offer accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled')
],
required=True,
copy=False,
default='new'
)

property_type_id = fields.Many2one("estate.property.type", string="Type")
user_id = fields.Many2one('res.users', string='Salesperson', default=lambda self: self.env.user)
partner_id = fields.Many2one('res.partner', string='Buyer', copy=False)
property_tag_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")

total_area = fields.Integer(compute="_compute_total_area", readonly=True)

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


best_price = fields.Float(compute="_compute_best_price", readonly=True)

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

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

def sell_property(self):
if self.state == 'cancelled':
raise exceptions.UserError("Canceled properties cannot be sold.")
else:
self.state = 'sold'

def cancel_property(self):
if self.state == 'sold':
raise exceptions.UserError("Sold properties cannot be cancelled.")
else:
self.state = 'cancelled'


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

@api.constrains("selling_price")
def _check_selling_price(self):
for record in self:
if float_is_zero(record.selling_price, precision_digits=1):
return
min_price = record.expected_price * 0.9
if float_compare(record.selling_price, min_price, precision_digits=1) == -1:
raise exceptions.ValidationError("The selling price is too low.")

@api.ondelete(at_uninstall=False)
def _unlink_except_not_new_cancelled(self):
for record in self:
if record.state not in ('new', 'cancelled'):
raise exceptions.UserError("You can't delete a property unless it is new or cancelled.")
else:
return super().delete()
65 changes: 65 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from odoo import fields, models, api, exceptions


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

price = fields.Float()
status = fields.Selection(
copy=False,
selection=[
('accepted', 'Accepted'),
('refused', 'Refused'),
]
)

partner_id = fields.Many2one('res.partner', string='Buyer', required=True)
property_id = fields.Many2one('estate.property', string="Property", required=True)

validity = fields.Integer(default=7)
create_date = fields.Date(default=fields.Date.today(), readonly=True)
date_deadline = fields.Date(compute="_compute_date_deadline", inverse="_inverse_date_deadline")

property_type_id = fields.Many2one(related="property_id.property_type_id", store=True)

@api.depends("validity", "create_date")
def _compute_date_deadline(self):
for record in self:
record.date_deadline = fields.Date.add(record.create_date, days=+record.validity)

@api.onchange("date_deadline")
def _inverse_date_deadline(self):
for record in self:
record.validity = (record.date_deadline - record.create_date).days

def action_validate(self):
if self.property_id.state == 'offer-accepted':
raise exceptions.UserError("An offer has already been accepted.")
self.status = 'accepted'
self.property_id.state = 'offer-accepted'
self.property_id.selling_price = self.price
self.property_id.partner_id = self.partner_id

def action_refuse(self):
if self.status == 'accepted':
self.property_id.selling_price = 0.0
self.property_id.partner_id = None
self.property_id.state = 'offer-received'
self.status = 'refused'

@api.model_create_multi
def create(self, val_list):
for record in self.env['estate.property'].browse(val_list[0]['property_id']).offer_ids:
if record.price > val_list[0]['price']:
raise exceptions.UserError("The offer price is too low.")
offer = super().create(val_list)
if offer.property_id.state == 'new':
offer.property_id.state = 'offer-received'


_sql_constraints = [
('check_price', 'CHECK(price >= 0)',
'The offer price of a property must be positive.')
]
15 changes: 15 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from odoo import fields, models


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

name = fields.Char(required=True)
color = fields.Integer()

_sql_constraints = [
('check_name', 'unique(name)',
'This tag already exists.')
]
34 changes: 34 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from odoo import fields, models, api


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

name = fields.Char(required=True)
sequence = fields.Integer('Sequence', default=1)
property_ids = fields.One2many("estate.property", "property_type_id")

offer_ids = fields.One2many('estate.property.offer', 'property_type_id', store=True)

offer_count = fields.Integer(compute='_compute_offer_count')

@api.depends('offer_ids')
def _compute_offer_count(self):
for record in self:
record.offer_count = len(record.offer_ids)

_sql_constraints = [
('check_name', 'unique(name)',
'This type already exists.')
]

class EstatePropertyTypeLine(models.Model):
_name = "estate.property.type.line"
_description = "Estate property type line"

property_id = fields.Many2one("estate.property")
name = fields.Char()
expected_price = fields.Float()
state = fields.Char()
11 changes: 11 additions & 0 deletions estate/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from odoo import fields, models

class Users(models.Model):
_inherit = 'res.users'

property_ids = fields.One2many(
comodel_name='estate.property',
inverse_name='user_id',
domain=['&', ('state', '!=', '"cancelled"'), ('state', '!=', '"sold"')],
string="Properties",
)
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
access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
access_estate_property_type,access_estate_property_type,model_estate_property_type,base.group_user,1,1,1,1
access_estate_property_type_line,access_estate_property_type_line,model_estate_property_type_line,base.group_user,1,1,1,1
access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1
access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1
32 changes: 32 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<menuitem
id="estate_menu_root"
name="Real Estate">
<menuitem
id="estate_menu_advertisements"
name="Advertisements">
<menuitem
id="menu_estate_advertisements_properties"
name="Properties"
action="estate_action_advertisements">
</menuitem>
</menuitem>
<menuitem
id="estate_menu_settings"
name="Settings">
<menuitem
id="menu_estate_settings_property_types"
name="Property Types"
action="estate_action_property_types">
</menuitem>
<menuitem
id="menu_estate_settings_property_tags"
name="Property Tags"
action="estate_action_property_tags">
</menuitem>
</menuitem>
</menuitem>

</odoo>
23 changes: 23 additions & 0 deletions estate/views/estate_property_offers_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>

<odoo>

<record id="estate_action_property_offers" model="ir.actions.act_window">
<field name="name">Property Offers</field>
<field name="res_model">estate.property.offer</field>
<field name="domain">[('property_type_id', '=', active_id)]</field>
<field name="view_mode">list</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>
<field name="partner_id"/>
<field name="price"/>
<field name="status"/>
</list>
</field>
</record>

</odoo>
20 changes: 20 additions & 0 deletions estate/views/estate_property_tags_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>

<odoo>

<record id="estate_action_property_tags" 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_tags_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 editable="bottom">
<field name="name"/>
</list>
</field>
</record>

</odoo>
Loading