Skip to content

Conversation

@wimar-odoo
Copy link

No description provided.

@wimar-odoo wimar-odoo requested a review from thcl-odoo October 22, 2025 14:10
@robodoo
Copy link

robodoo commented Oct 22, 2025

Pull request status dashboard

Copy link

@thcl-odoo thcl-odoo left a comment

Choose a reason for hiding this comment

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

First set of comments :) Overall, it's fine!

Here are a few general comments:

  • Try to be consistent with the use of single quotes ' and ". If you start using one of them, be consistent throughout :)
  • Be careful with commit conventions (you can check the documentation here: Documentation )

For example [ADD] Chapter 1: Init and manifest => [ADD] estate: app creation
[ADD] Chapter 3: Models and Basic Fields => [IMP] estate: add models & basic fields etc.

Create empty __init__.py and __manifest__.py with module name, set as application.
Created the Estate Property model and added it to the __init__.py file. Defined the model’s attributes and fields.
Created access rights granting full permissions to base.group_user and added them to the __manifest__.py file.
Created the first action and menu, and added them to the __manifest__.py file.
Linked the action to the menu.
- Added custom views: List, Form, and Search
- Fixed empty line at EOF to comply with PEP8
@wimar-odoo wimar-odoo force-pushed the 19.0-onboarding-wimar branch from 8e27485 to bd3010f Compare October 23, 2025 12:57
@wimar-odoo wimar-odoo force-pushed the 19.0-onboarding-wimar branch from bd3010f to db9ded5 Compare October 23, 2025 13:15
- Added property types such as House, Apartment, etc., with corresponding Menus, Actions, and Views
- Added property tags such as Renovated, etc., with corresponding Menus, Actions, and Views
- Added an Offers tab on properties to display all offers related to a specific property
- Added computed, inverse, and onchange fields to improve user convenience and automation:
  total_area, best_price, validity, date_deadline, and garden
- Added buttons to sell or cancel a property, and to accept or refuse an offer
- Added SQL constraints to ensure positive prices and unique names
- Added Python constraints to enforce offers at least 90% of the expected price
Copy link

@thcl-odoo thcl-odoo left a comment

Choose a reason for hiding this comment

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

New batch of comments but looking good so far :)

Comment on lines 65 to 69
for record in self:
if record.state == "cancelled":
raise UserError("Cancelled properties cannot be sold.")
record.state = "sold"
return True

Choose a reason for hiding this comment

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

It's better to make the error message translatable :)

raise UserError(_("Cancelled properties cannot be sold."))

You could also do something like this

Suggested change
for record in self:
if record.state == "cancelled":
raise UserError("Cancelled properties cannot be sold.")
record.state = "sold"
return True
if self.filtered(lambda x: x.state == "cancelled"):
raise UserError(_("Cancelled properties cannot be sold."))
self.state = "sold"
return True

selection=[("accepted", "Accepted"), ("refused", "Refused")],
copy=False,
)
partner_id = fields.Many2one("res.partner", string="Buyer", required=True)

Choose a reason for hiding this comment

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

linter cries about this space 😅

Suggested change
partner_id = fields.Many2one("res.partner", string="Buyer", required=True)
partner_id = fields.Many2one("res.partner", string="Buyer", required=True)

access_estate_property_user,access_estate_property_user,model_estate_property,base.group_user,1,1,1,1
access_estate_property_type_user,access_estate_property_type_user,model_estate_property_type,base.group_user,1,1,1,1
access_estate_property_tag_user,access_estate_property_tag_user,model_estate_property_tag,base.group_user,1,1,1,1
access_estate_property_offer_user,access_estate_property_offer_user,model_estate_property_offer,base.group_user,1,1,1,1 No newline at end of file

Choose a reason for hiding this comment

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

missing line break 😄

- Automatic and manual ordering
- Decorations and invisible attributes
- Stat button with action
- Domain filters
- Override create method with ondelete constraint
- Inherit res.users to display properties on user form
@wimar-odoo wimar-odoo force-pushed the 19.0-onboarding-wimar branch 2 times, most recently from e876115 to d87db09 Compare October 28, 2025 15:42
Copy link

