Skip to content

First and second chapter of the training #230

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 22 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
22 commits
Select commit Hold shift + click to select a range
a6f7cc5
First changes
artn-odoo Jan 20, 2025
dace4d8
[REF] refactors the commit messages
artn-odoo Jan 20, 2025
de9ee05
[IMP] Adds the model class. The class only has a name for now, it is …
artn-odoo Jan 21, 2025
471b0f4
[IMP] change the commits name to fit company regulations
artn-odoo Jan 20, 2025
0edf76f
[IMP] Adds the model class. The class only has a name for now, it is …
artn-odoo Jan 21, 2025
3506143
[IMP] adds the base for the estate module such as the name and the de…
artn-odoo Jan 21, 2025
c7f14d0
[IMP] Introducing security and bug fixing. The init file was missing …
artn-odoo Jan 21, 2025
bb8e04c
[IMP] Adding some views.
artn-odoo Jan 21, 2025
91d57ab
[IMP] Adding views to the module.
artn-odoo Jan 22, 2025
cff053b
[IMP] Adds a lot of menu items.
artn-odoo Jan 23, 2025
4c73aeb
[FIX] Fix the order of the file and end of chapter 7.
artn-odoo Jan 23, 2025
72b9caf
[IMP] add computed and onchange fields.
artn-odoo Jan 24, 2025
c1a28f3
[IMP] Add actions to the views
artn-odoo Jan 24, 2025
ea98248
[ADD] add gitignore for launch.json
artn-odoo Jan 24, 2025
41835ab
[IMP] Add constraints to various models.
artn-odoo Jan 27, 2025
6a86e61
[ADD] created probably conflict in ResUsers extension
ethanrobv Jan 27, 2025
f7b3ff8
[IMP] Add all kind of QoL.
artn-odoo Jan 28, 2025
aaae53f
[IMP] Add a res users field and constraints on create and delete.
artn-odoo Jan 29, 2025
ccc40e6
[ADD] Add the new estate_account module.
artn-odoo Jan 29, 2025
4cedb35
[IMP] Bug solving and add kanban view.
artn-odoo Jan 30, 2025
1aedbed
[FIX] Fixed bug in kanban view.
artn-odoo Jan 30, 2025
be5c265
[FIX] Fix warnings and bugs on the new module.
artn-odoo Jan 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ tutorial's solutions, and one for the
[Master the Odoo web framework](https://www.odoo.com/documentation/latest/developer/tutorials/master_odoo_web_framework.html)
tutorial's solutions. For example, `17.0`, `17.0-discover-js-framework-solutions` and
`17.0-master-odoo-web-framework-solutions`.
I'm trying to make some changes on my branche.
2 changes: 2 additions & 0 deletions estate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.vscode/launch.json
launch.json
25 changes: 25 additions & 0 deletions estate/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "python:odoo",
"type": "debugpy",
"request": "launch",
"stopOnEntry": false,
"python": "/usr/bin/python3",
"program": "/home/odoo/odoo-training/odoo/odoo-bin",
"console": "integratedTerminal",
"args": [
"--limit-time-cpu=99999",
"--limit-time-real=99999",
"--limit-request=10000",
"--addons-path=/home/odoo/odoo-training/odoo/addons,/home/odoo/odoo-training/enterprise,/home/odoo/odoo-training/tutorials",
"--dev=all",
"-d rd-demo",
],
}
]
}
3 changes: 3 additions & 0 deletions estate/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.languageServer": "None"
}
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
21 changes: 21 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
'name': "Real estate module!!",
'version': '1.0',
'depends': ['base'],
'author': "Arthur Nanson",
'category': 'Category',
'description': """
With this awesome module you can do awesome things like buy a house somehow maybe
""",
'application' : True,
# data files always loaded at installation
#'data' : ['data/estate.property.csv'],
'data' : ['security/ir.model.access.csv',
'views/estate_property_view.xml',
'views/estate_property_tag_view.xml',
'views/estate_property_offer_view.xml',
'views/estate_property_type_view.xml',
'views/estate_menu.xml',
'views/res.users.view.xml'
],
}
Empty file added estate/launch.json
Empty file.
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
111 changes: 111 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from odoo import models, fields, api, exceptions
from datetime import date, timedelta
from odoo.exceptions import ValidationError
class EstateProperty(models.Model):

_name = "estate.property"
_description = "Damn this model is good for doing real estate related stuff"
_order = "id desc"

