Skip to content

18.0 training kums #847

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

Draft
wants to merge 7 commits into
base: 18.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Real Estate",
"category": "Tutorials/RealEstate",
"application": True,
"installable": True,
"data": [
"views/estate_property_views.xml",
"views/estate_property_tag_views.xml",
"views/estate_property_offer_views.xml",
"views/estate_property_type_views.xml",
"views/estate_menus.xml",
"views/inherit_res_users_view.xml",
"security/ir.model.access.csv",
],
"license": "AGPL-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_type
from . import estate_property_tag
from . import estate_property_offer
from . import inherited_res_users
173 changes: 173 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
from odoo import fields, models, api
from dateutil.relativedelta import relativedelta
from datetime import date
from odoo.exceptions import UserError
from odoo.exceptions import ValidationError
from odoo.tools.float_utils import float_compare


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

name = fields.Char()
description = fields.Text()
postcode = fields.Text()
date_availability = fields.Date(
copy=False, default=lambda self: date.today() + relativedelta(months=3)
)
expected_price = fields.Float()
selling_price = fields.Integer(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="Type",
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"),
],
string="State",
default="new",
copy=False,
)

# property will have Many to one relation with property type since many properties can belong to one property type

property_type_id = fields.Many2one("estate.property.type", "Property Type")

user_id = fields.Many2one(
"res.users",
string="Salesperson",
copy=False,
default=lambda self: self.env.user,
)

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

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_property_area")

best_price = fields.Integer(compute="_best_property_offer")

status = fields.Char(default="new")

_order = "id desc"

_sql_constraints = [
(
"check_expected_price",
"CHECK(expected_price > 0)",
"Expected price must be strictly positive",
),
(
"check_selling_price",
"CHECK(selling_price >= 0)",
"Selling price should be positive",
),
]

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

@api.depends("offer_ids.price")
def _best_property_offer(self):
for record in self:
offers_list = record.mapped("offer_ids.price")
if offers_list:
record.best_price = max(offers_list)
return
record.best_price = 0

# on change of garden status , update gardern area and its orientation

@api.onchange("garden")
def _onchange_garden_status(self):
if self.garden:
self.garden_area = 10
self.garden_orientation = "north"
return
self.garden_area = 0
self.garden_orientation = ""

# acts when property is sold
# In case property is cancelled it cannot be sold
def action_sell_property(self):
# dictionary for the property status
property_sell_status_dict = {"new": True, "sold": True, "cancelled": False}

for record in self:
if property_sell_status_dict[record.status]:
record.status = "sold"
record.state = "sold"
return
raise UserError("Cancelled property cannot be sold.")

# action in case of cancel property button
# If property is sold than Cannot be cancelled

def action_cancel_property_selling(self):
property_cancel_status_dict = {
"new": True,
"cancelled": True,
"sold": False,
}
for record in self:
if property_cancel_status_dict[record.status]:
record.status = "cancelled"
return
raise UserError("Sold property cannot be cancelled.")

# constrains for the selling price

@api.constrains("selling_price", "expected_price")
def _check_selling_price(self):
for data in self:
# if call will come after selling price change than it will allow updated price to work
if data.selling_price <= 0:
return

price_float_ratio = data.selling_price / data.expected_price
ratio_diffrence = float_compare(price_float_ratio, 0.9, precision_digits=2)
if ratio_diffrence == -1:
data.selling_price = 0
raise ValidationError(
"The selling price cannot be lower than 90% of the expected price"
)
return

# delete opration for the process

@api.ondelete(at_uninstall=False)
def _unlink_if_state_new_or_cancelled(self):
for data in self:
if not bool(data.state == "new" or data.state == "cancelled"):
raise UserError(
"Can't delete property which is not in new or cancelled state!"
)
114 changes: 114 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# offers model that will offer us the offers


from odoo import fields, models, api
from datetime import datetime
from dateutil.relativedelta import relativedelta
from odoo.exceptions import UserError
from odoo.tools.float_utils import float_compare


class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Offer model for the properties of real estate"
price = fields.Float()
status = fields.Selection(
selection=[("accepted", "Accepted"), ("refused", "Refused ")],
string="Status",
copy=False,
)

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