@thcl-odoo thcl-odoo left a comment

Choose a reason for hiding this comment

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

new batch of comments 😄

  • this commit [IMP] estate: Chapter 13 - Other modules could/should be sth like [ADD] estate_account: Chapter 13 - Other modules

Comment on lines 77 to 80
_positive_expected_price = models.Constraint(
"CHECK(expected_price > 0)",
"The expected price must be strictly positive.",
)

Choose a reason for hiding this comment

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

just a small comment like that but nothing mandatory: I feel like it's more readable to have the constraints just under the fields, so they are not "hidden" between all those functions :)

but it's just a personal preference

@api.model
def create(self, vals_list):
for vals in vals_list:
if vals["price"] <= self.env["estate.property"].browse(vals["property_id"]).best_price:

Choose a reason for hiding this comment

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

it's better to make the orm method outside of the loop.
For example, you could retrieve every "estate.property" outside of the loop and then filtered them in your loop

for vals in vals_list:
if vals["price"] <= self.env["estate.property"].browse(vals["property_id"]).best_price:
raise UserError(_("The offer price must be strictly greater than the current best offer."))
self.env["estate.property"].browse(vals["property_id"]).state = "received"

Choose a reason for hiding this comment

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

same with this line, if you use an intermediate variable with your filtered property

<field name="living_area" filter_domain="[('living_area', '>', self)]"/>
<field name="facades"/>
<separator/>
<filter string="Available" name="state" domain="['|',('state', '=', 'new'),('state', '=', 'received')]"/>

Choose a reason for hiding this comment

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

it could be

Suggested change
<filter string="Available" name="state" domain="['|',('state', '=', 'new'),('state', '=', 'received')]"/>
<filter string="Available" name="state" domain="[('state', 'in', ['new', 'received'])]"/>

@wimar-odoo wimar-odoo force-pushed the 19.0-onboarding-wimar branch from 9ba22fe to b48fa58 Compare October 30, 2025 08:52
Copy link

@thcl-odoo thcl-odoo left a comment

Choose a reason for hiding this comment

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

new batch of comments, wp :p


<t t-name="awesome_owl.counter">
<div>
Counter: <t t-esc="state.value"/>

Choose a reason for hiding this comment

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

Suggested change
Counter: <t t-esc="state.value"/>
Counter: <t t-out="state.value"/>

Comment on lines 20 to 22
if (this.props.onChange) {
this.props.onChange();
}

Choose a reason for hiding this comment

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

just for your information, it's maybe less readable in this case but in JS you can do something like this

Suggested change
if (this.props.onChange) {
this.props.onChange();
}
this.props.onChange?.();

Comment on lines 1 to 3
.chapter_tuto {
border: solid; padding: 10px; border-radius: 5px; margin-bottom: 5px
}

Choose a reason for hiding this comment

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

just for lisibility :)

Suggested change
.chapter_tuto {
border: solid; padding: 10px; border-radius: 5px; margin-bottom: 5px
}
.chapter_tuto {
border: solid;
padding: 10px;
border-radius: 5px;
margin-bottom: 5px
}

<div class="chapter_tuto">
<Counter onChange.bind="incrementSum" t-props="{'buttonText': 'Custom Incr.'}"/>
<Counter onChange.bind="incrementSum"/>
<p>The sum id: <t t-esc="state.value"/></p>

Choose a reason for hiding this comment

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

Suggested change
<p>The sum id: <t t-esc="state.value"/></p>
<p>The sum id: <t t-out="state.value"/></p>

Copy link

@thcl-odoo thcl-odoo left a comment

Choose a reason for hiding this comment

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

Oups I missed this one

Comment on lines 7 to 9
<button class="btn btn-primary" t-on-click="increment" style="margin-left: 5px">
<t t-out="props.buttonText"/>
</button>

Choose a reason for hiding this comment

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

You can "inline" the t-out like this

Suggested change
<button class="btn btn-primary" t-on-click="increment" style="margin-left: 5px">
<t t-out="props.buttonText"/>
</button>
<button class="btn btn-primary" t-on-click="increment" style="margin-left: 5px" t-out="props.buttonText" />

