Skip to content

Commit

Permalink
[ADD] estate: Finish up to chapter 10 (included)
Browse files Browse the repository at this point in the history
  • Loading branch information
vandroogenbd committed Oct 25, 2024
1 parent b16e643 commit c61bcc4
Show file tree
Hide file tree
Showing 13 changed files with 660 additions and 0 deletions.
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
23 changes: 23 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# A custom real estate module, created for learning purposes

{
'name': '[TUTO] Estate',
'summary': 'Track your real estate properties',
'depends': [
'base_setup'
],
'data': [
'security/ir.model.access.csv',

'views/estate_property_views.xml',
'views/estate_settings_views.xml',
'views/estate_property_offer_views.xml',
'views/estate_menu_views.xml'
],
'installable': True,
'application': True,
'demo': [
"demo/estate_demo.xml",
],
'auto_install': True
}
97 changes: 97 additions & 0 deletions estate/demo/estate_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>

<!-- Demo Estate Property Types -->
<record id="estate_property_type_1" model="estate_property_type">
<field name="name">Appartment</field>
</record>
<record id="estate_property_type_2" model="estate_property_type">
<field name="name">House</field>
</record>

<!-- Demo Estate Property Tags -->
<record id="estate_property_tag_1" model="estate_property_tag">
<field name="name">cosy</field>
</record>
<record id="estate_property_tag_2" model="estate_property_tag">
<field name="name">renovated</field>
</record>
<record id="estate_property_tag_3" model="estate_property_tag">
<field name="name">renovation needed</field>
</record>

<!-- Demo Estate Property Offers -->
<record id="estate_property_offer_1" model="estate_property_offer">
<field name="price">2000000</field>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="validity">12</field>
</record>
<record id="estate_property_offer_2" model="estate_property_offer">
<field name="price">200000</field>
<field name="partner_id" ref="base.res_partner_3"/>
<field name="validity">10</field>
</record>
<record id="estate_property_offer_3" model="estate_property_offer">
<field name="price">220000</field>
<field name="partner_id" ref="base.res_partner_5"/>
<field name="validity">25</field>
</record>
<record id="estate_property_offer_4" model="estate_property_offer">
<field name="price">260000</field>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="deadline" eval="(DateTime.today() + relativedelta(days=7)).strftime('%Y-%m-%d %H:%M')" />
</record>

<!-- Demo Estate Properties -->
<record id="estate_property_1" model="estate_property">
<field name="name">Beach House</field>
<field name="status">canceled</field>
<field name="description">A brand new house by the beach</field>
<field name="postcode">8300</field>
<field name="date_availability" eval="(DateTime.today() + relativedelta(months=3)).strftime('%Y-%m-%d %H:%M')" />
<field name="expected_price">2500000</field>
<field name="selling_price">0</field>
<field name="bedrooms">6</field>
<field name="living_area">1050</field>
<field name="facades">4</field>
<field name="garage">True</field>
<field name="garden">True</field>
<field name="garden_area">420</field>
<field name="garden_orientation">south</field>
<field name="type_id" ref="estate_property_type_2" />
<field name="salesperson" ref="base.user_demo"/>
<field name="tag_ids" eval="[(6, 0, [ref('estate_property_tag_1'), ref('estate_property_tag_2')])]"/>
<field name="offer_ids" eval="[(6, 0, [ref('estate_property_offer_1')])]"/>
</record>
<record id="estate_property_2" model="estate_property">
<field name="name">City Apt</field>
<field name="status">offer_accepted</field>
<field name="description">A shitty appartment in Brussels</field>
<field name="postcode">1090</field>
<field name="date_availability" eval="(DateTime.today() + relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')" />
<field name="expected_price">250000</field>
<field name="selling_price">0</field>
<field name="bedrooms">2</field>
<field name="living_area">95</field>
<field name="facades">1</field>
<field name="garage">False</field>
<field name="garden">False</field>
<field name="garden_area">0</field>
<field name="type_id" ref="estate_property_type_1"/>
<field name="salesperson" ref="base.user_admin"/>
<field name="tag_ids" eval="[(6, 0, [ref('estate_property_tag_3')])]"/>
<field name="offer_ids" eval="[(6, 0, [ref('estate_property_offer_2'), ref('estate_property_offer_4'), ref('estate_property_offer_3')])]"/>
</record>

<!-- Mass cancel -->
<record id="model_estate_property_action_cancel" model="ir.actions.server">
<field name="name">Mass cancel</field>
<field name="model_id" ref="estate.model_estate_property"/>
<field name="binding_model_id" ref="estate.model_estate_property"/>
<field name="binding_view_types">list</field>
<field name="state">code</field>
<field name="code">action = records.action_cancel()</field>
</record>
</data>
</odoo>
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_infos, estate_property, estate_property_offer
139 changes: 139 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
from odoo import api, exceptions, fields, models
from datetime import date
from dateutil.relativedelta import relativedelta


class Estate_Property(models.Model):
_name = "estate_property"
_description = "Estate properties"
_order = "id desc"
active = False

name = fields.Char(required=True, string="Title")

