Skip to content
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

[ADD] estate: add real estate module #221

Draft
wants to merge 19 commits into
base: 18.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"python.analysis.typeCheckingMode": "standard",
"python.languageServer": "None"
}
3 changes: 3 additions & 0 deletions estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import models
from . import wizard
from . import controllers
33 changes: 33 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "Estate",
"summary": """
Starting module for "Master the Odoo web framework, chapter 1: Build a Clicker game"
""",
"description": """
Starting module for "Master the Odoo web framework, chapter 1: Build a Clicker game"
""",
"author": "Odoo",
"website": "https://www.odoo.com",
"category": "Real Estate/Brokerage",
"version": "0.1",
"application": True,
"installable": True,
"depends": ["base_setup", "website"],
"data": [
"security/estate_security.xml",
"security/ir.model.access.csv",
"report/estate_property_templates.xml",
"report/estate_property_report.xml",
"views/estate_property_views.xml",
"views/estate_property_offer_views.xml",
"views/estate_property_type_views.xml",
"views/estate_property_tags_views.xml",
"views/res_users_views.xml",
"views/estate_menu_views.xml",
"views/estate_website_template.xml",
"data/estate.property.type.csv",
"wizard/estate_property_wizard_view.xml",
],
"demo": ["demo/estate_property_demo.xml", "demo/estate_property_offers.xml"],
"license": "AGPL-3",
}
1 change: 1 addition & 0 deletions estate/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import estate_website
56 changes: 56 additions & 0 deletions estate/controllers/estate_website.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from odoo import http
from odoo.http import request


class EstateWebsite(http.Controller):
@http.route(
["/properties", "/properties/page/<int:page>"],
type="http",
auth="user",

Choose a reason for hiding this comment

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

we can use auth="public" here

website=True,
)
def list_properties(self, page=1, **kwargs):
step = 6
offset = (page - 1) * step

#! Fetch only the properties for the current page
properties = (
request.env["estate.property"]
.sudo()
.search(
[
"&",
("status", "in", ["new", "offer_received", "offer_accepted"]),
("active", "=", True),
],
limit=step,
offset=offset,
)
)

total_properties = request.env["estate.property"].sudo().search_count(
[
"&",
("status", "in", ["new", "offer_received", "offer_accepted"]),
("active", "=", True),
]
)

pager = request.website.pager(
url="/properties", total=total_properties, step=step, page=page
)

# Render the template with paginated properties and the pager
return request.render(
"estate.listing_page",
{"properties": properties, "pager": pager},
)

@http.route(
"/property/<model('estate.property'):property>",
type="http",
auth="user",
website=True,
)
def property_detail(self, property, **kwargs):
return request.render("estate.property_detail_page", {"property": property})
5 changes: 5 additions & 0 deletions estate/data/estate.property.type.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id,name
property_1,Residential
property_2,Commercial
property_3,Industrial
property_4,Land
66 changes: 66 additions & 0 deletions estate/demo/estate_property_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Record 1: Big Villa -->
<record id="estate_property_big_villa" model="estate.property">
<field name="name">Big Villa</field>
<field name="status">new</field>
<field name="description">A nice and big villa</field>
<field name="postcode">12345</field>
<field name="date_availability">2020-02-02</field>
<field name="expected_price">160000.00</field>
<field name="bedrooms">6</field>
<field name="living_area">100</field>
<field name="facades">4</field>
<field name="garage">True</field>
<field name="garden">True</field>
<field name="garden_area">100000</field>
<field name="garden_orientation">south</field>
<field name="property_type_id" ref="estate.property_1"/>
<field name="image" type="base64" file="estate/static/demo_images/1.jpg"></field>
</record>

<!-- Record 2: Trailer Home -->
<record id="estate_property_trailer_home" model="estate.property">
<field name="name">Trailer home</field>
<field name="status">canceled</field>
<field name="description">Home in a trailer park</field>
<field name="postcode">54321</field>
<field name="date_availability">1970-01-01</field>
<field name="expected_price">100000.00</field>
<field name="selling_price">120000.00</field>
<field name="bedrooms">1</field>
<field name="living_area">10</field>
<field name="facades">4</field>
<field name="garage">False</field>
<field name="property_type_id" ref="estate.property_1"/>
<field name="image" type="base64" file="estate/static/demo_images/2.jpg"></field>
</record>

<!-- record 3: Duplex with offers made directly here -->
<record id="estate_property_duplex" model="estate.property">
<field name="name">Duplex</field>
<field name="status">new</field>
<field name="description">A nice duplex</field>
<field name="postcode">12345</field>
<field name="date_availability">2020-02-02</field>
<field name="expected_price">160000.00</field>
<field name="selling_price">160000.00</field>
<field name="bedrooms">6</field>
<field name="living_area">100</field>
<field name="facades">4</field>
<field name="garage">True</field>
<field name="property_type_id" ref="estate.property_1"/>
<field name="image" type="base64" file="estate/static/demo_images/3.jpg"></field>
<field name="offer_ids" eval="[
Command.create({'partner_id':ref('base.res_partner_12'),'price': 150000.00}),
Command.create({'partner_id':ref('base.res_partner_18'),'price':155000.00}),
Command.create({'partner_id':ref('base.res_partner_10'),'price':160000.00})]"/>
</record>

