Skip to content

[ADD] estate:Create first Estate module #731

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 8 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_account/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
9 changes: 9 additions & 0 deletions estate_account/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "estate.account",
"depends": ["base", "account", "realestate"],
"data": [
"security/ir.model.access.csv",
],
"application": True,
"installable": True,
}
2 changes: 2 additions & 0 deletions estate_account/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import estate_account
from . import realestate
6 changes: 6 additions & 0 deletions estate_account/models/estate_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from odoo import fields, models


class EstateAccount(models.Model):
_name = "estate.account"
name = fields.Char(required=True)
24 changes: 24 additions & 0 deletions estate_account/models/realestate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from odoo import models, fields, Command


class Realestate(models.Model):
_name = "realestate"
_inherit = "realestate"

def action_set_property_sold(self):
self.env["account.move"].create(
{
"partner_id": super().buyer_id.id,
"move_type": "out_invoice",
"line_ids": [
Command.create(
{
"name": self.name,
"quantity": 1,
"price_unit": super().selling_price,
}
)
],
}
)
return super().action_set_property_sold()
3 changes: 3 additions & 0 deletions estate_account/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_estate_account,access.estate_acount,model_estate_account,,1,1,1,1
access_realestate,access.realestate,model_realestate,,1,1,1,1
1 change: 1 addition & 0 deletions realestate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
16 changes: 16 additions & 0 deletions realestate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "realestate",
"depends": ["base"],
"data": [
"security/ir.model.access.csv",
"views/estate_view.xml",
"views/owner_view.xml",
"views/tag_view.xml",
"views/seller_view.xml",
"views/buyer_view.xml",
"views/offer_view.xml",
"views/type_view.xml",
],
"application": True,
"installable": True,
}
7 changes: 7 additions & 0 deletions realestate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from . import realestate
from . import owner
from . import types
from . import tags
from . import seller
from . import buyer
from . import offer
Comment on lines +1 to +7
Copy link

Choose a reason for hiding this comment

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

nitpick: by convention, we prefix model names with the name of the module

Suggested change
from . import realestate
from . import owner
from . import types
from . import tags
from . import seller
from . import buyer
from . import offer
from . import realestate_property
from . import realesate_owner
....

8 changes: 8 additions & 0 deletions realestate/models/buyer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from odoo import fields, models


class Buyer(models.Model):
_name = "buyer"
Copy link

Choose a reason for hiding this comment

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

Suggested change
_name = "buyer"
_name = "realestate_buyer"

(etc...)


name = fields.Char(required=True)
properties_ids = fields.One2many("realestate", "buyer_id")
60 changes: 60 additions & 0 deletions realestate/models/offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from odoo import api, fields, models
from datetime import timedelta
from odoo.exceptions import UserError


class Offer(models.Model):
_name = "offer"
_order = "price desc"
price = fields.Float()
status = fields.Selection([("accepted", "Accepted"), ("refused", "Refused")])
buyer_id = fields.Many2one("res.partner")
property_id = fields.Many2one("realestate")
property_type_id = fields.Many2one(related="property_id.type_id")
state = fields.Selection(related="property_id.state")
validity = fields.Integer()
deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline")

Choose a reason for hiding this comment

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

It's a good habit to add the suffix _date on a Date field.

highest_offer = fields.Float(compute="_calc_highest_price")

@api.depends("validity")
def _compute_deadline(self):
for record in self:
record.deadline = (
record.create_date or fields.Datetime.now()
).date() + timedelta(days=record.validity)

def _inverse_deadline(self):
for record in self:
record.validity = (
record.deadline - (record.create_date or fields.Datetime.now()).date()
).days

def set_offer_accepted(self):
for record in self:
if record.property_id.state == "sold":
raise UserError("This property is already sold")
record.status = "accepted"
record.property_id.selling_price = record.price

Choose a reason for hiding this comment

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

You can use 'mappedto avoid repeatingrecord.property_id`

record.property_id.buyer_id = record.buyer_id
record.property_id.state = "offer_accepted"

def set_offer_refused(self):
for record in self:
if record.property_id.state == "sold":
raise UserError("This property is already sold")
self.status = "refused"

_sql_constraints = [
(
"check_offer_price",
"CHECK(price> 0)",
"Offer price must be positive.",
)
]

def create(self, vals):
res = super(Offer, self).create(vals)

if res.price < res.property_id.best_price:
raise UserError("Please set a higher price!")
return res
10 changes: 10 additions & 0 deletions realestate/models/owner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from odoo import fields, models


class Owner(models.Model):
_name = "owner"

name = fields.Char(required=True)
address = fields.Char()
phone = fields.Char()
properties_ids = fields.One2many("realestate", "owner_id")
124 changes: 124 additions & 0 deletions realestate/models/realestate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError


