8000 XAFR - Technical Training by xafr-odoo · Pull Request #740 · odoo/tutorials · GitHub
[go: up one dir, main page]

Skip to content

XAFR - Technical Training #740

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 21 commits into
base: 18.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
[IMP] estate: Chapter 11
Add the sprinkles
Fixes according to review comments
  • Loading branch information
xafr-odoo committed Apr 29, 2025
commit a4863d5a15533aaa97f48cafda740d2ecb1f6ebe
2 changes: 1 addition & 1 deletion awesome_gallery/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 8000 +4,7 @@
'summary': """
Starting module for "Master the Odoo web framework, chapter 3: Create a Gallery View"
""",

'author': "unknown",

Choose a reason for hiding this comment

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

But why? At least put Odoo if you have to fill something

Copy link
Author

Choose a reason for hiding this comment

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

I just wanted to get rid of the warning messages but yeah should have put odoo. I'm changing it now.

'description': """
Starting module for "Master the Odoo web framework, chapter 3: Create a Gallery View"
""",
Expand Down
2 changes: 1 addition & 1 deletion awesome_kanban/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
'summary': """
Starting module for "Master the Odoo web framework, chapter 4: Customize a kanban view"
""",

'author': "unknown",
'description': """
Starting module for "Master the Odoo web framework, chapter 4: Customize a kanban view.
""",
Expand Down
20 changes: 8 additions & 12 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
class EstateProperty(models.Model):
_name = 'estate.property'
_description = 'Estate properties'
_order = "id desc"

_check_strictly_positive_expected_price = (models.Constraint("""CHECK (expected_price > 0)""",
"The property expected price must be strictly positive."))

_check_positive_selling_price = (models.Constraint("""CHECK (selling_price >= 0)""",
"The property selling price must be positive."))

