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 gato tutorials #634

Closed
wants to merge 16 commits into from
Closed
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,6 @@ dmypy.json

# Pyre type checker
.pyre/

#editor
*.swp
1 change: 1 addition & 0 deletions estateaccountinator/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
14 changes: 14 additions & 0 deletions estateaccountinator/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
'name': 'estate-accountinator',
'description': 'Inator that helps you do accounting on real estate',
'category': 'Tutorials/Accountinator',
'author': 'gato',
'depends': [
'realestatinator',
'account',
],
'installable': True,
'application': True,
'auto_install': False,
'version': '0.1',
}
1 change: 1 addition & 0 deletions estateaccountinator/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import estate_property
24 changes: 24 additions & 0 deletions estateaccountinator/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from odoo import api, Command, fields, models


class EstateProperty(models.Model):
_inherit = 'estate.property'

def mark_sold(self):
for record in self:
line_defs = [
{
'name': f'selling price (6% of {record.selling_price})',
'quantity': 1,
'price_unit': 0.06*record.selling_price
},
{
'name': 'administrative fees',
'quantity': 1,
'price_unit': 100
},
]
lines = [Command.create(line) for line in line_defs]
values = {'partner_id': record.buyer.id, 'move_type': 'out_invoice', 'line_ids': lines}
moves = self.env['account.move'].create(values)
return super().mark_sold()
1 change: 1 addition & 0 deletions realestatinator/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
22 changes: 22 additions & 0 deletions realestatinator/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
'name': 'real-estate-inator',
'description': 'Inator that helps you find real estate.',
'category': 'Tutorials/RealEstateInator',
'author': 'gato',
'depends': [
'base',
'web',
Comment on lines +7 to +8
Copy link

Choose a reason for hiding this comment

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

keep only one indentation

],
'data': [
'security/ir.model.access.csv',
'views/estate_property_views.xml',
Copy link

Choose a reason for hiding this comment

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

you can fixup all the commit "Chapter 5" inside the same commit. Check git documentation on interactive rebase / fixup if needed :)

'views/estate_property_type_views.xml',
'views/estate_property_tags_views.xml',
'views/estate_property_offer_views.xml',
'views/estate_menus.xml',
],
'installable': True,
'application': True,
'auto_install': False,
'version': '0.1',
}
6 changes: 6 additions & 0 deletions realestatinator/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import res_users
from . import res_partner
from . import estate_property
from . import estate_property_type
from . import estate_property_tags
from . import estate_property_offer
112 changes: 112 additions & 0 deletions realestatinator/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
from odoo import api, exceptions, fields, models

class EstatePropery(models.Model):
_name = 'estate.property'
_description = 'real estate property'
_order = 'id desc'
_sql_constraints = [
('check_expected_price_positive', 'CHECK (0 < expected_price)', 'Check that the expected price is strictly positive'),

]


name = fields.Char('Title', required=True)
active = fields.Boolean('Active', default=False)
bedrooms = fields.Integer('Bedrooms', default=2)
best_price = fields.Float('Best Offer', compute='_compute_best_price')
buyer = fields.Many2one('res.partner', string='Buyer', copy=False)
description = fields.Text('Description')
date_availability = fields.Date('Available Date', copy=False, default=fields.Date.add(fields.Date.today(), months=+3))
expected_price = fields.Float('Expected Price')
facades = fields.Integer('Facades')
garage = fields.Boolean('Garage')
garden = fields.Boolean('Garden')
garden_area = fields.Integer('Garden Area')
garden_orientation = fields.Selection(string='Garden Orientation', selection=[
('north', 'North'),
('south', 'South'),
('east', 'East'),
('west', 'West')
])
living_area = fields.Integer('Living Area')
offer_ids = fields.One2many('estate.property.offer', 'property_id', string='Offer')
postcode = fields.Char('Postcode')
property_type_id = fields.Many2one('estate.property.type', string='Property Type')
sales_person = fields.Many2one('res.users', string='Sales Person', default=lambda self: self.env.user)
selling_price = fields.Float('Selling Price', readonly=True, copy=False)
sequence = fields.Integer('Sequence', default=0)
state = fields.Selection(string='State', selection=[
('new', 'New'),
('offer_received', 'Offer Received'),
('offer_accepted', 'Offer Accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled')
], default='new')
tag_ids = fields.Many2many('estate.property.tags', string='Tags')
total_area = fields.Integer('Total Area', readonly=True, compute='_compute_total_area')

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

