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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ downloads/
eggs/
.eggs/
lib/
.idea/
lib64/
parts/
sdist/
Expand Down
1 change: 1 addition & 0 deletions real_estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
22 changes: 22 additions & 0 deletions real_estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
'name': "real.estate",
'summary': "summary of the real estate",
'description': """Rugot first module""",
'author': "Ruchita Gothi (Rugot)",
'category': 'Real Estate',
'version': '0.1',
'depends': ['base', 'web'],
'data': [
'security/ir.model.access.csv',
'views/real_estate_property_offer_views.xml',
'views/real_estate_property_views.xml',
'views/real_estate_property_type_views.xml',
'views/real_estate_tag_views.xml',
'views/real_estate_property_maintenance_request_views.xml',
'views/res_users_views.xml',
'views/real_estate_menus.xml',
],
'application': True,
'installable': True,
'license': 'LGPL-3',
}
6 changes: 6 additions & 0 deletions real_estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import real_estate_property
from . import real_estate_tag
from . import real_estate_property_offer
from . import real_estate_property_type
from . import real_estate_property_maintenance_request
from . import res_users
150 changes: 150 additions & 0 deletions real_estate/models/real_estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
from datetime import timedelta

from odoo import models, fields, api
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_is_zero, float_compare


class real_estate(models.Model):
_name = 'real.estate'
_description = 'Real Estate Property'
_order = "id desc"

name = fields.Char(required=True)
property_type_id = fields.Many2one(
"real.estate.property.type", string="Property Type")
street_address = fields.Char()
description = fields.Text()
postcode = fields.Integer()
date_availability = fields.Datetime(default=lambda self: fields.Datetime.now() + timedelta(days=90))
expected_price = fields.Float()
bedrooms = fields.Integer(default=2)
living_area = fields.Integer()
bathrooms = 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)
tag_ids = fields.Many2many(
"real.estate.tag", string="Tags", ondelete='cascade')
offer_ids = fields.One2many(
"real.estate.property.offer", "property_id", string="Offers")
total_area = fields.Float(compute="_compute_total", store=True)
best_price = fields.Float(
string="Best Offer",
compute="_compute_best_price",
store=True)
# ist_time = fields.Char(
# string="Created On (IST)",
# compute="_compute_create_date_ist",
# store=True)
stage = fields.Selection([
('new', 'New'),
('offer_received', 'Offer Received'),
('offer_accepted', 'Offer Accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled'),
], default='new')
buyer_id = fields.Many2one(
'res.partner',
string='Buyer',
copy=False)
selling_price = fields.Float()
maintenance_request_ids = fields.One2many(
"real.estate.property.maintenance.request", "property_id", string="Maintenance Requests")
total_maintenance_cost = fields.Float(compute="_compute_total_maintenance_cost", store=True, string="Total Cost")
salesperson_id = fields.Many2one(
'res.users',
string='Salesperson'
)
_check_expected_price_positive = models.Constraint(
'CHECK(expected_price > 0)',
'The expected price must be strictly positive.',
)

@api.constrains('expected_price', 'selling_price')
def _check_selling_price(self):
for rec in self:
if float_is_zero(rec.selling_price, precision_rounding=0.01):
continue
if float_compare(
rec.selling_price,
rec.expected_price * 0.9,
precision_rounding=0.01) < 0:
raise ValidationError(
'The selling price cannot be lower than 90% of the expected price.')

@api.depends('offer_ids.price')
def _compute_best_price(self):
for record in self:
prices = []
for offer in record.offer_ids:
prices.append(offer.price)
record.best_price = max(prices) if prices else 0.0

@api.depends('maintenance_request_ids.cost')
def _compute_total_maintenance_cost(self):
for record in self:
costs = record.maintenance_request_ids.mapped('cost')
record.total_maintenance_cost = sum(costs) if costs else 0.0

