Skip to content
Open
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
10 changes: 9 additions & 1 deletion awesome_owl/static/src/playground.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Component } from "@odoo/owl";
import { Component, useState } from "@odoo/owl";

export class Playground extends Component {
static template = "awesome_owl.playground";

setup() {
this.state = useState({ value: 0 });
}

increment() {
this.state.value++
}
}
5 changes: 2 additions & 3 deletions awesome_owl/static/src/playground.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
<templates xml:space="preserve">

<t t-name="awesome_owl.playground">
<div class="p-3">
hello world
</div>
<p>Counter: <t t-esc="state.value"/></p>
<button class="btn btn-primary" t-on-click="increment">Increment</button>
</t>

</templates>
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",
"application": True,
"depends": ["base"],
"author": "Odoo S.A.",
"license": "LGPL-3",
"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_property_offer_views.xml",
"views/estate_menus.xml",
"views/res_users_views.xml",
],
}
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_offer
from . import estate_property_tag
from . import estate_property_type
from . import res_users
132 changes: 132 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
from dateutil.relativedelta import relativedelta

from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools.float_utils import float_compare, float_is_zero
Comment thread
sidum-odoo marked this conversation as resolved.


class EstateProperty(models.Model):
_name = "estate.property"
_description = "Estate Property"
_order = "id desc"
Comment thread
sidum-odoo marked this conversation as resolved.

name = fields.Char(string="Title", required=True)
Comment thread
sidum-odoo marked this conversation as resolved.
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(
string="Available From",
copy=False,
default=fields.Date.today() + relativedelta(days=3),
)
expected_price = fields.Float(required=True)
selling_price = fields.Float(readonly=True, copy=False)
bedrooms = fields.Integer(default=2)
living_area = fields.Integer(string="Living Area (sqm)")
facades = fields.Integer()
garages = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer(string="Garden Area (sqm)")
garden_orientation = fields.Selection(
selection=[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
],
)
active = fields.Boolean(default=True)
state = fields.Selection(
readonly=False,
selection=[
("new", "New"),
("offer_received", "Offer Received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("canceled", "Canceled"),
],
string="Status",
default="new",
required=True,
)
type_id = fields.Many2one("estate.property.type", string="Property Type")
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False, readonly=True)
salesperson_id = fields.Many2one(
"res.users",
string="Salesman",
default=lambda self: self.env.user,
)
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")
best_price = fields.Float(compute="_compute_best_price")

_check_expected_price = models.Constraint(
"CHECK(expected_price > 0)",
"A property expected price should be strictly positive.",
)

_check_selling_price = models.Constraint(
"CHECK(selling_price >= 0)", "A property selling price should be positive."
)

@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.depends("offer_ids.price")
def _compute_best_price(self):
for record in self:
record.best_price = (
max(record.offer_ids.mapped("price")) if record.offer_ids else 0
)

@api.constrains("selling_price", "expected_price")
def _check_selling_price(self):
for record in self:
if not float_is_zero(record.selling_price, precision_digits=2):
if (
float_compare(
record.selling_price,
0.9 * record.expected_price,
precision_digits=2,
)
< 0
):
Comment thread
sidum-odoo marked this conversation as resolved.
raise ValidationError(
_(
"The selling price must be at least 90% of the expected price"
)
)
Comment thread
sidum-odoo marked this conversation as resolved.

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

@api.ondelete(at_uninstall=False)
def _unlink_except_new_or_canceled(self):
if any(record.state == "new" or record.state == "canceled" for record in self):
raise UserError(_("Can't delete a new or canceled property."))

def action_set_sold(self):
for record in self:
if record.state == "canceled":
raise UserError(_("Canceled properties cannont be sold."))
if not any(offer.status == "accepted" for offer in record.offer_ids):
raise UserError(
_("You can't sell a property with no accepted offers on it")
)
record.state = "sold"
return True

def action_set_canceled(self):
for record in self:
if record.state == "sold":
raise UserError(_("Sold properties cannont be canceled."))
record.state = "canceled"
return True
87 changes: 87 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from dateutil.relativedelta import relativedelta

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


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

price = fields.Float()
status = fields.Selection(
selection=[("accepted", "Accepted"), ("refused", "Refused")],
copy=False,
)
partner_id = fields.Many2one("res.partner", required=True)
property_id = fields.Many2one("estate.property", required=True)
validity = fields.Integer(default=7, string="Validity (days)")
date_deadline = fields.Date(
compute="_compute_date_deadline",
inverse="_inverse_date_deadline",
string="Deadline",
)
property_type_id = fields.Many2one(
string="Property Type",
related="property_id.type_id",
store=True,
)

_check_price = models.Constraint(
"CHECK(price > 0)", "An offer price should be strictly positive."
)

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

def _inverse_date_deadline(self):
for record in self:
delta = record.date_deadline - (
fields.Date.to_date(record.create_date) or fields.Date.today()
)
record.validity = delta.days

@api.model
def create(self, vals_list):
properties = self.env["estate.property"].browse(
[vals["property_id"] for vals in vals_list]
)
properties_by_id = {prop.id: prop for prop in properties}
for vals in vals_list:
property = properties_by_id.get(vals["property_id"])
best_price = property.best_price
if property.state == "sold":
raise UserError(_("You can't create an offer for a sold property."))
if vals["price"] < best_price:
raise UserError(
_(
"You can't create an offer with a lower amount than an existing offer."
)
)
property.state = "offer_received"
return super().create(vals_list)
Comment thread
sidum-odoo marked this conversation as resolved.

def action_set_accepted(self):
states = self.property_id.offer_ids.mapped("status")
for record in self:
if "accepted" in states:
raise UserError(_("An offer has already been accepted."))
record.status = "accepted"
record.property_id.buyer_id = record.partner_id
record.property_id.selling_price = record.price
record.property_id.state = "offer_accepted"
return True

def action_set_refused(self):
for record in self:
previous_status = record.status
record.status = "refused"
if previous_status == "accepted":
record.property_id.buyer_id = None
record.property_id.selling_price = 0
Comment thread
sidum-odoo marked this conversation as resolved.
return True
14 changes: 14 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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()

_check_unique_name = models.Constraint(
"UNIQUE(name)", "A property tag name should be unique."
)
27 changes: 27 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from odoo import api, fields, models


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

name = fields.Char(required=True)
property_ids = fields.One2many("estate.property", "type_id", string="Properties")
sequence = fields.Integer()
offer_ids = fields.One2many(
"estate.property.offer",
"property_type_id",
string="Offers",
)
offer_count = fields.Integer(compute="_compute_offer_count")

_check_unique_name = models.Constraint(
"UNIQUE(name)", "A property tag name should be unique."
)

@api.depends("offer_ids")
def _compute_offer_count(self):
for record in self:
record.offer_count = len(record.offer_ids)
return True
12 changes: 12 additions & 0 deletions estate/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from odoo import fields, models


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

property_ids = fields.One2many(
"estate.property",
"salesperson_id",
domain="['|', ('state', '=', 'new'), ('state', '=', 'offer_received')]",
string="Real Estate Properties",
)
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
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_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
1 change: 1 addition & 0 deletions estate/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_property_offer
Loading