diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index 3ba6dcb1399..b90f563c354 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -306,7 +306,7 @@ class AccountBankStatement(models.Model):
#try to assign partner to bank_statement_line
stl_to_assign = st_lines_left.filtered(lambda stl: not stl.partner_id)
- refs = {stl_to_assign.mapped('name')}
+ refs = set(stl_to_assign.mapped('name'))
if stl_to_assign and refs\
and st_lines_left[0].journal_id.default_credit_account_id\
and st_lines_left[0].journal_id.default_debit_account_id:
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index db70e42b8f9..787b829816d 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -1653,17 +1653,20 @@ class AccountPartialReconcile(models.Model):
return self
#check if the reconcilation is full
#first, gather all journal items involved in the reconciliation just created
- partial_rec_set = OrderedDict.fromkeys([x for x in self])
aml_set = aml_to_balance = self.env['account.move.line']
total_debit = 0
total_credit = 0
total_amount_currency = 0
#make sure that all partial reconciliations share the same secondary currency otherwise it's not
#possible to compute the exchange difference entry and it has to be done manually.
- currency = list(partial_rec_set)[0].currency_id
+ currency = self[0].currency_id
maxdate = '0000-00-00'
- for partial_rec in partial_rec_set:
+ seen = set()
+ todo = set(self)
+ while todo:
+ partial_rec = todo.pop()
+ seen.add(partial_rec)
if partial_rec.currency_id != currency:
#no exchange rate entry will be created
currency = None
@@ -1682,13 +1685,15 @@ class AccountPartialReconcile(models.Model):
#currency is recorded on the partial rec and in order to check if the reconciliation is total, we need to convert the
#aml.balance in that foreign currency
total_amount_currency += aml.company_id.currency_id.with_context(date=aml.date).compute(aml.balance, partial_rec.currency_id)
+
for x in aml.matched_debit_ids | aml.matched_credit_ids:
- partial_rec_set[x] = None
- partial_rec_ids = [x.id for x in partial_rec_set]
+ if x not in seen:
+ todo.add(x)
+
+ partial_rec_ids = [x.id for x in seen]
aml_ids = aml_set.ids
#then, if the total debit and credit are equal, or the total amount in currency is 0, the reconciliation is full
digits_rounding_precision = aml_set[0].company_id.currency_id.rounding
-
if (currency and float_is_zero(total_amount_currency, precision_rounding=currency.rounding)) or float_compare(total_debit, total_credit, precision_rounding=digits_rounding_precision) == 0:
exchange_move_id = False
if currency and aml_to_balance:
diff --git a/addons/account/views/account_invoice_view.xml b/addons/account/views/account_invoice_view.xml
index d73af20294d..f924ad4ef29 100644
--- a/addons/account/views/account_invoice_view.xml
+++ b/addons/account/views/account_invoice_view.xml
@@ -238,7 +238,7 @@
-
+
@@ -378,7 +378,7 @@
-
+
diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py
index 5493e5928bc..99a7872a5e4 100644
--- a/addons/mrp/models/mrp_workorder.py
+++ b/addons/mrp/models/mrp_workorder.py
@@ -196,7 +196,7 @@ class MrpWorkorder(models.Model):
move_lots[0].qty_done = new_qty
elif move.product_id.tracking == 'serial':
# Create extra pseudo record
- qty_todo = new_qty - sum(move_lots.mapped('quantity'))
+ qty_todo = new_qty - sum(move_lots.mapped('qty_done'))
if float_compare(qty_todo, 0.0, precision_rounding=move.product_uom.rounding) > 0:
while float_compare(qty_todo, 0.0, precision_rounding=move.product_uom.rounding) > 0:
self.active_move_line_ids += self.env['stock.move.line'].new({
@@ -217,8 +217,8 @@ class MrpWorkorder(models.Model):
for move_lot in move_lots:
if qty_todo <= 0:
break
- if not move_lot.lot_id and qty_todo >= move_lot.quantity:
- qty_todo = qty_todo - move_lot.quantity
+ if not move_lot.lot_id and qty_todo >= move_lot.qty_done:
+ qty_todo = qty_todo - move_lot.qty_done
self.active_move_line_ids -= move_lot # Difference operator
else:
#move_lot.product_qty = move_lot.product_qty - qty_todo
diff --git a/addons/mrp/views/mrp_workorder_views.xml b/addons/mrp/views/mrp_workorder_views.xml
index 58856092cb9..6276cacc5f5 100644
--- a/addons/mrp/views/mrp_workorder_views.xml
+++ b/addons/mrp/views/mrp_workorder_views.xml
@@ -152,6 +152,9 @@
+
+
+
diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py
index 6334dfc6b5c..3d10872c728 100644
--- a/addons/purchase/models/purchase.py
+++ b/addons/purchase/models/purchase.py
@@ -556,7 +556,7 @@ class PurchaseOrderLine(models.Model):
qty -= inv_line.uom_id._compute_quantity(inv_line.quantity, line.product_uom)
line.qty_invoiced = qty
- @api.depends('order_id.state', 'move_ids.state')
+ @api.depends('order_id.state', 'move_ids.state', 'move_ids.product_uom_qty')
def _compute_qty_received(self):
for line in self:
if line.order_id.state not in ['purchase', 'done']:
diff --git a/addons/sale_stock/models/stock.py b/addons/sale_stock/models/stock.py
index 2db872ad8c7..045a8f0498c 100644
--- a/addons/sale_stock/models/stock.py
+++ b/addons/sale_stock/models/stock.py
@@ -36,6 +36,17 @@ class StockMove(models.Model):
subtype_id=self.env.ref('mail.mt_note').id)
return result
+ @api.multi
+ def write(self, vals):
+ res = super(StockMove, self).write(vals)
+ if 'product_uom_qty' in vals:
+ for move in self:
+ if move.state == 'done':
+ sale_order_lines = self.filtered(lambda move: move.procurement_id.sale_line_id and move.product_id.expense_policy == 'no').mapped('procurement_id.sale_line_id')
+ for line in sale_order_lines:
+ line.qty_delivered = line._get_delivered_qty()
+ return res
+
class StockPicking(models.Model):
_inherit = 'stock.picking'
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index 6379289bfe6..c29a941e3bb 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -741,9 +741,13 @@ class StockMove(models.Model):
# the reserved quantity on the quants, convert it here in
# `product_id.uom_id` (the UOM of the quants is the UOM of the product).
move_lines_in = move.move_orig_ids.filtered(lambda m: m.state == 'done').mapped('move_line_ids')
- keys_in = ['location_dest_id', 'lot_id', 'result_package_id', 'owner_id']
+ keys_in_groupby = ['location_dest_id', 'lot_id', 'result_package_id', 'owner_id']
+
+ def _keys_in_sorted(ml):
+ return (ml.location_dest_id.id, ml.lot_id.id, ml.result_package_id.id, ml.owner_id.id)
+
grouped_move_lines_in = {}
- for k, g in groupby(sorted(move_lines_in, key=itemgetter(*keys_in)), key=itemgetter(*keys_in)):
+ for k, g in groupby(sorted(move_lines_in, key=_keys_in_sorted), key=itemgetter(*keys_in_groupby)):
qty_done = 0
for ml in g:
qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
@@ -757,14 +761,18 @@ class StockMove(models.Model):
moves_out_siblings_to_consider = moves_out_siblings & (assigned_moves + partially_available_moves)
reserved_moves_out_siblings = moves_out_siblings.filtered(lambda m: m.state in ['partially_available', 'assigned'])
move_lines_out_reserved = (reserved_moves_out_siblings | moves_out_siblings_to_consider).mapped('move_line_ids')
- keys_out = ['location_id', 'lot_id', 'package_id', 'owner_id']
+ keys_out_groupby = ['location_id', 'lot_id', 'package_id', 'owner_id']
+
+ def _keys_out_sorted(ml):
+ return (ml.location_id.id, ml.lot_id.id, ml.package_id.id, ml.owner_id.id)
+
grouped_move_lines_out = {}
- for k, g in groupby(sorted(move_lines_out_done, key=itemgetter(*keys_out)), key=itemgetter(*keys_out)):
+ for k, g in groupby(sorted(move_lines_out_done, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)):
qty_done = 0
for ml in g:
qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
grouped_move_lines_out[k] = qty_done
- for k, g in groupby(sorted(move_lines_out_reserved, key=itemgetter(*keys_out)), key=itemgetter(*keys_out)):
+ for k, g in groupby(sorted(move_lines_out_reserved, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)):
grouped_move_lines_out[k] = sum(self.env['stock.move.line'].concat(*list(g)).mapped('product_qty'))
available_move_lines = {key: grouped_move_lines_in[key] - grouped_move_lines_out.get(key, 0) for key in grouped_move_lines_in.keys()}
# pop key if the quantity available amount to 0
@@ -983,18 +991,3 @@ class StockMove(models.Model):
move.state = 'waiting'
else:
move.state = 'confirmed'
-
- @api.multi
- def action_show_picking(self):
- view = self.env.ref('stock.view_picking_form')
- return {
- 'name': _('Transfer'),
- 'type': 'ir.actions.act_window',
- 'view_type': 'form',
- 'view_mode': 'form',
- 'res_model': 'stock.picking',
- 'views': [(view.id, 'form')],
- 'view_id': view.id,
- 'target': 'new',
- 'res_id': self.id}
- show_picking = action_show_picking
diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py
index 124f67ef6e2..1050d60902e 100644
--- a/addons/stock/models/stock_move_line.py
+++ b/addons/stock/models/stock_move_line.py
@@ -230,6 +230,14 @@ class StockMoveLine(models.Model):
# Unreserve and reserve following move in order to have the real reserved quantity on move_line.
next_moves |= ml.move_id.move_dest_ids.filtered(lambda move: move.state not in ('done', 'cancel'))
res = super(StockMoveLine, self).write(vals)
+
+ # As stock_account values according to a move's `product_uom_qty`, we consider that any
+ # done stock move should have its `quantity_done` equals to its `product_uom_qty`, and
+ # this is what move's `action_done` will do. So, we replicate the behavior here.
+ if updates or 'qty_done' in vals:
+ moves = self.filtered(lambda ml: ml.move_id.state == 'done').mapped('move_id')
+ for move in moves:
+ move.product_uom_qty = move.quantity_done
next_moves.do_unreserve()
next_moves.action_assign()
return res
diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py
index 338208e48e3..22ae95448e4 100644
--- a/addons/stock/tests/test_move.py
+++ b/addons/stock/tests/test_move.py
@@ -2016,6 +2016,7 @@ class StockMove(TransactionCase):
move1.move_line_ids.qty_done = 1
move1.action_done()
+ self.assertEqual(move1.product_uom_qty, 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
@@ -2024,6 +2025,7 @@ class StockMove(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), -1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), -1.0)
+ self.assertEqual(move1.product_uom_qty, 2.0)
def test_edit_done_move_line_9(self):
""" Test that editing a done stock move line linked to an untracked product correctly and
@@ -2052,12 +2054,14 @@ class StockMove(TransactionCase):
move1.move_line_ids.qty_done = 1
move1.action_done()
+ self.assertEqual(move1.product_uom_qty, 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
# edit once done, we actually moved 2 products
move1.move_line_ids.qty_done = 0
+ self.assertEqual(move1.product_uom_qty, 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 1.0)
@@ -2087,6 +2091,7 @@ class StockMove(TransactionCase):
quant = self.env['stock.quant']._gather(self.product1, self.stock_location)
self.assertEqual(len(quant), 0.0)
+ self.assertEqual(move1.product_uom_qty, 0.0)
def test_edit_done_move_line_11(self):
""" Add a move line and check if the quant is updated
@@ -2112,6 +2117,7 @@ class StockMove(TransactionCase):
picking.action_assign()
move1.move_line_ids.qty_done = 10
picking.action_done()
+ self.assertEqual(move1.product_uom_qty, 10.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 10.0)
self.env['stock.move.line'].create({
'picking_id': move1.move_line_ids.picking_id.id,
@@ -2124,6 +2130,7 @@ class StockMove(TransactionCase):
})
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 20.0)
move1.move_line_ids[1].qty_done = 5
+ self.assertEqual(move1.product_uom_qty, 15.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 15.0)
def test_immediate_validate_1(self):
diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml
index ac9c856bdb3..30ec38c8f79 100644
--- a/addons/stock/views/stock_picking_views.xml
+++ b/addons/stock/views/stock_picking_views.xml
@@ -257,19 +257,16 @@
-
-
-
-
-
-
+
+
+
+
-
+
-