Skip to content

Tutorials Amer #780

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 49 commits into
base: 18.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
f5df5c5
[ADD] estate: finished chapter 3
arthurrrm May 19, 2025
59e83b2
[ADD] chapter 3
arthurrrm May 19, 2025
439c30d
[ADD] chapter 4
arthurrrm May 19, 2025
ef2e205
[ADD] chapter 5 first half
arthurrrm May 19, 2025
3edc21b
[ADD] chapter 5 done
arthurrrm May 19, 2025
e6ce348
[ADD] finished chapter 6
arthurrrm May 19, 2025
ca315f5
[FIX] chapter 6 fix
arthurrrm May 20, 2025
a46be7f
[ADD] chapter 7 done
arthurrrm May 20, 2025
6d7858d
[ADD] chapter 7 done
arthurrrm May 20, 2025
86c8ab9
[FIX] chapter 7
arthurrrm May 20, 2025
27825b8
[ADD] estate: chapter 8 done + lint
arthurrrm May 20, 2025
7e69fef
[ADD] gitignore updated
arthurrrm May 20, 2025
3bb6ca4
[ADD] estate: chapter 9 done
arthurrrm May 20, 2025
5821075
[ADD] estate: chapter 9 done
arthurrrm May 20, 2025
d6558e2
[ADD] estate: chapter 10 done
arthurrrm May 21, 2025
f36eeea
[ADD] estate: chapter 11 inline view + widgets
arthurrrm May 21, 2025
c6be45b
[ADD] estate: chapter 11 += List Order
arthurrrm May 21, 2025
e590994
[ADD] estate: chapter 11 += Attributes and options
arthurrrm May 21, 2025
2aae01f
[ADD] estate: chapter 11 done
arthurrrm May 21, 2025
9255a03
[ADD] estate: chapter 12 += Python Inheritance
arthurrrm May 22, 2025
d35f9ba
[ADD] estate: chapter 12 done
arthurrrm May 22, 2025
a3f718a
[FIX] estate: chapter 12 done
arthurrrm May 22, 2025
ab5f481
[FIX] estate: chapter 12 actual final fix v2 final LAST fix
arthurrrm May 22, 2025
0b88c32
[FIX] estate: chapter 13 done + chapter 12 fix v4 (the last one)
arthurrrm May 22, 2025
84710d9
[FIX] estate: chapter 14 + 15 done
arthurrrm May 22, 2025
0356b6b
[REV] revised code on mathieu's comments
arthurrrm May 23, 2025
e9b6410
[REV] revised code on mathieu's comments
arthurrrm May 23, 2025
6c3ab74
[REV] revised code on mathieu's comments
arthurrrm May 23, 2025
546e977
[ADD] awesome_owl: 1.2 extract the counter in a sub component
arthurrrm May 23, 2025
c98f367
[ADD] awesome_owl: 1.3 add a simple card component
arthurrrm May 23, 2025
40c68a9
[ADD] awesome_owl: 1.4 markup to display html
arthurrrm May 23, 2025
4a2ce33
[ADD] awesome_owl: 1.5 + 1.6 props validation and sum oftwo Counter
arthurrrm May 23, 2025
c3713d8
[ADD] awesome_owl: 1.7 little todolist
arthurrrm May 26, 2025
933b277
[ADD] awesome_owl: 1.8 +1.9 dynamic attributes & user can add todos
arthurrrm May 26, 2025
588ea02
[ADD] awesome_owl: 1.10 auto focus on input
arthurrrm May 26, 2025
7b3aeb8
[ADD] awesome_owl: 1.11 toggling todos
arthurrrm May 26, 2025
2fa0f72
[ADD] awesome_owl: 1.12 deleting todos
arthurrrm May 26, 2025
8e65463
[ADD] awesome_owl: 1.13 generic card with slots
arthurrrm May 27, 2025
63d1d12
[ADD] awesome_owl: 1.14 minimizing card content
arthurrrm May 27, 2025
e078e49
[ADD] awesome_dashboard 2.1 new layout
arthurrrm May 27, 2025
9e76c58
[ADD] awesome_dashboard 2.2 Open leads and CRM view
arthurrrm May 27, 2025
029dc4d
[ADD] awesome_dashboard 2.3 add dashboard item
arthurrrm May 28, 2025
36d82b4
[ADD] awesome_dashboard 2.4 add some statistics
arthurrrm May 28, 2025
2c9c67c
[ADD] awesome_dashboard 2.5 cache network calls
arthurrrm May 28, 2025
14d0736
[ADD] awesome_dashboard 2.6 pie charts
arthurrrm May 28, 2025
aedd799
[ADD] estate : Add some standart Real estate property (tuto Define mo…
arthurrrm May 30, 2025
c3f2718
[ADD] estate : demo data for estate. property
arthurrrm May 30, 2025
caf2b1a
[ADD] estate : demo data for property offers
arthurrrm May 30, 2025
e4115af
[ADD] estate : add new demo property with offers directly defined in…
arthurrrm May 30, 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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,8 @@ dmypy.json

# Pyre type checker
.pyre/


#pre commit
.pre-commit-config.yaml
ruff.toml
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.3 # Use the latest Ruff version or the one that matches your setup
hooks:
- id: ruff
args: [ --fix]
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"
}
1 change: 0 additions & 1 deletion awesome_clicker/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
# -*- coding: utf-8 -*-
1 change: 0 additions & 1 deletion awesome_clicker/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': "Awesome Clicker",