name = fields.Char(name = "Title", required=True)
description = fields.Text(name = "Description", required = True)
postcode = fields.Char(name = "PostCode", required = True)
date_availability = fields.Date(name = "Availability", default=lambda self: date.today() + timedelta(days=90), copy=False)
expected_price = fields.Float(name="Expected Price")
selling_price = fields.Float(name = "Selling Price", readonly = True, copy = False)
bedrooms = fields.Integer(name = "Bedrooms", default = 2)
living_area = fields.Integer(name = "Living Area (m²)")
facades = fields.Integer(name = "Facades")
garage = fields.Boolean(name = "Garage")
garden = fields.Boolean(name = "Garden")
garden_area = fields.Integer(name = "Garden Area (m²)")
garden_orientation = fields.Selection(string='Garden orientation',
selection=[('north', 'North'),
('west', 'West'), ('south', 'South'),
('east', 'East')
],
help="Chose the direct which the garden is facing")

active = fields.Boolean(default=True)
state = fields.Selection(string='Status',
selection=[('new', 'New'),
('offer received', 'Offer received'),
('offer accepted', 'Offer accepted'),
('sold', 'Sold'),
('cancelled', 'Cancelled')
],
default='new',
help="Is the house sold already ?")
type_id = fields.Many2one("estate.property.types", string = "Property Type")
sales_person_id = fields.Many2one("res.users", string = "Sales Person", default=lambda self: self.env.user)
buyer_id = fields.Many2one("res.partner", string = "Buyer", copy=False)
tag_ids = fields.Many2many("estate.property.tag", string="Tags", name="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", name="Offers")
total_area = fields.Float(compute="_compute_total_area", string="Total Area (m²)")
best_price = fields.Float(compute="_compute_best_price", string="Best price")
salesperson_id = fields.Many2one("res.users", string="Salesperson")


_sql_constraints = [
('positive_expected_price',
'CHECK (expected_price>0)',
'The expected price should always be positive!'),
('positive_selling_price',
'CHECK (selling_price>0)',
'The selling price should always be positive!'),
]

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

@api.depends("offer_ids")
def _compute_best_price(self):
for record in self:
if record.offer_ids:
record.best_price = max(record.offer_ids.mapped('price'))
else:
record.best_price = 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.constrains('selling_price')
def check_selling_price_not_lower_than_90_percent_of_expected_price(self):
for record in self:
if record.selling_price < 0.9 * record.expected_price:
raise ValidationError("The selling price must be at least 90% of the expected price")

@api.ondelete(at_uninstall=False)
def _unlink_if_state_is_new_or_cancelled(self):
for record in self:
if record.state not in ['new', 'cancelled']:
raise ValidationError("You can't delete a property in this state")

Choose a reason for hiding this comment

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

remove extra lines

def cancel_property(self):

Choose a reason for hiding this comment

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

Leave a newline betwixt method definitions

for record in self:
if record.state == "sold":
raise exceptions.UserError("A sold property can't be cancelled")
else:
record.state = "cancelled"
def sold_property(self):
for record in self:
if record.state == "cancelled":
raise exceptions.UserError("A cancelled property can't be sold")
else:
record.state = "sold"
def set_buyer(self, buyer):
self.buyer_id=buyer
def set_status(self, status):
self.state = status
def set_sold_price(self, price):
self.selling_price = price
def get_status(self):
return self.state
64 changes: 64 additions & 0 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from odoo import models, fields, api, exceptions
from datetime import date, timedelta

class EstatePropertyOffer(models.Model):

_name = "estate.property.offer"
_description = "The offers for a property"
_order = "price desc"

price = fields.Float(name = "Price", required = True)
status = fields.Selection(string='Status',
selection=[('accepted', 'Accepted'),
('refused', 'Refused'),
],
help="What was the answer to the offer ?")
partner_id = fields.Many2one("res.partner", required=True, name="Partner")
property_id = fields.Many2one("estate.property", required=True)
validity = fields.Integer(name="Validity", default=7)
date_deadline = fields.Date(name="Deadline", compute="_compute_deadline", inverse="_inverse_validity")
property_type_id = fields.Many2one(
"estate.property.types",

Choose a reason for hiding this comment

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

You don't have to change it all at this point, but we prefer single quotes in python files unless using double is required for some semantic reasons

related="property_id.type_id",
store=True,
string="Property Type"
)
_sql_constraints = [
('positive_offer_price',
'CHECK(price > 0)',
'The offer price must be strictly positive!')
]

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

@api.model_create_multi
def create(self, vals):
if vals['price'] < self.env['estate.property'].browse(vals['property_id']).best_price:
raise ValueError("Can't make an offer this low")
return super(EstatePropertyOffer, self).create(vals)

def _inverse_validity(self):
for record in self:
if record.date_deadline:
create_date = record.create_date.date() if record.create_date else fields.Date.today()
record.validity = (record.date_deadline - create_date).days
else:
record.validity = 0

