diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index 09ec5256593..32cbefa051a 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -101,7 +101,7 @@ class AccountBankStatement(models.Model):
journals = self.env['account.journal'].search([('type', '=', journal_type), ('company_id', '=', company_id)])
if journals:
return journals[0]
- return False
+ return self.env['account.journal']
@api.multi
def _get_opening_balance(self, journal_id):
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 8ddfdfe1107..106579c7195 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -526,7 +526,7 @@ class AccountInvoice(models.Model):
report_invoice = self.env['report']._get_report_from_name('account.report_invoice')
except IndexError:
report_invoice = False
- if report_invoice:
+ if report_invoice and report_invoice.attachment:
for invoice in self:
with invoice.env.do_in_draft():
invoice.number, invoice.state = invoice.move_name, 'open'
diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py
index b7c1f13cf6b..cd3d04b043d 100644
--- a/addons/auth_oauth/controllers/main.py
+++ b/addons/auth_oauth/controllers/main.py
@@ -119,8 +119,6 @@ class OAuthLogin(Home):
@http.route()
def web_auth_reset_password(self, *args, **kw):
providers = self.list_providers()
- if len(providers) == 1:
- werkzeug.exceptions.abort(werkzeug.utils.redirect(providers[0]['auth_link'], 303))
response = super(OAuthLogin, self).web_auth_reset_password(*args, **kw)
response.qcontext.update(providers=providers)
return response
diff --git a/addons/l10n_be_intrastat/wizard/xml_decl.py b/addons/l10n_be_intrastat/wizard/xml_decl.py
index 2771e8b63cd..35151da426d 100644
--- a/addons/l10n_be_intrastat/wizard/xml_decl.py
+++ b/addons/l10n_be_intrastat/wizard/xml_decl.py
@@ -316,8 +316,10 @@ class xml_decl(osv.TransientModel):
numlgn = 0
for linekey in entries:
- numlgn += 1
amounts = entries[linekey]
+ if round(amounts[0], 0) == 0:
+ continue
+ numlgn += 1
item = ET.SubElement(datas, 'Item')
self._set_Dim(item, 'EXSEQCODE', unicode(numlgn))
self._set_Dim(item, 'EXTRF', unicode(linekey.EXTRF))
diff --git a/addons/project/models/project.py b/addons/project/models/project.py
index b645f7fa867..b14f0a60db4 100644
--- a/addons/project/models/project.py
+++ b/addons/project/models/project.py
@@ -294,7 +294,10 @@ class Task(models.Model):
def _get_default_stage_id(self):
""" Gives default stage_id """
- return self.stage_find(self.env.context.get('default_project_id'), [('fold', '=', False)])
+ project_id = self.env.context.get('default_project_id')
+ if not project_id:
+ return False
+ return self.stage_find(project_id, [('fold', '=', False)])
@api.multi
def _read_group_stage_ids(self, domain, read_group_order=None, access_rights_uid=None):
@@ -390,6 +393,10 @@ class Task(models.Model):
def _onchange_project(self):
if self.project_id:
self.partner_id = self.project_id.partner_id
+ self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)])
+ else:
+ self.partner_id = False
+ self.stage_id = False
@api.onchange('user_id')
def _onchange_user(self):
diff --git a/addons/project_issue/models/project_issue.py b/addons/project_issue/models/project_issue.py
index 66bed3ea24e..e93dc54688b 100644
--- a/addons/project_issue/models/project_issue.py
+++ b/addons/project_issue/models/project_issue.py
@@ -15,7 +15,10 @@ class ProjectIssue(models.Model):
@api.model
def _get_default_stage_id(self):
- return self.stage_find(self.env.context.get('default_project_id'), [('fold', '=', False)])
+ project_id = self.env.context.get('default_project_id')
+ if not project_id:
+ return False
+ return self.stage_find(project_id, [('fold', '=', False)])
name = fields.Char(string='Issue', required=True)
active = fields.Boolean(default=True)
@@ -150,6 +153,11 @@ class ProjectIssue(models.Model):
if self.project_id:
self.partner_id = self.project_id.partner_id.id
self.email_from = self.project_id.partner_id.email
+ self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)])
+ else:
+ self.partner_id = False
+ self.email_from = False
+ self.stage_id = False
@api.onchange('task_id')
def _onchange_task_id(self):
diff --git a/addons/purchase/invoice.py b/addons/purchase/invoice.py
index 1871117a07b..c3e45321371 100644
--- a/addons/purchase/invoice.py
+++ b/addons/purchase/invoice.py
@@ -160,13 +160,14 @@ class AccountInvoice(models.Model):
#line['tax_ids'] is like [(4, tax_id, None), (4, tax_id2, None)...]
taxes = self.env['account.tax'].browse([x[1] for x in line['tax_ids']])
price_unit = taxes.compute_all(price_unit, currency=inv.currency_id, quantity=1.0)['total_excluded']
+ price_before = line.get('price', 0.0)
line.update({'price': round(valuation_price_unit * line['quantity'], account_prec)})
diff_res.append({
'type': 'src',
'name': i_line.name[:64],
'price_unit': round(price_unit - valuation_price_unit, account_prec),
'quantity': line['quantity'],
- 'price': round((price_unit - valuation_price_unit) * line['quantity'], account_prec),
+ 'price': round(price_before - line.get('price', 0.0), account_prec),
'account_id': acc,
'product_id': line['product_id'],
'uom_id': line['uom_id'],
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index a8cee106183..f8e2c07cde1 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -74,12 +74,15 @@ class PurchaseOrder(models.Model):
types = type_obj.search([('code', '=', 'incoming'), ('warehouse_id', '=', False)])
return types[:1]
- @api.depends('order_line.move_ids.picking_id')
+ @api.depends('order_line.move_ids')
def _compute_picking(self):
for order in self:
pickings = self.env['stock.picking']
for line in order.order_line:
- moves = line.move_ids.filtered(lambda r: r.state != 'cancel')
+ # We keep a limited scope on purpose. Ideally, we should also use move_orig_ids and
+ # do some recursive search, but that could be prohibitive if not done correctly.
+ moves = line.move_ids | line.move_ids.mapped('returned_move_ids')
+ moves = moves.filtered(lambda r: r.state != 'cancel')
pickings |= moves.mapped('picking_id')
order.picking_ids = pickings
order.picking_count = len(pickings)
diff --git a/addons/sale_timesheet/models/sale_service.py b/addons/sale_timesheet/models/sale_service.py
index a60ed3a8167..d34b55f4708 100644
--- a/addons/sale_timesheet/models/sale_service.py
+++ b/addons/sale_timesheet/models/sale_service.py
@@ -29,6 +29,16 @@ class procurement_order(osv.osv):
def _run(self, cr, uid, ids, context=None):
procurement = self.browse(cr, uid, ids[0], context=context)
if procurement._is_procurement_task() and not procurement.task_id:
+ # If the SO was confirmed, cancelled, set to draft then confirmed, avoid creating a new
+ # task.
+ if procurement.sale_line_id:
+ existing_task = self.pool['project.task'].search(
+ cr, uid, [('sale_line_id', '=', procurement.sale_line_id.id)],
+ context=context
+ )
+ if existing_task:
+ return existing_task
+
#create a task for the procurement
return self._create_service_task(cr, uid, procurement, context=context)
return super(procurement_order, self)._run(cr, uid, ids, context=context)
diff --git a/addons/stock/models/stock_pack_operation.py b/addons/stock/models/stock_pack_operation.py
index a8cf2aa3357..c306eac1dae 100644
--- a/addons/stock/models/stock_pack_operation.py
+++ b/addons/stock/models/stock_pack_operation.py
@@ -77,9 +77,9 @@ class PackOperation(models.Model):
def _set_is_done(self):
# TDE FIXME: whuuuut ???
if not self.product_id:
- if self.processed_boolean and self.qty_done == 0:
+ if self.is_done and self.qty_done == 0:
self.qty_done = 1.0
- if not self.processed_boolean and self.qty_done != 0:
+ if not self.is_done and self.qty_done != 0:
self.qty_done = 0.0
def _get_remaining_prod_quantities(self):
diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py
index f9ece848bd2..5c86fd93d77 100644
--- a/addons/stock/models/stock_quant.py
+++ b/addons/stock/models/stock_quant.py
@@ -177,11 +177,13 @@ class Quant(models.Model):
quants_reconcile_sudo = self.env['stock.quant'].sudo()
quants_move_sudo = self.env['stock.quant'].sudo()
+ check_lot = False
for quant, qty in quants:
if not quant:
#If quant is None, we will create a quant to move (and potentially a negative counterpart too)
quant = self._quant_create_from_move(
qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location_from=location_from, force_location_to=location_to)
+ check_lot = True
else:
quant._quant_split(qty)
quants_move_sudo |= quant
@@ -201,6 +203,24 @@ class Quant(models.Model):
if self._cr.fetchone():
quants_reconcile_sudo._quant_reconcile_negative(move)
+ # In case of serial tracking, check if the product does not exist somewhere internally already
+ # Checking that a positive quant already exists in an internal location is too restrictive.
+ # Indeed, if a warehouse is configured with several steps (e.g. "Pick + Pack + Ship") and
+ # one step is forced (creates a quant of qty = -1.0), it is not possible afterwards to
+ # correct the inventory unless the product leaves the stock.
+ picking_type = move.picking_id and move.picking_id.picking_type_id or False
+ if check_lot and lot_id and move.product_id.tracking == 'serial' and (not picking_type or (picking_type.use_create_lots or picking_type.use_existing_lots)):
+ other_quants = self.search([('product_id', '=', move.product_id.id), ('lot_id', '=', lot_id),
+ ('qty', '>', 0.0), ('location_id.usage', '=', 'internal')])
+ if other_quants:
+ # We raise an error if:
+ # - the total quantity is strictly larger than 1.0
+ # - there are more than one negative quant, to avoid situations where the user would
+ # force the quantity at several steps of the process
+ if sum(other_quants.mapped('qty')) > 1.0 or len([q for q in other_quants.mapped('qty') if q < 0]) > 1:
+ lot_name = self.env['stock.production.lot'].browse(lot_id).name
+ raise UserError(_('The serial number %s is already in stock.') % lot_name + _("Otherwise make sure the right stock/owner is set."))
+
@api.model
def _quant_create_from_move(self, qty, move, lot_id=False, owner_id=False,
src_package_id=False, dest_package_id=False,
@@ -234,16 +254,10 @@ class Quant(models.Model):
negative_quant_id = self.sudo().create(negative_vals)
vals.update({'propagated_from_id': negative_quant_id.id})
- # In case of serial tracking, check if the product does not exist somewhere internally already
picking_type = move.picking_id and move.picking_id.picking_type_id or False
if lot_id and move.product_id.tracking == 'serial' and (not picking_type or (picking_type.use_create_lots or picking_type.use_existing_lots)):
if qty != 1.0:
raise UserError(_('You should only receive by the piece with the same serial number'))
- other_quants = self.search([('product_id', '=', move.product_id.id), ('lot_id', '=', lot_id),
- ('qty', '>', 0.0), ('location_id.usage', '=', 'internal')])
- if other_quants:
- lot_name = self.env['stock.production.lot'].browse(lot_id).name
- raise UserError(_('The serial number %s is already in stock.') % lot_name + _("Otherwise make sure the right stock/owner is set."))
# create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
return self.sudo().create(vals)
@@ -423,7 +437,9 @@ class Quant(models.Model):
if pack_operation_id:
pack_operation = self.env['stock.pack.operation'].browse(pack_operation_id)
- domain += [('owner_id', '=', pack_operation.owner_id.id), ('location_id', '=', pack_operation.location_id.id)]
+ domain += [('location_id', '=', pack_operation.location_id.id)]
+ if pack_operation.owner_id:
+ domain += [('owner_id', '=', pack_operation.owner_id.id)]
if pack_operation.package_id and not pack_operation.product_id:
domain += [('package_id', 'child_of', pack_operation.package_id.id)]
elif pack_operation.package_id and pack_operation.product_id:
@@ -431,7 +447,9 @@ class Quant(models.Model):
else:
domain += [('package_id', '=', False)]
else:
- domain += [('owner_id', '=', move.restrict_partner_id.id), ('location_id', 'child_of', move.location_id.id)]
+ domain += [('location_id', 'child_of', move.location_id.id)]
+ if move.restrict_partner_id:
+ domain += [('owner_id', '=', move.restrict_partner_id.id)]
if company_id:
domain += [('company_id', '=', company_id)]
diff --git a/addons/stock/report/report_stockpicking_operations.xml b/addons/stock/report/report_stockpicking_operations.xml
index bd77df3eaf4..891d3b92942 100644
--- a/addons/stock/report/report_stockpicking_operations.xml
+++ b/addons/stock/report/report_stockpicking_operations.xml
@@ -80,13 +80,14 @@
| Product | Quantity | Source | + | Barcode | @@ -108,7 +109,7 @@ - | + |
-
+
|
|---|