name = fields.Char('Title', required=True, translate=True)
description = fields.Text()
Expand All @@ -23,7 +30,7 @@ class EstateProperty(models.Model):
selection=[('north', 'North'), ('west', 'West'), ('south', 'South'), ('east', 'East')],
help='The selection of the garden orientation.')
active = fields.Boolean(default=True)
state = fields.Selection(
state = fields.Selection(string="Status",
required=True,
copy=False,
default='new',
Expand All @@ -33,7 +40,6 @@ class EstateProperty(models.Model):
buyer_id = fields.Many2one("res.partner", string="Buyer", copy=False)
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offers")

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

@api.depends("living_area", "garden_area")
Expand Down Expand Up @@ -79,18 +85,8 @@ def action_btn_cancel(self):
raise UserError(_("This property is already sold and thus cannot be cancelled anymore."))
return True

# Add a constraint so that the selling price cannot be lower than 90% of the expected price.
#
# Tip: the selling price is zero until an offer is validated. You will need to fine tune your check to take this into account.
@api.constrains('expected_price', 'selling_price')
@api.onchange("expected_price", "selling_price")
def _check_offer_price(self):
for record in self:
if tools.float_utils.float_compare((record.expected_price * 0.9), record.selling_price, precision_rounding=0.01) >= 0 and not tools.float_utils.float_is_zero(record.selling_price, precision_rounding=0.001):
raise ValidationError(_("Offer price cannot be lower than 90% of the expected price."))

_check_strictly_positive_expected_price = (models.Constraint("""CHECK (expected_price > 0)""",
"The property expected price must be strictly positive."))

_check_positive_selling_price = (models.Constraint("""CHECK (selling_price >= 0)""",
"The property selling price must be positive."))
8 changes: 5 additions & 3 deletions estate/models/estate_property_offer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
class EstatePropertyOffer(models.Model):
_name = 'estate.property.offer'
_description = 'Estate property offer'
_order = "price desc"

_check_positive_offer_price = (models.Constraint("""CHECK (price >= 0)""",
"The property offer price must be positive."))

price = fields.Float()
status = fields.Selection(
Expand All @@ -13,6 +17,7 @@ class EstatePropertyOffer(models.Model):
property_id = fields.Many2one("estate.property", string="Offer", required=True)

Choose a reason for hiding this comment

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

That's a nasty copy paste (string still indicates it's an Offer 😄). Same for these Many2ones

Copy link
Author

Choose a reason for hiding this comment

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

I actually named it offer_id at first but then the instructions said to name it property_id and it ended up not being displayed anyway.. Should I just omit string="..." as much as possible and try to rely on the automated system from odoo you referenced here: #740 (comment) ?

validity = fields.Integer(default=7)
date_deadline = fields.Date(compute="_compute_deadline", inverse="_inverse_deadline", default=fields.Date.add(fields.Date.today(), days=7))
property_type_id = fields.Many2one(related='property_id.property_type_id')

@api.depends("create_date", "validity")
def _compute_deadline(self):
Expand All @@ -34,6 +39,3 @@ def action_btn_refuse(self):
for record in self:
record.status = "refused"
return True

_check_positive_offer_price = (models.Constraint("""CHECK (price >= 0)""",
"The property offer price must be positive."))
6 changes: 4 additions & 2 deletions estate/models/estate_property_tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
class EstatePropertyTag(models.Model):
_name = 'estate.property.tag'
_description = 'Estate property tag'

name = fields.Char('Title', required=True, translate=True)
_order = "name asc"

_tag_name_uniq = (models.Constraint("""UNIQUE (name)""",
"The tag name must be unique."))

name = fields.Char('Title', required=True, translate=True)
color = fields.Integer('Color')
16 changes: 13 additions & 3 deletions estate/models/estate_property_type.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
from odoo import fields, models
from odoo import fields, models, api


class EstatePropertyType(models.Model):
_name = 'estate.property.type'
_description = 'Estate property type'

name = fields.Char('Title', required=True, translate=True)
_order = "manual_ordering asc, name asc"

_type_name_uniq = (models.Constraint("""UNIQUE (name)""",
"The type name must be unique."))

name = fields.Char('Title', required=True, translate=True)
line_ids = fields.One2many("estate.property", "property_type_id")
manual_ordering = fields.Integer('Sequence', default=1, help="Used to order stages. Lower is better.")

Choose a reason for hiding this comment

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

Normally we'd call the field sequence as it is something we have quite a bit in the standard code. But it is not a big issue

Copy link
Author

Choose a reason for hiding this comment

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

Noted and corrected :)

offer_ids = fields.One2many("estate.property.offer", "property_type_id")
offer_count = fields.Integer(compute='_compute_offers')

Choose a reason for hiding this comment

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

By convention the compute should include the name of the computed field.

_compute_offer_count should be the one here 😄

Copy link
Author

Choose a reason for hiding this comment

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

Noted and fixed


@api.depends("offer_ids")
def _compute_offers(self):
for record in self:
record.offer_count = len(record.offer_ids)
10 changes: 6 additions & 4 deletions estate/views/estate_property_offer_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
<field name="name">estate.property.offer.list</field>
<field name="model">estate.property.offer</field>
<field name="arch" type="xml">
<list string="Channel" editable="bottom">
<list string="Channel" decoration-success="status=='accepted'" decoration-danger="status=='refused'">
<field name="price"/>
<field name="partner_id"/>
<field name="validity"/>
<field name="date_deadline"/>
<button name="action_btn_accept" type="object" string="Accept" icon="fa-check"/>
<button name="action_btn_refuse" type="object" string="Refuse" icon="fa-times"/>
<field name="status"/>
<button name="action_btn_accept" type="object" string="Accept" icon="fa-check" invisible="status=='accepted' or status=='refused'"/>
<button name="action_btn_refuse" type="object" string="Refuse" icon="fa-times" invisible="status in ['accepted', 'refused']"/>
<field name="status" optional="hide"/>
<field name="property_id" optional="show"/>
<field name="property_type_id" optional="show"/>
</list>
</field>
</record>
Expand Down
4 changes: 2 additions & 2 deletions estate/views/estate_property_tag_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
<record id="estate_property_tag_model_action" model="ir.actions.act_window">
<field name="name">Property Tags</field>
<field name="res_model">estate.property.tag</field>
<field name="view_mode">form, list</field>
<field name="view_mode">list</field>
</record>

<record id="estate_property_tag_list_view" model="ir.ui.view">
<field name="name">estate.property.tag.list</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<list string="Channel" editable="bottom">
<list string="Channel">
<field name="name"/>
</list>
</field>
Expand Down
49 changes: 40 additions & 9 deletions estate/views/estate_property_type_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@
<record id="estate_property_type_model_action" model="ir.actions.act_window">
<field name="name">Property Types</field>
<field name="res_model">estate.property.type</field>
<field name="view_mode">form, list</field>
<field name="view_mode">list,form</field>
</record>

<record id="estate_property_tag_list_view" model="ir.ui.view">
<field name="name">estate.property.tag.list</field>
<field name="model">estate.property.tag</field>
<field name="arch" type="xml">
<list string="Channel" editable="bottom">
<field name="name"/>
</list>
</field>
<record id="estate_property_offer_model_action" model="ir.actions.act_window">
<field name="name">Property Offers</field>
<field name="res_model">estate.property.offer</field>
<field name="view_mode">list</field>
<field name="domain">[('property_id.property_type_id', '=', active_id)]</field>
</record>

<record id="estate_property_type_form_view" model="ir.ui.view">
Expand All @@ -22,11 +19,45 @@
<field name="arch" type="xml">
<form string="Property Type">
<sheet>
<div class="oe_button_box" name="button_box">
<button name="%(estate_property_offer_model_action)d"
type="action"
class="oe_stat_button"
icon="fa-money">
<field name="offer_count" widget="statinfo" string="Offers"/>
</button>
</div>
<h1>
<field name="name"/>
</h1>
<notebook>
<page string="Properties">

<field name="line_ids">
<list>
<field name="name"/>
<field name="expected_price"/>
<field name="state"/>
</list>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>

<record id="estate_property_type_list_view" model="ir.ui.view">
<field name="name">estate.property.type.list</field>
<field name="model">estate.property.type</field>
<field name="arch" type="xml">
<list>
<field name="manual_ordering" widget="handle"/>
<field name="name"/>
<field name="line_ids"/>
<field name="offer_ids"/>
<field name="offer_count"/>
</list>
</field>
</record>
</odoo>
33 changes: 18 additions & 15 deletions estate/views/estate_property_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@
<field name="name">Properties</field>
<field name="res_model">estate.property</field>
<field name="view_mode">list,form</field>
<field name="context">{'search_default_availability': True}</field>

Choose a reason for hiding this comment

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

Guess that eludes my review on that part 😄

</record>

<record id="estate_property_list_view" model="ir.ui.view">
<field name="name">estate.property.list</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<list string="Channel" editable="bottom">
<field name="name"/>
<list string="Channel" decoration-success="state in ['offer_received', 'offer_accepted']" decoration-muted="state=='sold'">
<field name="name" decoration-success="state in ['offer_received', 'offer_accepted']" decoration-bf="state=='offer_accepted'" decoration-muted="state=='sold'"/>
<field name="bedrooms"/>
<field name="postcode"/>
<field name="living_area"/>
<field name="expected_price"/>
<field name="selling_price"/>
<field name="date_availability"/>
<field name="date_availability" optional="hide"/>
<field name="salesman_id"/>
<field name="buyer_id"/>
<field name="property_type_id"/>
<field name="tag_ids" widget="many2many_tags"/>
<field name="property_type_id" widget="many2one" options="{'no_create': True}"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="state"/>
</list>
</field>
</record>
Expand All @@ -30,20 +32,20 @@
<field name="name">estate.property.form</field>
<field name="model">estate.property</field>
<field name="arch" type="xml">
<form string="Properties">
<form string="Properties" decoration-success="state in ['offer_received', 'offer_accepted']" decoration-muted="state=='sold'">
<header>
<button name="action_btn_sold" type="object" string="Sold"/>
<button name="action_btn_cancel" string="Cancel" type="object" class="oe_highlight"/>
<button name="action_btn_sold" type="object" string="Sold" invisible="state=='cancelled' or state=='sold'"/>
<button name="action_btn_cancel" string="Cancel" type="object" class="oe_highlight" invisible="state=='cancelled' or state=='sold'"/>
<field name="state" widget="statusbar" statusbar_visible="new,offer_received,offer_accepted,sold"/>
</header>
<sheet>
<h1>
<field name="name"/>
<field name="tag_ids" widget="many2many_tags"/>
</h1>
<group>
<group>
<field name="state"/>
<field name="property_type_id"/>
<field name="property_type_id" options="{'no_create': True}"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field name="postcode"/>
<field name="date_availability"/>
</group>
Expand All @@ -62,13 +64,13 @@
<field name="facades"/>
<field name="garage"/>
<field name="garden"/>
<field name="garden_area"/>
<field name="garden_orientation"/>
<field name="garden_area" invisible="not garden"/>
<field name="garden_orientation" invisible="not garden"/>
<field name="total_area"/>
</group>
</page>
<page string="Offers">
<field name="offer_ids"/>
<field name="offer_ids" readonly="state in ['offer_accepted', 'sold', 'cancelled']"/>
</page>
<page string="Other Info">
<group>
Expand All @@ -93,8 +95,9 @@
<field name="bedrooms" string="Bathrooms"/>
<field name="facades" string="Facades"/>
<field name="property_type_id"/>
<field name="living_area" filter_domain="[('living_area', '>=', self)]"/>
<separator/>
<filter string="Available" name="state" domain="['|', ('state', '=', 'new'), ('state', '=', 'offer_received')]"/>
<filter string="Available" name="availability" domain="['|', ('state', '=', 'new'), ('state', '=', 'offer_received')]" help="Shows only available properties."/>
<group expand="1" string="Group By">
<filter string="Postcode" name="postcode" context="{'group_by':'postcode'}"/>
</group>
Expand Down
0