@api.depends('offer_ids.price')
def _compute_best_price(self):
for record in self:
prices = [0] + record.offer_ids.mapped('price')
record.best_price = max(prices)

@api.onchange('garden')
def _set_garden_properties(self):
for record in self:
# record.garden_orientation = 'N' if record.garden else ''
# record.garden_area = 10 if record.garden else 0

if record.garden:
if record.garden_orientation not in ['north', 'east', 'west', 'south']:
record.garden_orientation = 'north'
if record.garden_area == 0:
record.garden_area = 10
else:
record.garden_orientation = ''
record.garden_area = 0

def mark_cancelled(self):
for record in self:
if record.state == 'cancelled':
raise exceptions.UserError('This property is already cancelled.')

if record.state == 'sold':
raise exceptions.UserError('This property cannot be cancelled because it has already been sold.')

record.state = 'cancelled'
record.active = False

def mark_sold(self):

for record in self:
if record.state == 'sold':
raise exceptions.UserError('This property is already sold.')


if record.state == 'cancelled':
raise exceptions.UserError('This property cannot be sold because it has already been cancelled.')

record.state = 'sold'
record.active = False

@api.constrains('selling_price')
def _check_selling_price(self):
for record in self:
if record.state not in ['offer_accepted', 'sold']:
return
if record.selling_price < 0.9 * record.expected_price:
raise exceptions.ValidationError('Selling price must be at least 90% of expected price.')

@api.ondelete(at_uninstall=False)
def _unlink(self):
for record in self:
if record.state not in ['new', 'cancelled']:
raise exceptions.UserError('Property must be either new or cancelled to be deleted.')


57 changes: 57 additions & 0 deletions realestatinator/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from odoo import api, exceptions, fields, models

class EstatePropertyOffer(models.Model):
_name = 'estate.property.offer'
_description = 'estate property offer'
_order = 'price desc'
_sql_constraints = [
('check_offer_price_positive', 'CHECK (0 < price)', 'Check that the offer price is strictly positive.'),
]

creation_date = fields.Date('Creation Date', default=fields.Date.today())
date_deadline = fields.Date('Deadline', compute='_compute_deadline', inverse='_inverse_deadline')
partner_id = fields.Many2one('res.partner', string='Partner')
price = fields.Float('Price')
property_id = fields.Many2one('estate.property', string='Property')
property_type_id = fields.Many2one(related='property_id.property_type_id', store=True)
status = fields.Selection(string='Status', selection=[
('accepted', 'Accepted'),
('refused', 'Refused')
], copy=False)
validity = fields.Integer('Validity', default=7)


@api.depends('validity')
def _compute_deadline(self):
for record in self:
record.date_deadline = fields.Date.add(record.creation_date, days=record.validity)

def _inverse_deadline(self):
for record in self:
delta = record.date_deadline - record.creation_date
record.validity = delta.days

def refuse_offer(self):
for record in self:
if record.status == 'accepted':
record.property_id.state = 'offer_received'
record.property_id.selling_price = 0
record.property_id.buyer = None
record.status = 'refused'

