Skip to content

18.0 tutorial module data aras #809

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

Open
wants to merge 23 commits into
base: 18.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5c40c23
[ADD] created estate module - ch2
abdrahmanrashed May 19, 2025
93035f1
[IMP] added estate property model and access csv - ch3 & ch4
abdrahmanrashed May 20, 2025
f6bc65e
[IMP] added estate views - ch5
abdrahmanrashed May 20, 2025
69db2b7
[IMP] created view layouts for list, form, and search - ch6
abdrahmanrashed May 20, 2025
1c513a2
[IMP] -Created the estate property type model with form and list view…
abdrahmanrashed May 21, 2025
52dd220
[IMP] - Created total area field in estate property model to present…
abdrahmanrashed May 21, 2025
9dcc21f
[IMP] -Created header buttons and actions to set the status(cancelled…
abdrahmanrashed May 22, 2025
8489f24
[IMP] -Created constraint in estate.property to ensure expected_price…
abdrahmanrashed May 22, 2025
82af63f
[IMP] -Added a list view in property types to view all houses with su…
abdrahmanrashed May 26, 2025
b8a3294
[IMP] -Allowed new offers to be created only if their pricing is high…
abdrahmanrashed May 27, 2025
1b12775
[ADD] -Added estate_account module to invoice sold properties.
abdrahmanrashed May 27, 2025
461b5d3
[IMP] -Added kanban view for estate properties.
abdrahmanrashed May 27, 2025
6c38871
[IMP] -Refactored code accorting to runbot linting guidelines.
abdrahmanrashed May 27, 2025
b219aad
[IMP] -added some estate property types that are available with estat…
abdrahmanrashed Jun 4, 2025
e00585c
[IMP] -added some demo estate model data for estate module.
abdrahmanrashed Jun 4, 2025
cc0c3ea
[IMP] -Added some demo estate offer model data for estate module.
abdrahmanrashed Jun 4, 2025
b3f8277
[IMP] -Set demo estate offers dates to be relative to module creation…
abdrahmanrashed Jun 4, 2025
05d3a45
[IMP] -Set demo estate properties to be residential type.
abdrahmanrashed Jun 4, 2025
a96dd13
[IMP] -Set an offer to be accepted by default, and the rest to be ref…
abdrahmanrashed Jun 4, 2025
7958f69
[IMP] -Created demo estate property with offers inside.
abdrahmanrashed Jun 4, 2025
94d0478
[ADD] -Added check to raise an error if an offer is created for a sol…
abdrahmanrashed Jun 10, 2025
996ee6d
[IMP] -Added tests to check garden values are set/reset correctly onc…
abdrahmanrashed Jun 10, 2025
c04331e
[IMP] -refactoring based of PR feedback.
abdrahmanrashed Jun 11, 2025
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
Binary file added estate.zip
Binary file not shown.
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
24 changes: 24 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
'name': 'ESTATE',
'description': "aras estate tutorial module",
'website': 'https://www.odoo.com/page/estate',
'depends': [
'base',
],
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_property_tag_views.xml',
'views/estate_property_offer_views.xml',
'views/estate_property_type_views.xml',
'views/estate_menus.xml',
'views/res_users_views.xml',
],
'demo': [
'demo/estate.property.type.csv',
'demo/estate_property.xml',
'demo/estate_property_offer.xml'
],
'installable': True,
'application': True,
}
5 changes: 5 additions & 0 deletions estate/demo/estate.property.type.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"id","name"
"data_property_type_residential","Residential"
"data_property_type_commerial","Commerial"
"data_property_type_industrial","Industrial"
"data_property_type_land","Land"
57 changes: 57 additions & 0 deletions estate/demo/estate_property.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<odoo>
<record id="estate_property_1" model="estate.property">
<field name="name">Big Villa</field>
<field name="description">A nice and big villa</field>
<field name="postcode">12345</field>
<field name="date_availability" eval="datetime.now() + timedelta(days=14)" />
<field name="expected_price">1600000</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="data_property_type_residential"/>
</record>

<record id="estate_property_2" model="estate.property">
<field name="name">Trailer home</field>
<field name="state">cancelled</field>
<field name="description">Home in a trailer park</field>
<field name="postcode">54321</field>
<field name="date_availability" eval="datetime.now() + timedelta(days=14)" />
<field name="expected_price">100000</field>
<field name="bedrooms">1</field>
<field name="living_area">10</field>
<field name="facades">4</field>
<field name="property_type_id" ref="data_property_type_residential"/>
</record>

<record id="estate_property_3" model="estate.property">
<field name="name">Tutorial House</field>
<field name="state">offer_received</field>
<field name="description">X2many tutorial</field>
<field name="postcode">13579</field>
<field name="date_availability" eval="datetime.now() + timedelta(days=14)" />
<field name="expected_price">123456</field>
<field name="bedrooms">2</field>
<field name="living_area">30</field>
<field name="facades">4</field>
<field name="property_type_id" ref="data_property_type_residential"/>
<field name="offer_ids" eval="[
Command.clear(),
Command.create({
'partner_id': ref('base.res_partner_12'),
'price': 150000,
'validity': 14,
}),
Command.create({
'partner_id': ref('base.res_partner_2'),
'price': 150002,
'validity': 14,
}),
]"/>
</record>
</odoo>

29 changes: 29 additions & 0 deletions estate/demo/estate_property_offer.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<odoo>
<record id="estate_property_offer_1" model="estate.property.offer">
<field name="partner_id" ref="base.res_partner_12"/>
<field name="property_id" ref="estate_property_1"/>
<field name="price">10000</field>
<field name="validity">14</field>
<field name="create_date" eval="datetime.now()"/>
</record>

