-
-
Notifications
You must be signed in to change notification settings - Fork 719
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
16.0 mig stock reserve sale #1788
Conversation
…_reserve and stock_reserve_sale.
before running the tests. This fixes the failure experienced when running the test in --init mode rather than --update mode.
ed2b844
to
668f2c1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just initial install errors - functional review to come
has_stock_reservation = fields.Boolean( | ||
compute="_compute_stock_reservation", | ||
readonly=True, | ||
multi="stock_reservation", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
multi="stock_reservation", |
is_stock_reservable = fields.Boolean( | ||
compute="_compute_stock_reservation", | ||
readonly=True, | ||
multi="stock_reservation", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
multi="stock_reservation", |
string="Can Have Stock Reservations", | ||
) | ||
reserves_count = fields.Integer(compute="_compute_reserves_count") | ||
all_lines_reserved = fields.Boolean( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will need a compute_sudo
2023-08-14 21:04:18,128 1 WARNING mig odoo.modules.registry: sale.order: inconsistent 'compute_sudo' for computed fields: reserves_count, all_lines_reserved
<button | ||
name="%(action_sale_stock_reserve)d" | ||
type="action" | ||
icon="fa-lock" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2023-08-14 21:09:09,611 1 WARNING mig odoo.addons.base.models.ir_ui_view: A button with icon attribute (fa-lock) must have title in its tag, parents, descendants or have text
<button | ||
name="release_stock_reservation" | ||
type="object" | ||
icon="fa-undo" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2023-08-14 21:09:09,612 1 WARNING mig odoo.addons.base.models.ir_ui_view: A button with icon attribute (fa-undo) must have title in its tag, parents, descendants or have text
/ocabot migration stock_reserve_sale |
The migration issue (#1494) has not been updated to reference the current pull request because a previous pull request (#1695) is not closed. |
Thanks for the feedback @gdgellatly. In the afternoon, when I have some free time, I'll check it out!!! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we could improve these remark
line_ids = [line.id for order in self for line in order.order_line] | ||
lines = self.order_line.browse(line_ids) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello, thanks for your works.
I think we can optimize this function.
[EDIT 11/09] Adds @gdgellatly suggestion instead of self.mapped("order_line")
line_ids = [line.id for order in self for line in order.order_line] | |
lines = self.order_line.browse(line_ids) | |
lines = self.order_line |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not just self.order_line
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If self is a recordset, i don't remember if odoo gives all order_line for all record without a mapped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FrancoMaxime In v16, you can call the relation field on multi recordset
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rousseldenis Thanks
stock_picking = ( | ||
self.env["stock.picking"] | ||
.search([("origin", "=", self.name)]) | ||
.filtered(lambda a: a.state not in "cancel") | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make a search with both conditions.
stock_picking = ( | |
self.env["stock.picking"] | |
.search([("origin", "=", self.name)]) | |
.filtered(lambda a: a.state not in "cancel") | |
) | |
stock_picking = ( | |
self.env["stock.picking"] | |
.search([ | |
("origin", "=", self.name), | |
("state", "!=", "cancel") | |
]) | |
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally a != condition for a state field in search is a bad idea. The problem with these is often state fields are indexed and the other conditions aren't. But because of the generally bad distribution of states it is usually faster if you aren't using '=' for a state which is a small percentage, the queries can be quite bad. The search would have prefetched anyway, the cancelled records will be minimal. IDK, I feel like I would want testing on big databases first as one of the performance bottlenecks we always look for is exactly this suggestion. It is almost always better to reverse it to a state in type query or use filtered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just looking at this, the filtered clause is wrong anyway, it should be != instead of not in. But as it happens, in testing the suggested query on a database with 1m pickings performed basically identically as the original BEFORE the filtered clause and planned identically using standard Odoo indexes, so I think it is a good suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gdgellatly Thanks for your tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gdgellatly Have you tried with a condition like "state", "in", "[....]" ? Did they give the same result (in time)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't really matter I think. The existing code is clearly a mistake. But also the planner will have statistics on uniqueness and origin field is indexed so regardless, for this query, I think no matter the state condition, it will pick the origin index.
wh_routes = warehouse.route_ids | ||
wh_route_ids = [route.id for route in wh_routes] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can optimize this
wh_routes = warehouse.route_ids | |
wh_route_ids = [route.id for route in wh_routes] | |
wh_route_ids = warehouse.route_ids.ids |
reserv_ids = [reserv.id for line in self for reserv in line.reservation_ids] | ||
reservations = self.env["stock.reservation"].browse(reserv_ids) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can optimize
reserv_ids = [reserv.id for line in self for reserv in line.reservation_ids] | |
reservations = self.env["stock.reservation"].browse(reserv_ids) | |
reservations = self.mapped("reservation_ids") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why mapped? surely just self.reservation_ids
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment #1788 (comment)
stock_picking = ( | ||
self.env["stock.picking"] | ||
.search([("origin", "=", self.sale_id.name)]) | ||
.filtered(lambda a: a.state not in "cancel") | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can add both conditions in the search
stock_picking = ( | |
self.env["stock.picking"] | |
.search([("origin", "=", self.sale_id.name)]) | |
.filtered(lambda a: a.state not in "cancel") | |
) | |
stock_picking = ( | |
self.env["stock.picking"] | |
.search([ | |
("origin", "=", self.sale_id.name), | |
("state", "!=", "cancel"), | |
]) | |
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, I think a suggestion like this which could have big performance implications should at least be tested. I can do it, just not right now.
len(line.reservation_ids) > 0 or line.order_id.state != "draft" | ||
) | ||
|
||
reservation_ids = fields.One2many( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you put fields on top ?
for line in self: | ||
if not line.reservation_ids: | ||
continue | ||
raise except_orm( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
except_orm has been deprecated a long time ago. Could you change to UserError ?
@@ -10,10 +10,26 @@ class StockReservation(models.Model): | |||
"sale.order.line", string="Sale Order Line", ondelete="cascade", copy=False | |||
) | |||
sale_id = fields.Many2one( | |||
"sale.order", string="Sale Order", related="sale_line_id.order_id" | |||
"sale.order", string="Sale Order", store=True, related="sale_line_id.order_id" | |||
) | |||
|
|||
def release_reserve(self): | |||
for rec in self: | |||
rec.sale_line_id = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.update({"sale_line_id": False})
line_ids = [line.id for order in self for line in order.order_line] | ||
lines = self.order_line.browse(line_ids) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FrancoMaxime In v16, you can call the relation field on multi recordset
) | ||
for order in self: | ||
if reserve_ids: | ||
order.reserves_count = reserve_ids[0]["sale_id_count"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@HugoCordobaLeal Is that correct to take only the first one ???
@HugoCordobaLeal are you working on this? If you want I can help on this to implement the last change requests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this migration 😃
@api.model | ||
def _default_location_id(self): | ||
return self.env["stock.reservation"].get_location_from_ref( | ||
"stock.stock_location_stock" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That'll work only if you're connected using the default company (ID 1).
Should be something like:
@api.model
def _default_location_id(self):
domain = [
'|',
('company_id', '=', self.env.company.id),
('company_id', '=', False),
]
return self.env["stock.warehouse"].search(domain, limit=1).lot_stock_id
|
||
if active_model == "sale.order": | ||
sales = env["sale.order"].browse(active_ids) | ||
line_ids = [line.id for sale in sales for line in sale.order_line] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplest way to read that:
line_ids = sales.order_line.ids
|
||
def _compute_reserves_count(self): | ||
reserve_ids = self.env["stock.reservation"]._read_group( | ||
domain=expression.AND( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useless.
domain=[("sale_id", "in", self.ids)]
StockRule = self.env["stock.rule"] | ||
product = self.product_id | ||
product_route_ids = [ | ||
x.id for x in product.route_ids + product.categ_id.total_route_ids |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More readable:
product_route_ids = (product.route_ids + product.categ_id.total_route_ids).ids
("route_id", "in", wh_route_ids), | ||
] | ||
rules = StockRule.search(domain, order="route_sequence, sequence") | ||
if rules: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This if
section should be inside of the previous if
section.
Because you already have a limit=1
.
And this is useless: you should add a limit=1
in the search(...)
on the line before.
rules = StockRule.search(domain, order="route_sequence, sequence") | ||
if rules: | ||
fields.first(rules) | ||
return False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should return a rule (_get_line_rule(...)
) but return False
.
So you're looking for a rule but you never return
it.
And if you really don't find a rule, you should return an empty recordset.
is_readonly = fields.Boolean(compute="_compute_is_readonly", store=False) | ||
|
||
def release_stock_reservation(self): | ||
reserv_ids = [reserv.id for line in self for reserv in line.reservation_ids] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More readable:
reserv_ids = self.reservation_ids.ids
But it's completely useless as you browse them just after.
You should do this:
reservations = self.reservation_ids
order.reserves_count = reserve_ids[0]["sale_id_count"] | ||
else: | ||
order.reserves_count = 0 | ||
if order.reserves_count == len(lines): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this possible?
That can not happens in a multi-recordset computation.
If this compute is called on a recordset with more than one sale.order
, this will be never True.
stock_picking = ( | ||
self.env["stock.picking"] | ||
.search([("origin", "=", self.name)]) | ||
.filtered(lambda a: a.state not in "cancel") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct at all.
The value of state could be "draft", "cancel",...
As is, this mean:
"draft" in ("c", "a", "n", "c", "e", "l")
? => True/False
It should be:
# Use limit=1 because we use the record with .id later
stock_picking = self.env["stock.picking"].search([("origin", "=", self.name), ("state", "!=", "cancel")], limit=1)
stock_picking = ( | ||
self.env["stock.picking"] | ||
.search([("origin", "=", self.sale_id.name)]) | ||
.filtered(lambda a: a.state not in "cancel") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct. Like my previous comment
if stock_picking: | ||
view_id = self.env.ref("stock.view_picking_form").id | ||
action.update(views=[(view_id, "form")], res_id=stock_picking.id) | ||
return action |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be outstide of this if
section.
@username-hugo Do you plan to attend comments ? |
@username-hugo |
I'm working on the migration of this module to V17: #2004 |
There hasn't been any activity on this pull request in the past 4 months, so it has been marked as stale and it will be closed automatically if no further activity occurs in the next 30 days. |
[MIG] stock_reserve_sale: migrate from v13.0 to v16.0
Hi.
After trying to push the developments in version 14 and version 16, and receiving no feedback, I have decided to create a PR and take responsibility for the migration.
I am open to any kind of suggestions.
I have included improvements in the module that I find useful and improve the module at user and usability level.
We will find a button to access the reservations associated to the order and the reservation slip. They will only be visible if the quotation has reservations.
Within the reservations, we will also be able to access the associated reservation delivery note, the reservation movement and even release (undo) the reservation.
A button is added in the header, along with the rest of the buttons, to cancel the reservation made. When pressed, it will cancel the reservation movements and the reservation delivery note, so the corresponding smart buttons will no longer be visible.
The button to reserve stock will only be visible if there are units to be reserved. If all units have already been reserved, the button will disappear.