def accept_offer(self):
for record in self:
if record.property_id.selling_price != 0:
raise exceptions.UserError('An offer as already been accepted for this property.')
record.property_id.state = 'offer_accepted'
record.status = 'accepted'
record.property_id.selling_price = record.price
record.property_id.buyer = record.partner_id
@api.model
def create(self, vals):
estate_property = self.env['estate.property'].browse(vals["property_id"])
if vals["price"] < estate_property.best_price:
raise exceptions.UserError(f'Offer must be higher than the current best offer({estate_property.best_price})')
if estate_property.state == 'new':
estate_property.state = 'offer_received'
return super().create(vals)
13 changes: 13 additions & 0 deletions realestatinator/models/estate_property_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from odoo import fields, models

class EstatePropertyTags(models.Model):
_name = 'estate.property.tags'
_description = 'estate property tag'
_order = 'name'
_sql_constraints = [
('name_unique', 'UNIQUE (name)', 'make sure tag name is unique.')
]


color = fields.Integer('Colour')
name = fields.Char('Name', required=True)
22 changes: 22 additions & 0 deletions realestatinator/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from odoo import api, fields, models

class EstatePropertyType(models.Model):
_name = 'estate.property.type'
_description = 'real estate property type'
_order = 'name, sequence'
_sql_constraints = [
('name_unique', 'UNIQUE (name)', 'make sure type name is unique.')
]

name = fields.Char('Name', required=True)
offer_count = fields.Integer(string='Offer Count', compute='_count_offers')
offer_ids = fields.One2many('estate.property.offer', 'property_type_id', string='Offers')
property_ids = fields.One2many('estate.property', 'property_type_id', string='Property Type')
sequence = fields.Integer('sequence', default=1)


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

8 changes: 8 additions & 0 deletions realestatinator/models/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from odoo import api, fields, models
from odoo.osv import expression


class Partner(models.Model):
_inherit = 'res.partner'


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


class Users(models.Model):
_inherit = 'res.users'

property_ids = fields.One2many('estate.property', 'sales_person', string='Properties')
5 changes: 5 additions & 0 deletions realestatinator/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
realestatinator.access_estate_property,access_estate_property,realestatinator.model_estate_property,base.group_user,1,1,1,1
realestatinator.access_estate_property_type,access_estate_property_type,realestatinator.model_estate_property_type,base.group_user,1,1,1,1
realestatinator.access_estate_property_tags,access_estate_property_tags,realestatinator.model_estate_property_tags,base.group_user,1,1,1,1
realestatinator.access_estate_property_offer,access_estate_property_offer,realestatinator.model_estate_property_offer,base.group_user,1,1,1,1
10 changes: 10 additions & 0 deletions realestatinator/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0"?>
<odoo>
<menuitem id="estate_menu_root" name="estate">
<menuitem id="estate_views_menu" name="Dashboard" action="estate_property_model_action"/>
<menuitem id="property_settings_menu" name="Settings">
<menuitem id="property_type_button" action="estate_property_type_action"/>
<menuitem id="property_tags_button" action="estate_property_tags_action"/>
</menuitem>
</menuitem>
</odoo>
27 changes: 27 additions & 0 deletions realestatinator/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<odoo>
<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>
<field name='view_mode'>list,form</field>
<field name='domain'>[('property_type_id', '=', active_id)]</field>
</record>
<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>
<field name="arch" type="xml">
<list string="Offers"
editable="bottom"
decoration-success="status == 'accepted'"
decoration-danger="status == 'refused'"
>
<field name="price"/>
<field name="partner_id"/>
<button name="accept_offer" type="object" icon="fa-check" invisible="status == 'accepted' or status == 'refused'"/>
<button name="refuse_offer" type="object" icon="fa-times" invisible="status == 'accepted' or status == 'refused'"/>
<field name="date_deadline"/>
<field name="validity"/>
</list>
</field>
</record>
</odoo>
8 changes: 8 additions & 0 deletions realestatinator/views/estate_property_tags_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<odoo>
<record id="estate_property_tags_action" model="ir.actions.act_window">
<field name='name'>estate.property.tags.action</field>
<field name='res_model'>estate.property.tags</field>
<field name='view_mode'>list,form</field>
</record>
</odoo>
Loading