<record id="estate_property_offer_2" model="estate.property.offer">
<field name="partner_id" ref="base.res_partner_12"/>
<field name="property_id" ref="estate_property_1"/>
<field name="price">1500000</field>
<field name="validity">14</field>
<field name="create_date" eval="datetime.now()"/>
</record>

<record id="estate_property_offer_3" model="estate.property.offer">
<field name="partner_id" ref="base.res_partner_2"/>
<field name="property_id" ref="estate_property_1"/>
<field name="price">1500001</field>
<field name="validity">14</field>
<field name="create_date" eval="datetime.now()"/>
</record>

<function model="estate.property.offer" name="action_offer_refuse" eval="[ref('estate_property_offer_1')]"/>
<function model="estate.property.offer" name="action_offer_refuse" eval="[ref('estate_property_offer_2')]"/>
<function model="estate.property.offer" name="action_offer_accept" eval="[ref('estate_property_offer_3')]"/>
</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_tag
from . import estate_property_offer
from . import res_user
99 changes: 99 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from odoo import api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_compare


class EstateProperty(models.Model):
_name = "estate.property"
_description = "ch3 exercise tutorial"
_order = "id desc"

name = fields.Char(required=True)
# active = fields.Boolean(default=False)
state = fields.Selection(
string='Status',
selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('cancelled', 'Cancelled')],
default='new'
)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(default=lambda _: fields.Date.add(fields.Date.today(), months=3), copy=False)
expected_price = fields.Float()
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(
string='Garden Orientation',
selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')]
)
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
buyer = fields.Many2one("res.partner", string="Buyer", copy=False)
sales_person = fields.Many2one("res.users", string="Sales Person", default=lambda self: self.env.user)
property_tag_ids = fields.Many2many("estate.property.tag")
offer_ids = fields.One2many("estate.property.offer", "property_id")
total_area = fields.Integer(compute="_compute_total_area")
best_price = fields.Float(compute="_compute_best_price", string="Best Offer")

_sql_constraints = [
('check_expected_price', 'CHECK(expected_price > 0)',
'The Expected price of an estate 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:
prices = record.offer_ids.mapped('price')
record.best_price = max(prices) if prices else 0
if prices and record.state in [None, 'new']:
record.state = 'offer_received'

@api.onchange("garden")
def _onchange_garden(self):
for record in self:
if record.garden:
record.garden_area = 10
record.garden_orientation = 'north'
else:
record.garden_area = 0
record.garden_orientation = False

def action_property_sold(self):
for record in self:
if record.state == 'cancelled':
raise UserError("Cancelled properties cannot be sold.")
elif record.state != 'offer_accepted':
raise UserError("You cannot sell a property without an accepted offer.")
else:
record.state = 'sold'
return True

def action_property_cancelled(self):
for record in self:
if record.state == 'sold':
raise UserError("Sold properties cannot be cancelled.")
else:
record.state = 'cancelled'
return True

@api.constrains('selling_price')
def _check_selling_price(self):
for record in self:
if record.selling_price is not None and float_compare(record.selling_price, 0.9 * record.expected_price, precision_digits=9) == -1:
raise ValidationError("The selling price cannot be less than 90% of the expected price")

@api.ondelete(at_uninstall=False)
def unlink_if_new_or_cancelled(self):
for record in self:
if record.state not in ('new', 'cancelled'):
raise UserError("Only new and cancelled properties can be deleted.")
if record.offer_ids:
record.offer_ids.unlink()
66 changes: 66 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from odoo import api, fields, models
from odoo.exceptions import UserError


class EstatePropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "ch7 exercise tutorial"
_order = "price desc"

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

_sql_constraints = [
('check_price', 'CHECK(price > 0)',
'The Price offered for an estate should be positive.'),
]

@api.depends("create_date", "validity")
def _compute_date_deadline(self):
for record in self:
record.date_deadline = fields.Date.add(record.create_date, days=record.validity)

def _inverse_date_deadline(self):
for record in self:
record.validity = (record.date_deadline - record.create_date).days

def action_offer_accept(self):
for record in self:
if record.status == 'accepted':
raise UserError("A offer has already been accepted.")
record.status = 'accepted'
for id in record.property_id:
id.selling_price = record.price
id.buyer = record.partner_id
id.state = 'offer_accepted'
return True

def action_offer_refuse(self):
for record in self:
if record.status == 'accepted':
raise UserError("You cannot refuse an accepted offer.")
record.status = 'refused'
return True

@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
property = self.env['estate.property'].browse(vals.get('property_id'))
if property.state in ('sold', 'cancelled'):
raise UserError(f"Cannot create an offer in a {property.state} property.")
price = vals.get('price')
estate_property = self.env['estate.property'].browse(vals.get('property_id'))
if estate_property.best_price and price <= estate_property.best_price:
raise UserError(f"The selling must be higher than {estate_property.best_price:.2f}")
estate_property.best_price = price
return super().create(vals_list)
15 changes: 15 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from odoo import fields, models


class EstatePropertyTag(models.Model):
_name = "estate.property.tag"
_description = "ch7 exercise tutorial"
_order = "name"

name = fields.Char(required=True)
color = fields.Integer()

_sql_constraints = [
('unique_property_tag', 'UNIQUE(name)',
'Each property tag should have a unique name.')
]
22 changes: 22 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from odoo import fields, models


class EstatePropertyType(models.Model):
_name = "estate.property.type"
_description = "ch7 exercise tutorial"
_order = "sequence, name"

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

_sql_constraints = [
('unique_property_type', 'UNIQUE(name)',
'Each property type should have a unique name.')
]

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


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

property_ids = fields.One2many(
comodel_name="estate.property",
inverse_name="sales_person",
domain="""[
('state', 'in', ('new', 'offer_received'))
]"""
)
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_estate_property
Loading