Expand Down
1 change: 0 additions & 1 deletion awesome_dashboard/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-

from . import controllers
1 change: 0 additions & 1 deletion awesome_dashboard/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': "Awesome Dashboard",

Expand Down
3 changes: 1 addition & 2 deletions awesome_dashboard/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-

from . import controllers
from . import controllers
1 change: 0 additions & 1 deletion awesome_dashboard/controllers/controllers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-

import logging
import random
Expand Down
1 change: 0 additions & 1 deletion awesome_gallery/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
# -*- coding: utf-8 -*-
from . import models
1 change: 0 additions & 1 deletion awesome_gallery/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': "Gallery View",
'summary': """
Expand Down
1 change: 0 additions & 1 deletion awesome_gallery/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# import filename_python_file_within_folder_or_subfolder
from . import ir_action
from . import ir_ui_view
1 change: 0 additions & 1 deletion awesome_gallery/models/ir_action.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from odoo import fields, models


Expand Down
1 change: 0 additions & 1 deletion awesome_gallery/models/ir_ui_view.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from odoo import fields, models


Expand Down
1 change: 0 additions & 1 deletion awesome_kanban/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
# -*- coding: utf-8 -*-
1 change: 0 additions & 1 deletion awesome_kanban/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': "Awesome Kanban",
'summary': """
Expand Down
3 changes: 1 addition & 2 deletions awesome_owl/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-

from . import controllers
from . import controllers
1 change: 0 additions & 1 deletion awesome_owl/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
{
'name': "Awesome Owl",

Expand Down
3 changes: 1 addition & 2 deletions awesome_owl/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-

from . import controllers
from . import controllers
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': 'estate',
'depends': [
'base'
],
'installable': True,
'application': True,
'data': [
'views/estate_property_offer_views.xml',
'views/estate_property_type_views.xml',
'views/estate_property_tag_views.xml',
'security/ir.model.access.csv',
'views/estate_property_views.xml',
'views/estate_menus.xml',
'views/estate_res_users.xml',
],
'license': 'LGPL-3',
}
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 estate_res_users
109 changes: 109 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from odoo import api, fields, models, exceptions
from datetime import datetime
from dateutil.relativedelta import relativedelta
from odoo.tools.float_utils import float_compare, float_is_zero
from odoo.exceptions import ValidationError


class EstateProperty(models.Model):
_name = "estate.property"
_description = "its just an estate property"
_order = "id desc"

# Basic fields
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date(default=datetime.now() + relativedelta(months=3), copy=False)
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(
selection=[('north', 'North'), ('south', 'South'), ('east', 'East'), ('west', 'West')],
)
active = fields.Boolean(default=True)

state = fields.Selection(
selection=[('new', 'New'), ('offer_received', 'Offer Received'), ('offer_accepted', 'Offer Accepted'), ('sold', 'Sold'), ('canceled', 'Canceled')],
required=True,
copy=False,
default='new')

# Relational fields
property_type_id = fields.Many2one("estate.property.type", string="Property Type")
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
sales_id = fields.Many2one("res.users", string="Salesperson", default=lambda self: self.env.user)
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")

# Computed fields
total_area = fields.Integer(compute="_compute_total_area")
best_price = fields.Float(compute="_compute_best_offer", default=0)

# Constraints
_sql_constraints = [
('check_expected_price', 'CHECK(expected_price > 0)',
'The expected price of a property should be strictly positive'),
('check_selling_price', 'CHECK(selling_price >= 0)',
'The selling price of a property should be positive')
]

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