class Realestate(models.Model):
_name = "realestate"
_description = "Estate"
_order = "type_id"
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date()
expected_price = fields.Float(required=True)
selling_price = fields.Float(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(
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"),
],
default="new",
)
owner_id = fields.Many2one("buyer")
type_id = fields.Many2one("types", widget="handle")
tag_ids = fields.Many2many("tags", string="Tags")
seller_id = fields.Many2one("seller")
buyer_id = fields.Many2one("res.partner")
offer_ids = fields.One2many("offer", "property_id")
status = fields.Selection(related="offer_ids.status")
price = fields.Float(related="offer_ids.price")
validity = fields.Integer(related="offer_ids.validity")
deadline = fields.Date(related="offer_ids.deadline")
Comment on lines +47 to +50
Copy link

Choose a reason for hiding this comment

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

You cannot created related fields that pass through an x2many (you can look at the warning in the Doc)

What are you trying to accomplish here? What do you think the value should be?

total_area = fields.Integer(compute="_compute_total")
best_price = fields.Float(compute="_max_offer_price")

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

@api.depends("offer_ids")

Choose a reason for hiding this comment

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

Depends only on offer_ids, not offer_ids.price, which means the method won’t be triggered when an offer’s price changes, unless a new offer is added/removed.

def _max_offer_price(self):

Choose a reason for hiding this comment

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

the method should start with _compute_...

for record in self:
record.best_price = max(record.offer_ids.mapped("price"), default=0)

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

Choose a reason for hiding this comment

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

Suggested change
self.garden_orientation = ""
self.garden_orientation = False


def action_set_property_sold(self):
for record in self:
if record.state == "cancelled":
raise UserError("Cancelled properties cannot be sold")
record.state = "sold"

def action_set_property_cancelled(self):
for record in self:
if record.state == "sold":
raise UserError("Sold properties cannot be Cancelled")
record.state = "cancelled"

# def action_set_offer_accepted(self):
# for record in self:
# if record.status

_sql_constraints = [
(
"check_expected_price",
"CHECK(expected_price> 0)",
Copy link

Choose a reason for hiding this comment

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

nitpick:

Suggested change
"CHECK(expected_price> 0)",
"CHECK(expected_price > 0)",

(more places)

"Expected price must be positive.",
),
(
"check_selling_price",
"CHECK(selling_price> 0)",
"Selling price must be positive.",
),
(
"check_offer_price",
"CHECK(price> 0)",
"Offer price must be positive.",
),
]

@api.constrains("selling_price")
def _validate_selling_price(self):
for record in self:
if record.selling_price < 0.9 * record.expected_price:
raise ValidationError(
"Selling price must be at least 90'%' of the expected price"
Copy link

Choose a reason for hiding this comment

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

  • why the quotes around the %
  • try wrap user facing text in a call to _ so it can be translated

)

@api.constrains("offer_ids")
def _check_for_offers(self):
if len(self.offer_ids) > 0:
self.state = "offer_received"

@api.ondelete(at_uninstall=False)
def _unlink_new_properties(self):
for record in self:
if record.state != "new" or record.state != "cancelled":
raise UserError("Property cannot be deleted!")
8 changes: 8 additions & 0 deletions realestate/models/seller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from odoo import fields, models


class Seller(models.Model):
_name = "seller"

name = fields.Char(required=True)
properties_ids = fields.One2many("realestate", "seller_id")
8 changes: 8 additions & 0 deletions realestate/models/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from odoo import fields, models


class Tags(models.Model):
_name = "tags"
_order = "name"
name = fields.Char(required=True)
color = fields.Integer(string="Color") # <-- add this
24 changes: 24 additions & 0 deletions realestate/models/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from odoo import api, fields, models


class Types(models.Model):
_name = "types"
_order = "sequence"
name = fields.Char(required=True)
properties_ids = fields.One2many("realestate", "type_id")
sequence = fields.Integer("Sequence", default=1)
offer_ids = fields.One2many("offer", "property_type_id")
offer_count = fields.Integer(compute="_compute_offer_count")

@api.depends("offer_ids")
def _compute_offer_count(self):
for record in self:
record.offer_count = len(record.offer_ids)

_sql_constraints = [
(
"unique_name",
"unique(name)",
"Name already exists",
),
]
8 changes: 8 additions & 0 deletions realestate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_realestate,access.realestate,model_realestate,,1,1,1,1
access_owner,access.owner,model_owner,,1,1,1,1
access_types,access.types,model_types,,1,1,1,1
access_tags,access.tags,model_tags,,1,1,1,1
access_seller,access.seller,model_seller,,1,1,1,1
access_buyer,access.buyer,model_buyer,,1,1,1,1
access_offer,access.offer,model_offer,,1,1,1,1
17 changes: 17 additions & 0 deletions realestate/views/buyer_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<odoo>


<record id="buyer_action" model="ir.actions.act_window">
<field name="name">Buyer</field>
<field name="res_model">buyer</field>
<field name="view_mode">list,form</field>
</record>

<menuitem id="real_estate_root_menu" name="Real Estate">
<menuitem id="Users_menu" name="Users">
<menuitem id="buyer_menu_item" name="Buyers" action="buyer_action" />
</menuitem>
</menuitem>


</odoo>
Copy link

Choose a reason for hiding this comment

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

Some of your files are missing the eol character at the end of the last line (hence the red signs in the github ui) -- could you configure your editor so it doesn't do that please?

Loading