status = fields.Selection(
[
("new", "New"),
("offer_received", "Offer received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("canceled", "Canceled"),
],
default="new",
readonly=True,
copy=False,
string="Sale State",
)

description = fields.Text(copy=False, string="Description")

postcode = fields.Char(
help="Une adresse serait mieux mais bon...", string="Postcode"
)

date_availability = fields.Date(
default=(date.today() + relativedelta(months=+3)),
copy=False,
string="Available From",
)

expected_price = fields.Float(
default=0.0, required=True, copy=False, string="Expected Price"
)

selling_price = fields.Float(readonly=True, string="Selling Price")

bedrooms = fields.Integer(default=2, string="Bedrooms")

living_area = fields.Integer(string="Living Area (sqm)")

facades = fields.Integer(default=2, string="Facades")

garage = fields.Boolean(default=False, string="Garage")

garden = fields.Boolean(default=False, string="Garden")

garden_area = fields.Integer(default=0, string="Garden Area (sqm)")

garden_orientation = fields.Selection(
[("north", "North"), ("south", "South"), ("west", "West"), ("east", "East")],
string="Garden Orientation",
)

type_id = fields.Many2one(
"estate_property_type", required=True, string="Property Type"
)

buyer = fields.Many2one("res.partner", copy=False, string="Buyer")

salesperson = fields.Many2one(
"res.users", default=(lambda self: self.env.user), string="Salesman"
)

tag_ids = fields.Many2many("estate_property_tag", string="Property Tags")

offer_ids = fields.One2many("estate_property_offer", "property_id", string="Offers")

total_area = fields.Integer(compute="_compute_total_area", string="Total Area")

best_offer = fields.Float(
compute="_compute_best_offer", default=0.0, string="Best Offer"
)

_sql_constraints = [
(
"check_positive_expected_price",
"CHECK(expected_price >= 0.0)",
"Expected Price should be a positive number.",
),
(
"check_positive_selling_price",
"CHECK(selling_price >= 0.0)",
"Selling Price should be a positive number.",
),
]

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

@api.depends("offer_ids")
def _compute_best_offer(self):
for record in self:
record.best_offer = max(record.offer_ids.mapped("price"))

@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 = ""

@api.constrains("selling_price", "expected_price")
def _check_expected_vs_selling_price(self):
for record in self:
if (record.selling_price > 0.0) and (
record.selling_price < 0.9 * record.expected_price
):
raise exceptions.ValidationError(
r"Cannot sell for less than 90% of expected price."
)

def action_sold(self):
for record in self:
if record.status != "canceled":
record.status = "sold"
else:
raise exceptions.UserError("Canceled properties cannot be sold.")
return True

def action_cancel(self):
for record in self:
if record.status != "sold":
record.status = "canceled"
else:
raise exceptions.UserError("Sold properties cannot be canceled.")
return True
42 changes: 42 additions & 0 deletions estate/models/estate_property_infos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from odoo import fields, models


class Estate_Property_Type(models.Model):
_name = "estate_property_type"
_description = "Estate property Types"
_order = "name"

name = fields.Char(
required=True,
string="Type"
)

property_ids = fields.One2many(
"estate_property",
"type_id",
string="Estate Properties"
)

_sql_constraints = [
("check_unique_type", "UNIQUE(name)", "Property types must be unique.")
]


class Estate_Property_Tag(models.Model):
_name = "estate_property_tag"
_description = "Estate property Tags"
_order = "name"

name = fields.Char(
required=True,
string="Type"
)

property_estate_ids = fields.Many2many(
"estate_property",
string="Estate Properties"
)

_sql_constraints = [
("check_unique_tag", "UNIQUE(name)", "Property tags must be unique.")
]
76 changes: 76 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from odoo import api, exceptions, fields, models
from datetime import date
from dateutil.relativedelta import relativedelta


class Estate_Property_Offer(models.Model):
_name = "estate_property_offer"
_description = "Estate Property Offers"
_order = "price desc"

price = fields.Float(
string="Price"
)

status = fields.Selection(
[("accepted", "Accepted"), ("refused", "Refused")],
readonly=True,
copy=False,
string="Status"
)

partner_id = fields.Many2one(
'res.partner',
required=True,
string="Partner"
)

property_id = fields.Many2one(
'estate_property',
string="Property"
)

validity = fields.Integer(
default=7,
string="Validity (days)"
)

deadline = fields.Date(
compute="_compute_deadline",
copy=False,
string="Deadline"
)

_sql_constraints = [
("check_positive_price", "CHECK(price > 0.0)", "Offer Price should be a positive number (higher than 0).")
]

@api.depends("validity")
def _compute_deadline(self):
for record in self:
record.deadline = date.today() + relativedelta(days=+record.validity)

def _inverse_deadline(self):
for record in self:
record.validity = relativedelta(date.today(), record.deadline)

def action_accept(self):
for record in self:
if not any(offer_status == "accepted" for offer_status in record.property_id.offer_ids.mapped("status")):
# Set values in the Property itself
record.property_id.selling_price = record.price
record.property_id.buyer = record.partner_id

record.status = "accepted"
else:
raise exceptions.UserError("An offer has already been accepted.")
return True

def action_refuse(self):
for record in self:
if record.status == "accepted":
# Set values in the Property itself
record.property_id.selling_price = 0.0
record.property_id.buyer = None
record.status = "refused"
return True
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
Binary file added estate/static/description/icon.webp
Binary file not shown.
Loading

0 comments on commit c61bcc4

Please sign in to comment.