@api.depends('living_area', 'garden_area')
def _compute_total(self):
for record in self:
record.total_area = (record.living_area or 0) + (record.garden_area or 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 = False

@api.ondelete(at_uninstall=False)
def _check_property_delete(self):
for record in self:
if record.stage not in ('new', 'cancelled'):
raise UserError(
"You can only delete properties in New or Cancelled state."
)

# @api.depends('create_date')
# def _compute_create_date_ist(self):
# for rec in self:
# if rec.create_date:
# ist_dt = fields.Datetime.context_timestamp(
# rec, rec.create_date
# )
# rec.ist_time = ist_dt.strftime("%Y-%m-%d %H:%M:%S")
# else:
# rec.ist_time = False

def action_cancel(self):
if self.stage == 'sold':
raise UserError("A sold property cannot be cancelled.")
self.stage = 'cancelled'

def action_sold(self):
if self.stage == 'cancelled':
raise UserError("A cancelled property cannot be sold.")
maintenace_request = self.maintenance_request_ids.filtered_domain([('status', '!=', 'done')])
if maintenace_request:
raise UserError("CProperty cannot be sold , there is any maintenance request not done")
self.stage = 'sold'

@api.constrains('expected_price')
def _check_expected_price(self):
for rec in self:
if rec.expected_price < 0:
raise ValidationError(
'The selling price cannot be lower than 90% of the expected price.'
)
28 changes: 28 additions & 0 deletions real_estate/models/real_estate_property_maintenance_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from odoo.exceptions import UserError

from odoo import fields, models, api


class real_estate_properties_maintenance_request(models.Model):
_name = 'real.estate.property.maintenance.request'
_description = 'Real Estate Property Maintenance Request'

name = fields.Char()
cost = fields.Integer()
status = fields.Selection([
('new', 'New'),
('approved', 'Approved'),
('done', 'Done'),
], string="Status", copy=False, default='new')
property_id = fields.Many2one('real.estate', string='Property', ondelete='restrict')

@api.onchange('status')
def _check_cost_on_accepted_status(self):
if self.status == 'approved' and self.cost <= 0:
raise UserError("Approved cost must be greater than 0")

@api.ondelete(at_uninstall=False)
def _unlink_if_maintenance_request_not_done(self):
maintenace_request = self.filtered_domain([('status', '!=', 'done')])
if maintenace_request:
raise UserError("Can't delete an active Maintenance Request Record!")
149 changes: 149 additions & 0 deletions real_estate/models/real_estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
from datetime import timedelta
# from collections import defaultdict

from odoo import models, fields, api
from odoo.exceptions import UserError
from odoo.tools import float_compare


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

price = fields.Float(required=True)
property_id = fields.Many2one('real.estate', string='Property', ondelete='restrict')
property_type_id = fields.Many2one(
related="property_id.property_type_id",
store=True
)
status = fields.Selection([
('accepted', 'Accepted'),
('refused', 'Refused'),
], string="Status", copy=False)
partner_id = fields.Many2one('res.partner', string="Buyer", required=True)
validity = fields.Integer(default=7)
date_deadline = fields.Date(
string="Deadline",
compute="_compute_date_deadline",
inverse="_inverse_date_deadline",
store=True
)
_check_offer_price_positive = models.Constraint(
'CHECK(price > 0)',
'The offer price must be strictly positive.',
)

# @api.model
# def create(self, vals):
# grouped = defaultdict(list)
# for val in vals:
# property_id = val.get('property_id')
# price = val.get('price')
# if property_id and price:
# grouped[property_id].append(price)
#
# for property_id, new_prices in grouped.items():
# property_rec = self.env['real.estate'].browse(property_id)
# existing_prices = property_rec.best_price
# new_max = max(new_prices)
# max_price = max(existing_prices, new_max)
# for price in new_prices:
# if price < max_price:
# raise UserError(
# "Only the highest offer is allowed"
# )
# offers = super().create(vals)
# for offer in offers:
# if offer.property_id and offer.property_id.stage == 'new':
# offer.property_id.stage = 'offer_received'
# return offers

@api.model
def create(self, vals):
for val in vals:
price = val.get('price')
property_id = val.get('property_id')
property = self.env['real.estate'].browse(property_id)
if property.stage == "new":
property.best_price = price
elif float_compare(price, property.best_price, precision_rounding=0.01) < 0:
raise UserError(
f"Price should be greater than {property.best_price}")
else:
property.best_price = price
if property and property.stage == 'new':
property.stage = 'offer_received'

return super().create(vals)

# @api.model
# def create(self, vals):
# for property_id, n
# for val in vals:
# property_id = val.get('property_id')
# price = val.get('price')
# if property_id and price:
# property_rec = self.env['real.estate'].browse(property_id)
# if property_rec.offer_ids:
# # max_offer = max(property_rec.offer_ids.mapped('price'))
# if price < property_rec.best_price:
# raise UserError(
# "The offer must be higher than existing offers."
# )
# offer = super().create(vals)
# if offer.property_id and offer.property_id.stage == 'new':
# offer.property_id.stage = 'offer_received'
# return offer

@api.depends('create_date', 'validity')
def _compute_date_deadline(self):
for offer in self:
if offer.create_date:
offer.date_deadline = offer.create_date.date() + timedelta(days=offer.validity)
else:
offer.date_deadline = fields.Date.today() + timedelta(days=offer.validity)

def _inverse_date_deadline(self):
for offer in self:
if offer.create_date and offer.date_deadline:
offer.validity = (offer.date_deadline - offer.create_date.date()).days

@api.ondelete(at_uninstall=False)
def _unlink_if_accepted_offer(self):
accepted_offer = self.filtered_domain([('status', '=', 'accepted')])
if accepted_offer:
raise UserError("Can't delete an active record!")

def action_accept(self):
# accepted_offer = self.search([
# ('property_id', '=', self.property_id.id),
# ('status', '=', 'accepted')
# ], limit=1)
# accepted_offer = self.property_id.offer_ids.filtered(
# lambda o: o.status == 'accepted'
# )
accepted_offer = self.property_id.offer_ids.filtered_domain([
('status', '=', 'accepted')
])
maintenace_request = self.property_id.maintenance_request_ids.filtered_domain([('status', '!=', 'done')])
if accepted_offer:
raise UserError(
"Only one offer can be accepted for a property.")
if maintenace_request:
raise UserError("Property cannot be sold , there is any maintenance request not done")
self.status = 'accepted'
self.property_id.write({
'selling_price': self.price,
'stage': 'offer_accepted',
'buyer_id': self.partner_id.id,
})
refused_offer = self.property_id.offer_ids.filtered_domain([
('id', '!=', 'self.id'),
('status', '!=', 'accepted')
])
for refuse in refused_offer:
refuse.status = 'refused'

def action_refuse(self):
self.status = 'refused'
Loading