property_id = fields.Many2one("estate.property", string="Property")

property_type_id = fields.Many2one(
"estate.property.type",
related="property_id.property_type_id",
store=True,
readonly=False,
)

validity = fields.Integer(default=7)

date_deadline = fields.Date(
compute="_compute_offer_deadline", inverse="_deadline_update"
)

# constrains of sql

_sql_constraints = [
("check_price", "CHECK(price > 0)", "Offered price must be strictly positive")
]

# order in which data is fetched

_order = "price desc"

# deadline will be computed based upon the validity date
@api.depends("validity")
def _compute_offer_deadline(self):
for offer in self:
if not offer.create_date:
offer.date_deadline = datetime.now() + relativedelta(
days=(offer.validity or 0)
)
return

offer.date_deadline = offer.create_date + relativedelta(
days=(offer.validity or 0)
)

# deadline date can also be changed and once this is saved validity will be updated
def _deadline_update(self):
for offer in self:
offer.validity = (
offer.date_deadline - (offer.create_date or datetime.now()).date()
).days

# action for the accepting the offer
def action_offer_confirm(self):
for record in self:
# since saling price is only updated when offer is accepted therefore it validates if offer
# is already accepted than warning

if record.property_id.selling_price > 0:
raise UserError("Offer price already accepted for the property")

record.status = "accepted"
record.property_id.selling_price = self.price
record.property_id.partner_id = record.partner_id
record.property_id.state = "offer_accepted"

# action for the refusal of the status
def action_offer_refuse(self):
for record in self:
if record.status == "accepted":
record.property_id.selling_price = 0
record.property_id.partner_id = ""
record.status = "refused"

# now in case of offer creation CRUD
# self will be a proxy object ,
# property_id feilds is available in vals
@api.model_create_multi
def create(self, vals):
# will check the offer value and does property has other offers which are max thw\an this one
for value in vals:
property_details = self.env["estate.property"].browse(
value.get("property_id")
)
for property_data in property_details:
offers_list = property_data.mapped("offer_ids.price")
max_offer = max(offers_list, default=0)
comparison_result = float_compare(
value.get("price"), max_offer, precision_digits=2
)

if comparison_result == -1:
raise UserError("Offer with a lower amount than an existing offer")

if property_data.state == "new":
property_data.state = "offer_received"

return super().create(vals)
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 @@
# the tag model will be here


from odoo import fields, models


class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Tag model for the estate properties"
name = fields.Char(required=True)
color = fields.Integer(default=1)
_sql_constraints = [("check_uniquness", " UNIQUE(name)", "Tag name must be unique")]

# order on which data will be fetched
_order = "name desc"
33 changes: 33 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from odoo import fields, models, api


# Property type model for properties
# prpoerties can be of type house, penthouse, etc.
class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Type of properties of estate model"
name = fields.Char(required=True)

property_ids = fields.One2many("estate.property", "property_type_id")

offer_ids = fields.One2many(
"estate.property.offer", "property_type_id", "Offer For Property Type"
)

offer_count = fields.Integer(compute="_compute_offer_count")
sequence = fields.Integer("Sequence", default=1)

# sql constrains :::

_sql_constraints = [
("check_uniquness", " UNIQUE(name)", "Type of property name must be unique")
]

# order on which data will be fetched

_order = "sequence, name desc"

@api.depends("offer_ids")
def _compute_offer_count(self):
for data in self:
data.offer_count = len(data.offer_ids)
13 changes: 13 additions & 0 deletions estate/models/inherited_res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# This model inherits from res.users model

from odoo import models, fields


class InheritedResUsers(models.Model):
_inherit = "res.users"
property_ids = fields.One2many(
"estate.property",
"user_id",
"Estate Property",
domain=[("state", "in", ["new", "offer_received"])],
)
7 changes: 7 additions & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
estate.access_estate_property, ir.model.access,model_estate_property,base.group_user,1,1,1,1
estate.access_estate_property_type,ir.model.access,model_estate_property_type,base.group_user,1,1,1,1

estate.access_estate_property_tag,ir.model.access,model_estate_property_tag,base.group_user,1,1,1,1

estate.access_estate_property_offer,ir.model.access,model_estate_property_offer,base.group_user,1,1,1,1
Loading