def action_reject_offer(self):
self.status = "refused"

def action_accept_offer(self):
if not self.property_id.get_status():
self.status = "accepted"
self.property_id.set_buyer(self.partner_id)
self.property_id.set_status("sold")
self.property_id.set_sold_price(self.price)
else:
raise exceptions.UserError("This property has already been sold !")
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 models, fields

class EstatePropertyTag(models.Model):

_name = "estate.property.tag"
_description = "All tags applicable to estate properties"
_order = "name"

name = fields.Char(name = "Tag name", required = True)
color = fields.Integer(name="Color")
_sql_constraints = [
('unique_tag_name',
'UNIQUE(name)',
'Tag names must be unique!')
]
18 changes: 18 additions & 0 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from odoo import models, fields, api

class EstatePropertyType(models.Model):

_name = "estate.property.types"
_description = "Different kind of estate properties"
_order = "name"

name = fields.Char(name = "Type of Estate Property", required = True)
sequence = fields.Integer('Sequence', default=1, help="Used to order stages. Lower is better.")
property_ids = fields.One2many("estate.property", "type_id", name = "Properties")
offer_ids = fields.One2many("estate.property.offer", "property_type_id", name="Offers")
offer_number = fields.Integer(compute="_count_offers", name =" Offers")

@api.depends("offer_ids")
def _count_offers(self):
for record in self:
record.offer_number = len(record.offer_ids)
7 changes: 7 additions & 0 deletions estate/models/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", "salesperson_id", string="Properties", domain=[('state', 'in', ['new', 'offer received'])])
8 changes: 8 additions & 0 deletions estate/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
estate.access_estate_property,access_estate_property,model_estate_property,base.group_user,1,1,1,1
estate.access_estate_property_type,access_estate_property_type,model_estate_property_types,base.group_user,1,1,1,1
estate.access_estate_property_tag,access_estate_property_tag,model_estate_property_tag,base.group_user,1,1,1,1
estate.access_estate_property_offer,access_estate_property_offer,model_estate_property_offer,base.group_user,1,1,1,1
res.users,res.users,model_res_users,base.group_user,1,1,1,1


15 changes: 15 additions & 0 deletions estate/views/estate_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<menuitem id="properties_menu_root" name="Estate Properties">
<menuitem id="properties_first_level_menu" name="Dashboard">
<menuitem id="properties_model_menu_action" action="action_estate_property"/>
</menuitem>
<menuitem id="properties_types_first_level_menu" name="Categories">
<menuitem id="properties_model_type_menu_action" action="action_estate_property_type"/>
<menuitem id="properties_model_tag_menu_action" action="action_estate_property_tag"/>
<menuitem id="properties_model_offer_menu_action" action="action_estate_property_offer"/>
</menuitem>
</menuitem>
</data>
</odoo>
35 changes: 35 additions & 0 deletions estate/views/estate_property_offer_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<record id="view_estate_property_offer_list" model="ir.ui.view">
<field name="name">estate.property.offer.list</field>
<field name="model">estate.property.offer</field>
<field name="type">list</field>
<field name="arch" type="xml">
<list editable="bottom" decoration-success="status in ['accepted']" decoration-danger="status in ['refused']">
<field name="price"/>
<field name="partner_id"/>
<field name="date_deadline"/>
<button name="action_accept_offer" string="Accept" type="object" icon="fa-check" invisible="status in ['accepted', 'refused']"/>
<button name="action_reject_offer" string="Reject" type="object" icon="fa-times" invisible="status in ['accepted', 'refused']"/>
<field name="property_type_id" width="120px"/>
<field name="status" column_invisible="1"/>
</list>
</field>
</record>
<record id="action_estate_property_offer" model="ir.actions.act_window">
<field name="name">Offers</field>
<field name="res_model">estate.property.offer</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="view_estate_property_offer_list"/>
</record>
<record id="action_estate_property_offer_from_types" model="ir.actions.act_window">
<field name="name">Offers From Types</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>
<field name="view_id" ref="view_estate_property_offer_list"/>
</record>

</data>
</odoo>
22 changes: 22 additions & 0 deletions estate/views/estate_property_tag_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<record id="view_estate_property_tag_list" model="ir.ui.view">
<field name="name">estate.property.tag.list</field>
<field name="model">estate.property.tag</field>
<field name="type">list</field>
<field name="arch" type="xml">
<list editable="bottom">
<field name="name"/>
</list>
</field>
</record>
<record id="action_estate_property_tag" model="ir.actions.act_window">
<field name="name">Tags</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="view_estate_property_tag_list"/>
</record>

</data>
</odoo>
Loading