Copy link

@thcl-odoo thcl-odoo left a comment

Choose a reason for hiding this comment

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

Good job!

Lastest batch of comments

}

getItemDescription(item) {
return item.description || "Dashboard item";

Choose a reason for hiding this comment

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

Suggested change
return item.description || "Dashboard item";
return item.description || _t("Dashboard item");

t-on-change="() => this.toggleItem(item.backend_attribute)"
/>
<label class="form-check-label" t-att-for="'item_' + item.backend_attribute">
<t t-esc="getItemDescription(item)"/>

Choose a reason for hiding this comment

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

Suggested change
<t t-esc="getItemDescription(item)"/>
<t t-out="getItemDescription(item)"/>

type: Number,
}
}
} No newline at end of file

Choose a reason for hiding this comment

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

Missing EOL

class="form-check-input"
t-att-id="'item_' + item.backend_attribute"
t-att-checked="state.selectedItems[item.backend_attribute]"
t-on-change="() => this.toggleItem(item.backend_attribute)"

Choose a reason for hiding this comment

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

No need to change, but another pattern could have been used like.
Sometimes, it can be useful so you've seen this at least once :)

Suggested change
t-on-change="() => this.toggleItem(item.backend_attribute)"
t-att-data-backend-attribute="item.backend_attribute"
t-on-change="toggleItem"

And then update your JS function like this

    toggleItem(event) {
		const backendAttribute = event.currentTarget.getAttribute("backend-attribute")
        this.state.selectedItems[backendAttribute] = !this.state.selectedItems[backendAttribute];
    }

<templates xml:space="preserve">

<t t-name="awesome_dashboard.NumberCard">
<p class="text-center"><t t-out="props.title"/></p>

Choose a reason for hiding this comment

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

could be simplified like this

Suggested change
<p class="text-center"><t t-out="props.title"/></p>
<p class="text-center" t-out="props.title"/>

Comment on lines 31 to 37
const visibleItems = {};
Object.entries(this.allItems).forEach(([itemId, item]) => {
if (!this.state.hiddenItems.includes(item.backend_attribute)) {
visibleItems[itemId] = item;
}
});
return visibleItems;

Choose a reason for hiding this comment

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

In general, when you want to "migrate" from an array to a single value (here a dict), .reduce(...) is the solution :)

(just for your information)

Suggested change
const visibleItems = {};
Object.entries(this.allItems).forEach(([itemId, item]) => {
if (!this.state.hiddenItems.includes(item.backend_attribute)) {
visibleItems[itemId] = item;
}
});
return visibleItems;
return Object.entries(this.allItems).reduce((acc, [itemId, item]) => {
if (!this.state.hiddenItems.includes(item.backend_attribute)) {
acc[itemId] = item;
}
return acc;
}, {});

Comment on lines 58 to 61
const config = {};
for (const item of Object.values(this.allItems)) {
config[item.backend_attribute] = !hiddenItems.includes(item.backend_attribute);
}

Choose a reason for hiding this comment

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

Same here for the reduce (but nothing mandatory)

Suggested change
const config = {};
for (const item of Object.values(this.allItems)) {
config[item.backend_attribute] = !hiddenItems.includes(item.backend_attribute);
}
const config = Object.values(this.allItems).reduce((acc, item) => {
acc[item.backend_attribute] = !hiddenItems.includes(item.backend_attribute);
return acc;
}, {});

margin: 5px;
padding: 10px;
color: black;
} No newline at end of file

Choose a reason for hiding this comment

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

missing eol


<t t-name="awesome_dashboard.dashboarditem">
<div class="item_card" t-att-style="`width: ${18 * props.size}rem;`">
<t t-slot="default"></t>

Choose a reason for hiding this comment

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

could be autoclosing tag

Suggested change
<t t-slot="default"></t>
<t t-slot="default" />

},
};

registry.category("services").add("awesome_dashboard.statistics", statisticsService); No newline at end of file

Choose a reason for hiding this comment

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

missing EOL 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants