Skip to content
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

18.0 training csan #242

Open
wants to merge 9 commits into
base: 18.0
Choose a base branch
from
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.languageServer": "None"
}
2 changes: 2 additions & 0 deletions estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import security
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
14 changes: 14 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
'name' : "Real Estate",
'depends' : ['base'],
'application': True,
'data' : [
'security/ir.model.access.csv',
'views/estate_property_offer.xml',
'views/estate_property_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag.xml',
'views/estate_property_users_views.xml',
'views/estate_menus.xml',
]
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
}
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
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_users
101 changes: 101 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from odoo import api, fields, models
from datetime import date
from dateutil.relativedelta import relativedelta
from odoo.exceptions import UserError

class EstateProperty(models.Model):
_name = "estate_property"
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
_description = "estate property description"
_order = "id desc"

name = fields.Char('Estate Name', required=True)

property_type_id = fields.Many2one('estate_property_type', string='Property type')
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
partner_id = fields.Many2one('res.partner', string='Buyer')
users_id = fields.Many2one('res.users', string='Salesman', default=lambda self: self.env.user)
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved

tags_ids = fields.Many2many('estate_property_tag', string='Tags')
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved


offers_ids = fields.One2many('estate_property_offer', 'property_id', string='Offers')
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved

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

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

best_price = fields.Integer(compute="_compute_best_price")

@api.depends("offers_ids.price")
def _compute_best_price(self):
for record in self:
best_price = 0

for offer in record.offers_ids:
best_price = max(best_price, offer.price)

record.best_price = best_price


active = fields.Boolean('Active', default=True)
description = fields.Text('Description')
postcode = fields.Char('Postcode')
date_availability = fields.Date('Availability', copy=False, default=fields.Date.today() + relativedelta(month=4))
expected_price = fields.Float('Expected Price', required=True)
selling_price = fields.Float('Selling Price', readonly=True, copy=False)
bedrooms = fields.Integer('Number of Bedrooms', default=2)
living_area = fields.Integer('Living Area')
facades = fields.Integer('Number of Facades')
garage = fields.Boolean('Garage')
garden = fields.Boolean('Garden')
garden_area = fields.Integer('Garden Area')
garden_orientation = fields.Selection(
selection=[
('north', 'North'),
('south', 'South'),
('east', 'East'),
('west', 'West')])

state = fields.Selection(
selection=[
('new', 'New'),
('offer received', 'Offer Received'),
('offer accepted', 'Offer Accepted'),
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
('sold', 'Sold'),
('cancelled', 'Cancelled')], default="new", required=True, copy=False)

@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

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

def cancel_button_action(self):
for record in self:
if record.state == "sold":
raise UserError('Sold property cannot be cancelled')
record.state = "cancelled"

@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(f"You cannot delete the property {record.name} unless its state is 'New' or 'Cancelled'.'")

_sql_constraints = [
('check_positive_price', 'CHECK(expected_price >= 0)',
'The expected price of a real estate should always be positive'),
('check_positive_selling_price', 'CHECK(selling_price >= 0)',
'The selling price of a real estate should always be positive')
]
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
88 changes: 88 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from odoo import api, fields, models
from datetime import timedelta
from odoo.exceptions import UserError, ValidationError

class OfferModel(models.Model):
_name = "estate_property_offer"
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
_description = "estate property offer"
_order = "price desc"

price = fields.Float('Price')
status = fields.Selection(
selection=[
('accepted', 'Accepted'),
('refused', 'Refused'),
('pending', 'Pending')
]
)
partner_id = fields.Many2one('res.partner', string='Offeror')
property_id = fields.Many2one('estate_property', string='property')

validity = fields.Integer('Validity (days)', default=7)
date_deadline = fields.Date('Date_deadline', compute="_compute_deadline", inverse="_inverse_deadline")

property_type_id = fields.Many2one(related='property_id.property_type_id', store=True)

@api.depends("validity")
def _compute_deadline(self):
for record in self:
if record.validity:
record.date_deadline = fields.Date.today() + timedelta(days=record.validity)

def _inverse_deadline(self):
for record in self:
if record.date_deadline:
date_deadline = record.date_deadline
today = fields.Date.today()
record.validity = (date_deadline - today).days

def action_accept_offer(self):
for record in self:
if record.property_id.state == "offer accepted":
raise UserError('An Offer has already been accepted for this property')
record.property_id.state = "offer accepted"
record.property_id.partner_id = record.partner_id
if record.price < record.property_id.expected_price * 0.9:
raise ValidationError("The price cannot be lower than 90% of the expcted price")
record.property_id.selling_price = record.price

record.status = 'accepted'
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved

def action_refuse_offer(self):
for record in self:
record.property_id.partner_id = False
record.property_id.selling_price = 0.0

record.status = 'refused'

@api.model
def create(self, vals):
property_id = vals.get('property_id')

property_record = self.env['estate_property'].browse(property_id)
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved

if property_record:
property_record.state = 'offer received'
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved

new_offer_price = None

if 'price' in vals:
new_offer_price = vals['price']