# Compute Methods
@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_offer(self):
for record in self:
record.best_price = max(record.offer_ids.mapped('price'), default=0)

# On Changes
@api.onchange('garden')
def _onchange_garden(self):
# set default
if self.garden:
self.garden_area = 10
self.garden_orientation = 'north'

# clear values
else:
self.garden_area = 0
self.garden_orientation = None

# CRUD methods
@api.ondelete(at_uninstall=False)
def _chek_state(self):

Choose a reason for hiding this comment

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

The Model class implements an unlink method which is called when you unlink. You can override this method, do the if, then call super().unlink() to finish the unlink process

for record in self:
if record.state != 'new' and record.state != 'canceled':
raise exceptions.UserError("A property must be newly created or canceld to be deleted")

# Buttons methods
def set_canceled(self):
for record in self:
if record.state == 'sold':
raise exceptions.UserError("Sold properties cannot be canceled")

record.state = 'canceled'
return True

def set_sold(self):
for record in self:
if record.state == 'canceled':
raise exceptions.UserError("Canceled properties cannot be sold")

record.state = 'sold'
return True
62 changes: 62 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from odoo import api, fields, models, exceptions
from dateutil.relativedelta import relativedelta
from odoo.tools.float_utils import float_compare


class PropertyOffer(models.Model):
_name = "estate.property.offer"
_description = "Offer made by a potential buyer"
_order = "price desc"

price = fields.Float()
status = fields.Selection(copy=False, selection=[('accepted', 'Accepted'), ('refused', 'Refused')])
partner_id = fields.Many2one("res.partner", required=True)
property_id = fields.Many2one("estate.property", required=True)
property_type_id = fields.Many2one("estate.property.type", related="property_id.property_type_id", store=True)

validity = fields.Integer(default=7)
date_deadline = fields.Datetime(compute="_compute_deadline", inverse="_inverse_deadline")

# Constraints
_sql_constraints = [
('check_price', 'CHECK(price > 0)',
'The price of an offer should be strictly positive'),
]

# Compute methods
@api.depends('validity')
def _compute_deadline(self):
for record in self:
if record.create_date:
record.date_deadline = record.create_date + relativedelta(days=record.validity)
else:
record.date_deadline = fields.Date.today() + relativedelta(days=record.validity)

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

# CRUD methods
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
prop = self.env['estate.property'].browse(vals['property_id'])
if float_compare(vals['price'], prop.best_price, precision_digits=2) == -1:
raise exceptions.UserError("Cannot create an offer with a lower amount than an existing offer")
prop.state = 'offer_received'

return super().create(vals_list)

# Action methods
def action_accept(self):
for record in self:
record.status = 'accepted'
record.property_id.buyer_id = record.partner_id
record.property_id.selling_price = record.price
record.property_id.state = 'offer_accepted'
return True

def action_refuse(self):
for record in self:
record.status = 'refused'
return True
16 changes: 16 additions & 0 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from odoo import fields, models


class PropertyTag(models.Model):
_name = "estate.property.tag"
_description = "used to add precision to a property"
_order = "name"

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

# SQL constraints
_sql_constraints = [
('name_unique', 'unique(name)',
'The name of a tag should be unique'),
]
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 api, fields, models


class PropertyType(models.Model):
_name = "estate.property.type"
_description = "describe the type property"
_order = "sequence"

sequence = fields.Integer('Sequence', default=1)

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

# SQL constraints
_sql_constraints = [
('name_unique', 'unique(name)',
'The name of a property type should be unique')
]

# Compute Methods
@api.depends('offer_ids')
def _compute_nb_offer(self):
for record in self:
record.offer_count = len(record.offer_ids)
7 changes: 7 additions & 0 deletions estate/models/estate_res_users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from odoo import fields, models


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

property_ids = fields.One2many("estate.property", "sales_id", domain="['|', ('state', '=', 'new'), ('state', '=', '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
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
17 changes: 17 additions & 0 deletions estate/views/estate_menus.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

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

<menuitem id="estate_menu_Adv" name="Advertisements" parent="estate_menu_root"/>

<menuitem id="Adv_menu_property" action="estate.property_action" parent="estate_menu_Adv"/>


<menuitem id="estate_menu_settings" name="Settings" parent="estate_menu_root"/>

<menuitem id="sett_menu_property_type" action="estate.property_type_action" parent="estate_menu_settings"/>

<menuitem id="sett_menu_property_tag" action="estate.property_tag_action" parent="estate_menu_settings"/>

</odoo>
Loading