Skip to content
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
18 changes: 18 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Real Estate",
"version": "1.0",
"author": "yacha",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When you are working for a company, you should keep the author name as Odoo S.A. or just skip writing it.

"depends": ["base"],
"application": True,
"category": "Tutorials",
"installable": True,
"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",
],
}
4 changes: 4 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
77 changes: 77 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from odoo import api, models, fields
from dateutil.relativedelta import relativedelta
Comment on lines +1 to +2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You can refer to this for ordering your imports.



@api.model
def _default_date_availability(self):
return fields.Date.today() + relativedelta(months=3)
Comment on lines +5 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This should be placed inside the class.
You can follow this for ordering your fields, functions, constraints etc. You can check the attribute order followed in a model.


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

active = fields.Boolean(default=True)

name = fields.Char(required=True, default="Unknown")
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(copy=False, default=_default_date_availability)
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(
[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
]
)
Comment on lines +28 to +35

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
garden_orientation = fields.Selection(
[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
]
)
garden_orientation = fields.Selection(
[
('north', "North"),
('south', "South"),
('east', "East"),
('west', "West"),
]
)

Try to keep the key i.e the technical strings in single quotes and the values which are to be displayed to the user in double quotes

state = fields.Selection(
[
("new", "New"),
("offer_received", "Offer Received"),
("offer_accepted", "Offer Accepted"),
("sold", "Sold"),
("cancelled", "Cancelled"),
],
string="State",
required=True,
copy=False,
default="new",
)

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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

What if you do not write the string here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Odoo automatically derives the string name explicitly from the variable name. So if we don't give this string then also it is deriving string to Property Type. I tested it. So i think i should remove it.


salesperson_id = fields.Many2one(
"res.users", string="Salesperson", default=lambda self: self.env.user
)
Comment on lines +50 to +56

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
salesperson_id = fields.Many2one(
"res.users", string="Salesperson", default=lambda self: self.env.user
)
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
salesperson_id = fields.Many2one(
"res.users", string="Salesperson", default=lambda self: self.env.user
)

Do not leave unnecessary lines between the field declarations.


tag_ids = fields.Many2many("estate.property.tag", string="Tags")

offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

How will I be able to view that which offers are linked to which property?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The property_id field in estate.property.offer stores which property the offer belongs to. The One2many field uses this inverse field to retrieve all offers linked to that particular property.


total_area = fields.Integer(compute="_compute_total")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
total_area = fields.Integer(compute="_compute_total")
total_area = fields.Integer(compute="_compute_total_area")


best_price = fields.Float(compute="_compute_best_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.price")
def _compute_best_price(self):
for record in self:
if record.offer_ids:
record.best_price = max(record.offer_ids.mapped("price"))
else:
record.best_price = 0
14 changes: 14 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from odoo import models, fields


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

price = fields.Float()
status = fields.Selection(
[("accepted", "Accepted"), ("refused", "Refused")], copy=False
)

