Skip to content

Commit

Permalink
[IMP] estate: added widgets, stat button, inherited methods and kanba…
Browse files Browse the repository at this point in the history
…n view

Day 5:
Added inline list view inside property type model's form view to view properties
linked with each type.

Added statusbar widget for displaying Four states such as New, Offer Received,
Offer Accepted and Sold.

Added Model ordering and Manual ordering(using sequence) in list view's of
estate.property,estate.property.type,estate.property.tag and property.offer
along with widget options for adding colors and conditional display of buttons
by using 'invisible' attribute in the field.Also added stat button

Added business logic to CRUD methods using python inheritance.
added ResUsers class inheriting res.users model setting one2many relationship
b/w a user and properties linked with that user.
added properties to base.view_users_form

Created estate_account module for invoicing of the properties sold

Added kanban view to the properties section grouped by property type.
  • Loading branch information
nmak-odoo committed Feb 11, 2025
1 parent be8436b commit 7482e90
Show file tree
Hide file tree
Showing 16 changed files with 330 additions and 51 deletions.
30 changes: 16 additions & 14 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

{
'name': 'Real Estate',
'author': 'nmak',
'category': 'Tutorials/estate',
'depends': ['base'],
'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_menus.xml',
"name": "Real Estate",
"author": "nmak",
"category": "Tutorials/estate",
"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/res_users_view.xml",
"views/estate_menus.xml",
],
'installable': True,
'application': True,
'auto_install': True,
'license': 'LGPL-3',
"installable": True,
"application": True,
"auto_install": True,
"license": "LGPL-3",
}
1 change: 1 addition & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
from . import res_users
30 changes: 21 additions & 9 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
class EstateProperty(models.Model):
_name = "estate.property"
_description = "Properties/Advertisements created and managed in estate"
_order = "id desc"
_sql_constraints = [
(
"check_expected_price",
Expand All @@ -24,7 +25,7 @@ class EstateProperty(models.Model):
description = fields.Text(string="Property Description")
postcode = fields.Char(string="Postal code")
date_availability = fields.Date(
"Availability From",
string="Availability From",
copy=False,
default=fields.Date.add(fields.Date.today(), months=3),
)
Expand All @@ -42,9 +43,10 @@ class EstateProperty(models.Model):
("south", "South"),
("east", "East"),
("west", "West"),
]
],
string="Garden Orientation"
)
total_area = fields.Float(compute="_compute_total_area")
total_area = fields.Float(string="Total Area (sqft)", compute="_compute_total_area")
active = fields.Boolean(default=True)
state = fields.Selection(
selection=[
Expand Down Expand Up @@ -73,18 +75,28 @@ class EstateProperty(models.Model):
compute="_compute_best_price", string="Best Offer", store=True
)

@api.ondelete(at_uninstall=False)
def _check_property_deletion(self):
for record in self:
if record.state not in ["new", "cancelled"]:
raise UserError(
"You can only delete properties in 'New' or 'Cancelled' state"
)

@api.depends("living_area", "garden_area")
def _compute_total_area(self):
"""this function adds the value of living_area and garden_area to total_area field"""
for record in self:
record.total_area = record.living_area + record.garden_area

@api.depends("offer_ids")
def _compute_best_price(self):
"""this function computes the best_price field by looking for the maximum price offered"""
for record in self:
record.best_price = max(record.offer_ids.mapped("price"), default=0)

@api.onchange("garden")
def _onChange_garden(self):
def _onChange_garden_status(self):
if self.garden:
self.garden_area = 10
self.garden_orientation = "north"
Expand All @@ -94,8 +106,8 @@ def _onChange_garden(self):

def action_sold(self):
for record in self:
if record.state == "canceled":
raise UserError("Canceled property cannot be sold")
if record.state == "cancelled":
raise UserError("Cancelled property cannot be sold")
if not record.buyer_id or not record.selling_price:
raise UserError(
"Property must have an accepted offer before being sold"
Expand All @@ -105,8 +117,8 @@ def action_sold(self):
def action_cancel(self):
for record in self:
if record.state == "sold":
raise UserError("Sold Property can't be canceled")
record.state = "canceled"
raise UserError("Properties that are already sold, cannot be cancelled")
record.state = "cancelled"

@api.constrains("selling_price", "expected_price")
def _check_selling_price(self):
Expand All @@ -116,5 +128,5 @@ def _check_selling_price(self):
and record.selling_price < record.expected_price * 0.9
):
raise ValidationError(
"The selling price cannot be less than 90% of the expected price"
"The selling price of the property cannot be less than 90% of the expected price"
)
28 changes: 26 additions & 2 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models
from odoo.exceptions import UserError
from odoo.exceptions import UserError, ValidationError


class EstatePropertyOffer(models.Model):
Expand All @@ -10,6 +10,7 @@ class EstatePropertyOffer(models.Model):
_sql_constraints = [
("check_price", "CHECK(price > 0)", "Offer price must be strictly positive."),
]
_order = "price desc"

price = fields.Float(string="Price", required=True)
status = fields.Selection(
Expand All @@ -19,20 +20,43 @@ class EstatePropertyOffer(models.Model):
)
partner_id = fields.Many2one("res.partner", string="Partner", required=True)
property_id = fields.Many2one("estate.property", string="Property", required=True)
property_type_id = fields.Many2one(
"estate.property.type",
string="Property Type",
related="property_id.property_type_id",
)
validity = fields.Integer(default=7, string="Validity (days)")
deadline = fields.Date(
string="Deadline", compute="_compute_deadline", inverse="_inverse_deadline", store="True"
string="Deadline",
compute="_compute_deadline",
inverse="_inverse_deadline",
store="True",
)

@api.model_create_multi
def create(self, vals_list):
records = super().create(vals_list)
for record in records:
property_id = record.property_id
if property_id.best_price and record.price <= property_id.best_price:
raise ValidationError(
"The new offer must be higher than the existing best offer."
)
property_id.state = "offer_recieved"

return records

@api.depends("validity")
def _compute_deadline(self):
"""This function helps to automatically set the deadline date acc. to the validity days entered"""
for offer in self:
if offer.create_date:
offer.deadline = fields.Date.add(offer.create_date, days=offer.validity)
else:
offer.deadline = fields.Date.today()

def _inverse_deadline(self):
"""This function sets validity(days) acc. to the deadline date entered"""
for offer in self:
if offer.create_date:
offer.validity = (offer.deadline - offer.create_date.date()).days
Expand Down
4 changes: 3 additions & 1 deletion estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "tags attached to property"
_description = "Tags attached to the property"
_sql_constraints = [
("unique_tag_name", "UNIQUE(name)", "Property tag name must be unique."),
]
_order = "name"

name = fields.Char(string="Property Tag", required=True)
color = fields.Integer(string="Color")
17 changes: 16 additions & 1 deletion estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import models, fields
from odoo import api, models, fields


class EstatePropertyType(models.Model):
Expand All @@ -9,5 +9,20 @@ class EstatePropertyType(models.Model):
_sql_constraints = [
("unique_type_name", "UNIQUE(name)", "Property type name must be unique."),
]
_order = "sequence, name"

name = fields.Char(string="Property Type", required=True)
property_ids = fields.One2many(
"estate.property",
"property_type_id",
string="Properties",
required=True
)
offer_ids = fields.One2many("estate.property.offer", "property_type_id", string="Offers")
offer_count = fields.Integer(compute="_compute_offer_count", string="Offer Count")
sequence = fields.Integer('Sequence', default=1, help="Used to manually order properties")

@api.depends("offer_ids")
def _compute_offer_count(self):
for record in self:
record.offer_count = len(record.offer_ids)
14 changes: 14 additions & 0 deletions estate/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import fields, models


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

property_ids = fields.One2many(
"estate.property",
"salesperson_id",
string="Properties",
domain=[("state", "in", ["new", "offer_recieved"])],
)
1 change: 1 addition & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ access_estate_property,access_estate_property,model_estate_property,base.group_u
access_estate_property_type,access_estate_property_type,model_estate_property_type,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
access_res_users,access_res_users,model_res_users,base.group_user,1,1,1,1
44 changes: 44 additions & 0 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0"?>
<odoo>
<!-- List View for Offers -->
<record id="view_estate_property_offer_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">
<field name="price" />
<field name="partner_id" />
<field name="status" />
<field name="property_id" />
<field name="property_type_id" />
</list>
</field>
</record>

<!-- Form View for Offers -->
<record id="view_estate_property_offer_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="Offer">
<sheet>
<group>
<field name="property_id" />
<field name="partner_id" />
<field name="price" />
<field name="validity" />
<field name="deadline" readonly="1" />
<field name="status" />
</group>
</sheet>
</form>
</field>
</record>

<record id="estate_property_offer_action" model="ir.actions.act_window">
<field name="name">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>
</odoo>
26 changes: 25 additions & 1 deletion estate/views/estate_property_type_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<field name="model">estate.property.type</field>
<field name="arch" type="xml">
<list string="Property Types">
<field name="sequence" widget="handle" />
<field name="name" />
</list>
</field>
Expand All @@ -15,10 +16,32 @@
<field name="model">estate.property.type</field>
<field name="arch" type="xml">
<form string="Property Type">
<header>
<button name="%(estate.estate_property_offer_action)d"
type="action"
class="oe_stat_button"
icon="fa-list">
<field name="offer_count"/>
</button>
</header>
<sheet>
<group>
<field name="name" />
<h1>
<field name="name" />
</h1>
<field name="sequence" />
</group>
<notebook>
<page string="Properties">
<field name="property_ids">
<list>
<field name="name" />
<field name="expected_price" />
<field name="state" />
</list>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
Expand All @@ -29,4 +52,5 @@
<field name="res_model">estate.property.type</field>
<field name="view_mode">list,form</field>
</record>

</odoo>
Loading

0 comments on commit 7482e90

Please sign in to comment.