if new_offer_price is not None:
existing_offers = self.env['estate_property_offer'].search([
('property_id', '=', property_id),
('price', '>=', new_offer_price)
])

if existing_offers:
raise UserError(f"The new offer price ({new_offer_price}) is lower than an existing offer.")

return super().create(vals)



_sql_constraints = [
('check_positive_offer', 'CHECK(price >= 0)',
'The offer for a real estate should always be positive')
]
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 TagModel(models.Model):
_name = "estate_property_tag"
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
_description = "estate property tag"
_order = "name"

name = fields.Char('Property Tags', required=True)
color = fields.Integer("Color", default=0)

_sql_constraints = [
('check_tag_uniqueness', 'UNIQUE(name)',
'The new tag should be unique')
]

csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
26 changes: 26 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from odoo import fields, models, api

class EstatePropertyType(models.Model):
_name = "estate_property_type"
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
_description = "estate property type"
_order = "sequence, name"

name = fields.Char('Property Types', required=True)
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved

property_ids = fields.One2many('estate_property', 'property_type_id', string='Properties')
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
sequence = fields.Integer('Sequence', default=1, help="Used to order types. Lower is better.")

offer_ids = fields.One2many('estate_property_offer', 'property_type_id', string='offers')

offer_count = fields.Integer(string='Offer Count', compute='_compute_offer_count', store=True)

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


_sql_constraints = [
('check_type_uniqueness', 'UNIQUE(name)',
'The new property type should be unique')
]
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions estate/models/res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from odoo import fields, models

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

property_ids = fields.One2many('estate_property', 'users_id', string="Properties", domain=[('state', '=', 'available')])
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
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
estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,1,1,1
estate.access_estate_property_type,access_estate_property_type,estate.model_estate_property_type,base.group_user,1,1,1,1
estate.access_estate_property_tag,access_estate_property_tag,estate.model_estate_property_tag,base.group_user,1,1,1,1
estate.access_estate_property_offer,access_estate_property_offer,estate.model_estate_property_offer,base.group_user,1,1,1,1
14 changes: 14 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>

<odoo>
<menuitem id="test_menu_root" name="Real Estate">
<menuitem id="estate_first_level_menu" name="Advertisements">
<menuitem id="estate_property_menu_action" action="estate_property_action"/>
</menuitem>
<menuitem id="estate_type_first_level_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>
53 changes: 53 additions & 0 deletions estate/views/estate_property_offer.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0"?>

<odoo>
<data>
<record id="estate_property_offer_view_list" model="ir.ui.view">
<field name="name">estate.property.offer.view.list</field>
<field name="model">estate_property_offer</field>
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
<field name="arch" type="xml">
<list string="Channel" editable="bottom">
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
<field name="price" width="200px"/>
<field name="partner_id" width="200px"/>
<button name="action_accept_offer" string="Accept Offer" type="object" icon="fa-thumbs-up" class="btn-primary" invisible="status == 'accepted' or status == 'refused'"/>
<button name="action_refuse_offer" string="Refuse Offer" type="object" icon="fa-times" class="btn-danger" invisible="status == 'refused' or status == 'accepted'"/>
<field name="validity"/>
<field name="date_deadline"/>
<field name="status"/>
<field name="property_type_id"/>
</list>
</field>
</record>

<record id="estate_property_offer_view_form" model="ir.ui.view">
<field name="name">estate.property.offer.view.form</field>
<field name="model">estate_property_offer</field>
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
<field name="arch" type="xml">
<form string="Test">
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
<sheet>
<group>
<field name="price"/>
<field name="partner_id"/>
<button name="action_accept_offer" string="Accept Offer" type="object" icon="fa-thumbs-up" class="btn-primary" invisible="status == 'accepted' or status == 'refused'"/>
<button name="action_refuse_offer" string="Refuse Offer" type="object" icon="fa-times" class="btn-danger" invisible="status == 'refused' or status == 'accepted'"/>
<field name="validity"/>
<field name="date_deadline"/>
<field name="status"/>

</group>

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

<record id="estate_property_offer_action" model="ir.actions.act_window">
<field name="name">Estate Property Offer Action</field>
<field name="res_model">estate_property_offer</field>
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
<field name="view_mode">list,form</field>
<field name="domain">[('property_type_id', '=', active_id)]</field>
<field name="context">{'search_default_property_type_id': active_id}</field>
</record>

</data>
</odoo>
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
19 changes: 19 additions & 0 deletions estate/views/estate_property_tag.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0"?>

<odoo>
<record id="estate_property_tag_action" model="ir.actions.act_window">
<field name="name"> Estate property tag action</field>
<field name="res_model">estate_property_tag</field>
<field name="view_mode">list,form</field>
</record>

<record id="estate_property_tag_view_list" model="ir.ui.view">
<field name="name">estate.property.tag.view.list</field>
<field name="model">estate_property_tag</field>
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
<field name="arch" type="xml">
<list string="Channel" editable="bottom">
csan-odoo marked this conversation as resolved.
Show resolved Hide resolved
<field name="name" width="200px"/>
</list>
</field>
</record>
</odoo>
Loading