partner_id = fields.Many2one("res.partner", required=True)
property_id = fields.Many2one("estate.property", required=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

What is the difference between setting a field as required=True in the model and required="1" in the XML view?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

required=True makes the field mandatory at the model level, so it applies whenever the field is used. required="1" in the XML only makes the field mandatory in that specific view.

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


class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "Real Estate Property Tag"

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


class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "Real Estate Property Type"

name = fields.Char(required=True)
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Always leave an extra line at the end of every file.

35 changes: 35 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<menuitem
id="estate_menu_root"
name="Real Estate">

<menuitem
id="estate_advertisements_menu"
name="Advertisements">

<menuitem
id="estate_property_menu_action"
action="estate_property_action"/>

</menuitem>
Comment on lines +4 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
<menuitem
id="estate_menu_root"
name="Real Estate">
<menuitem
id="estate_advertisements_menu"
name="Advertisements">
<menuitem
id="estate_property_menu_action"
action="estate_property_action"/>
</menuitem>
<menuitem
id="estate_menu_root"
name="Real Estate"
>
<menuitem
id="estate_advertisements_menu"
name="Advertisements"
>
<menuitem
id="estate_property_menu_action"
action="estate_property_action"
/>
</menuitem>

Re-format this file a bit.


<menuitem
id="estate_property_type_menu"
name="Settings">

<menuitem
id="estate_property_type_menu_action"
action="estate_property_type_action"/>

<menuitem
id="estate_property_tag_menu_action"
action="estate_property_tag_action"/>

</menuitem>

</menuitem>


</odoo>
32 changes: 32 additions & 0 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record id="estate_property_offer_view_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>
<field name="price"/>
<field name="partner_id"/>
<field name="status"/>
</list>
</field>
</record>

<record id="estate_property_offer_view_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>
<sheet>
<group>
<field name="price"/>
<field name="partner_id"/>
<field name="status"/>
</group>
</sheet>
</form>
</field>
</record>

</odoo>
12 changes: 12 additions & 0 deletions estate/views/estate_property_tag_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record
id="estate_property_tag_action"
model="ir.actions.act_window">
<field name="name">Property Tags</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">list,form</field>
</record>

</odoo>
12 changes: 12 additions & 0 deletions estate/views/estate_property_type_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is it compulsory to write this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It's the XML declaration that identifies the file as an XML 1.0 document. It's the standard way to start an XML file.
If we don't write it still ODOO parses the XML files and shows no errors
But we still explicitly define the version of that xml file.

<odoo>

<record
id="estate_property_type_action"
model="ir.actions.act_window">
<field name="name">Property Types</field>
<field name="res_model">estate.property.type</field>
<field name="view_mode">list,form</field>
</record>

</odoo>
118 changes: 118 additions & 0 deletions estate/views/estate_property_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record
id="estate_property_view_list"
model="ir.ui.view">
<field name="name">estate.property.listview</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<list string="Properties">
<field name="name"/>
<field name="property_type_id"/>
<field name="tag_ids" widget="many2many_tags"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
</list>
</field>
</record>

<record
id="estate_property_view_form"
model="ir.ui.view">
<field name="model">estate.property</field>
<field name="arch" type="xml">
<form string="Property">
<sheet>

<group>
<h1>
<field name="name"/>
</h1>
</group>

<group>

<group>
<field name="property_type_id"/>
<field name="tag_ids" widget="many2many_tags"/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why do we use a widget?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

We use a widget when we want to change the default way a field is displayed or interacted with in the view. It allows us to provide a more suitable UI for that particular field.

<field name="postcode"/>
<field name="date_availability"/>
</group>

<group>
<field name="expected_price"/>
<field name="selling_price"/>
</group>

</group>

<notebook>
<page string="Description">
<group>
<field name="description"/>
<field name="bedrooms"/>
<field name="living_area"/>
<field name="facades"/>
<field name="garage"/>
<field name="garden"/>
<field name="garden_area" invisible="not garden"/>
<field name="garden_orientation" invisible="not garden"/>
<field name="total_area"/>
<field name="best_price"/>
<field name="active"/>
<field name="state"/>
</group>
</page>
<page string="Other Info">
<group>
<field name="buyer_id"/>
<field name="salesperson_id"/>
</group>
</page>
<page string="Offers">
<field name="offer_ids"/>
</page>
</notebook>

</sheet>
</form>
</field>
</record>

<record id="estate_property_view_search" model="ir.ui.view">
<field name="name">estate.property.search</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<search string="Properties">
<field name="name"/>
<field name="property_type_id"/>
<field name="postcode"/>
<field name="bedrooms"/>
<field name="expected_price"/>
<field name="living_area"/>
<field name="garden_area"/>
<field name="total_area"/>
<field name="facades"/>

<filter string="Available" name="date_availability" domain="[('state','in',('new','offer_received'))]"/>

<filter string="Postcode" name="postcode" context="{'group_by':'postcode'}"/>

</search>
</field>
</record>

<record
id="estate_property_action"
model="ir.actions.act_window">
<field name="name">Properties</field>
<field name="res_model">estate.property</field>
<field name="view_mode">list,form</field>
</record>

</odoo>