<record id="menu_properties" model="website.menu">
<field name="name">Properties</field>
<field name="url">/properties</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence" type="int">60</field>
</record>
</odoo>
33 changes: 33 additions & 0 deletions estate/demo/estate_property_offers.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record id="big_villa_offer_2" model="estate.property.offer">
<field name="partner_id" ref="base.res_partner_12"/>
<field name="property_id" ref="estate.estate_property_big_villa" />
<field name="price">150000.00</field>
<field name="validity">14</field>
</record>

<record id="big_villa_offer_3" model="estate.property.offer">
<field name="partner_id" ref="base.res_partner_2"/>
<field name="property_id" ref="estate.estate_property_big_villa" />
<field name="price">150001.00</field>
<field name="validity">14</field>
</record>

<record id="big_villa_offer_1" model="estate.property.offer">
<field name="partner_id" ref="base.res_partner_12"/>
<field name="property_id" ref="estate.estate_property_big_villa" />
<field name="price">160000.00</field>
<field name="validity">14</field>
<field name="date_deadline" eval="(datetime.today() + relativedelta(months=3)).date()" />
</record>

<function model="estate.property.offer" name="action_refuse" eval="[[ref('big_villa_offer_2'), ref('big_villa_offer_3')]]">
</function>

<function model="estate.property.offer" name="action_accept" eval="[ref('big_villa_offer_1')]">
</function>


</odoo>
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_tags
from . import estate_property_offer
from . import inherited_users
144 changes: 144 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
from odoo import models, fields, api
from odoo.exceptions import UserError
from datetime import datetime
from dateutil.relativedelta import relativedelta

Choose a reason for hiding this comment

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

Suggested change
from odoo import models, fields, api
from odoo.exceptions import UserError
from datetime import datetime
from dateutil.relativedelta import relativedelta
from datetime import datetime
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.exceptions import UserError

Generally we first make external imports and then we import from odoo in alphabetical order.
Need to adapt in all files



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

name = fields.Char(required=True)
description = fields.Text("description")
postcode = fields.Char()
date_availability = fields.Date(
copy=False, default=(datetime.today() + relativedelta(months=3)).date()
)
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer()

living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(
selection=[
("north", "NORTH"),
("south", "SOUTH"),
("east", "EAST"),
("west", "WEST"),
],
)
sequence = fields.Integer("Sequence", default=1)
active = fields.Boolean(default=True)
property_type_id = fields.Many2one("estate.property.type", ondelete="cascade")
salesman_id = fields.Many2one(
"res.users", ondelete="restrict", default=lambda self: self.env.user
)
buyer_id = fields.Many2one("res.partner", ondelete="restrict", copy=False)
tag_ids = fields.Many2many("estate.property.tags")
offer_ids = fields.One2many(
comodel_name="estate.property.offer", inverse_name="property_id"
)
total_area = fields.Integer(compute="_compute_total_area")
best_offer = fields.Float(compute="_compute_best_offer")
status = fields.Selection(
selection=[
("new", "New"),
("offer_received", "Offer received"),
("offer_accepted", "Offer accepted"),
("sold", "Sold"),
("canceled", "Canceled"),
],
default="new",
copy=False,
)
company_id = fields.Many2one(
"res.company", default=lambda self: self.env.company, required=True
)
image = fields.Image("Image")
_sql_constraints = [
(
"check_expected_price",
"CHECK(expected_price > 0)",
"Expected price must be strictly positive",
),
(
"check_selling_price",
"CHECK(selling_price >= 0)",
"Selling price must be non-negative",
),
]

@api.ondelete(at_uninstall=False)
def _unlink_if_state_new_canceled(self):
for record in self:
if (
record.status == "offer_received"
or record.status == "offer_accepted"
or record.status == "sold"
):
raise UserError("Only new and canceled properties can be deleted")

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

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

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

def action_sold(self):
for record in self:
if (
record.status == "new"
or record.status == "offer_accepted"
or record.status == "offer_received"
):
record.status = "sold"
elif record.status == "canceled":
raise UserError("Canceled property can't be sold")
else:
raise UserError("Property already sold")

def action_cancel(self):
for record in self:
if (
record.status == "new"
or record.status == "offer_accepted"
or record.status == "offer_received"
):
record.status = "canceled"
elif record.status == "sold":
raise UserError("Sold property can't be canceled")
else:
raise UserError("Property already canceled")

def action_make_offer(self):
return {
"name": "Make offer",
"type": "ir.actions.act_window",
"target": "new",
"view_mode": "form",
"res_model": "estate.property.make.offer",
"context": {
"default_property_ids": self.ids,
},

Choose a reason for hiding this comment

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

it will not be needed we can get the selected property by self.env.context.get('active_ids')

}
Loading