Select for update moves used to compute assignation
As a concurrent transaction could update a moves after waiting for the release of the table lock, we must take a lock on each rows used for the computation. issue6134 review25921002
This commit is contained in:
@@ -7,7 +7,7 @@ from functools import partial
|
||||
from collections import OrderedDict, defaultdict
|
||||
from itertools import groupby
|
||||
|
||||
from sql import Literal, Union, Column, Null
|
||||
from sql import Literal, Union, Column, Null, For
|
||||
from sql.aggregate import Sum
|
||||
from sql.conditionals import Coalesce
|
||||
from sql.operators import Concat
|
||||
@@ -778,8 +778,10 @@ class Move(Workflow, ModelSQL, ModelView):
|
||||
Uom = pool.get('product.uom')
|
||||
Date = pool.get('ir.date')
|
||||
Location = pool.get('stock.location')
|
||||
|
||||
Transaction().database.lock(Transaction().connection, cls._table)
|
||||
Period = pool.get('stock.period')
|
||||
transaction = Transaction()
|
||||
database = transaction.database
|
||||
connection = transaction.connection
|
||||
|
||||
if with_childs:
|
||||
locations = Location.search([
|
||||
@@ -788,12 +790,42 @@ class Move(Workflow, ModelSQL, ModelView):
|
||||
])
|
||||
else:
|
||||
locations = list(set((m.from_location for m in moves)))
|
||||
location_ids = [l.id for l in locations]
|
||||
product_ids = list(set((m.product.id for m in moves)))
|
||||
stock_date_end = Date.today()
|
||||
|
||||
if database.has_select_for():
|
||||
table = cls.__table__()
|
||||
query = table.select(Literal(1),
|
||||
where=(table.to_location.in_(location_ids)
|
||||
| table.from_location.in_(location_ids))
|
||||
& table.product.in_(product_ids),
|
||||
for_=For('UPDATE', nowait=True))
|
||||
|
||||
PeriodCache = Period.get_cache(grouping)
|
||||
if PeriodCache:
|
||||
periods = Period.search([
|
||||
('date', '<', stock_date_end),
|
||||
('state', '=', 'closed'),
|
||||
], order=[('date', 'DESC')], limit=1)
|
||||
if periods:
|
||||
period, = periods
|
||||
query.where &= Coalesce(
|
||||
table.effective_date,
|
||||
table.planned_date,
|
||||
datetime.date.max) > period.date
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(*query)
|
||||
else:
|
||||
database.lock(connection, cls._table)
|
||||
|
||||
with Transaction().set_context(
|
||||
stock_date_end=Date.today(),
|
||||
stock_date_end=stock_date_end,
|
||||
stock_assign=True):
|
||||
pbl = Product.products_by_location(
|
||||
location_ids=[l.id for l in locations],
|
||||
product_ids=[m.product.id for m in moves],
|
||||
location_ids=location_ids,
|
||||
product_ids=product_ids,
|
||||
grouping=grouping)
|
||||
|
||||
def get_key(move, location):
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from itertools import chain
|
||||
|
||||
from sql import Literal, For
|
||||
|
||||
from trytond.model import Workflow, ModelView, ModelSQL, fields
|
||||
from trytond.pyson import Eval, If
|
||||
from trytond.transaction import Transaction
|
||||
@@ -93,6 +96,20 @@ class Period(Workflow, ModelSQL, ModelView):
|
||||
Location = pool.get('stock.location')
|
||||
Move = pool.get('stock.move')
|
||||
Date = pool.get('ir.date')
|
||||
transaction = Transaction()
|
||||
connection = transaction.connection
|
||||
database = transaction.database
|
||||
|
||||
# XXX: A move in the period could be inserted before the lock
|
||||
# from a different transaction. It will not be taken in the pbl
|
||||
# computation but it is quite rare because only past periods are
|
||||
# closed.
|
||||
database.lock(connection, Move._table)
|
||||
if database.has_select_for():
|
||||
move = Move.__table__()
|
||||
query = move.select(Literal(1), for_=For('UPDATE', nowait=True))
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(*query)
|
||||
|
||||
locations = Location.search([
|
||||
('type', 'not in', ['warehouse', 'view']),
|
||||
|
||||
Reference in New Issue
Block a user