diff --git a/addons/account/static/src/js/reconciliation/reconciliation_action.js b/addons/account/static/src/js/reconciliation/reconciliation_action.js
index 1cd47c70a9c..bd3e17fd4cd 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_action.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_action.js
@@ -305,9 +305,12 @@ var StatementAction = AbstractAction.extend(ControlPanelMixin, {
'context': self.model.getContext(),
});
_.each(result.handles, function (handle) {
- self._getWidget(handle).destroy();
- var index = _.findIndex(self.widgets, function (widget) {return widget.handle===handle;});
- self.widgets.splice(index, 1);
+ var widget = self._getWidget(handle);
+ if (widget) {
+ widget.destroy();
+ var index = _.findIndex(self.widgets, function (widget) {return widget.handle===handle;});
+ self.widgets.splice(index, 1);
+ }
});
// Get number of widget and if less than constant and if there are more to laod, load until constant
if (self.widgets.length < self.model.defaultDisplayQty
diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py
index 52a81fc8723..e3e2bc99d2f 100644
--- a/addons/hr_expense/models/hr_expense.py
+++ b/addons/hr_expense/models/hr_expense.py
@@ -207,6 +207,25 @@ class HrExpense(models.Model):
# Business
# ----------------------------------------
+ @api.multi
+ def _prepare_move_values(self):
+ """
+ This function prepares move values related to an expense
+ """
+ self.ensure_one()
+ journal = self.sheet_id.bank_journal_id if self.payment_mode == 'company_account' else self.sheet_id.journal_id
+ account_date = self.sheet_id.accounting_date or self.date
+ move_values = {
+ 'journal_id': journal.id,
+ 'company_id': self.env.user.company_id.id,
+ 'date': account_date,
+ 'ref': self.sheet_id.name,
+ # force the name to the default value, to avoid an eventual 'default_name' in the context
+ # to set it to '' which cause no number to be given to the account.move when posted.
+ 'name': '/',
+ }
+ return move_values
+
@api.multi
def _get_account_move_by_sheet(self):
""" Return a mapping between the expense sheet of current expense and its account move
@@ -215,18 +234,8 @@ class HrExpense(models.Model):
move_grouped_by_sheet = {}
for expense in self:
# create the move that will contain the accounting entries
- account_date = expense.sheet_id.accounting_date or expense.date
if expense.sheet_id.id not in move_grouped_by_sheet:
- journal = expense.sheet_id.bank_journal_id if expense.payment_mode == 'company_account' else expense.sheet_id.journal_id
- move = self.env['account.move'].create({
- 'journal_id': journal.id,
- 'company_id': self.env.user.company_id.id,
- 'date': account_date,
- 'ref': expense.sheet_id.name,
- # force the name to the default value, to avoid an eventual 'default_name' in the context
- # to set it to '' which cause no number to be given to the account.move when posted.
- 'name': '/',
- })
+ move = self.env['account.move'].create(expense._prepare_move_values())
move_grouped_by_sheet[expense.sheet_id.id] = move
else:
move = move_grouped_by_sheet[expense.sheet_id.id]
diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py
index fd69b44d222..92e4c2c4463 100644
--- a/addons/l10n_fr_fec/wizard/account_fr_fec.py
+++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py
@@ -305,7 +305,7 @@ class AccountFrFec(models.TransientModel):
END
AS PieceRef,
TO_CHAR(am.date, 'YYYYMMDD') AS PieceDate,
- CASE WHEN aml.name IS NULL THEN '/' ELSE replace(replace(aml.name, '|', '/'), '\t', '') END AS EcritureLib,
+ CASE WHEN aml.name IS NULL OR aml.name = '' THEN '/' ELSE replace(replace(aml.name, '|', '/'), '\t', '') END AS EcritureLib,
replace(CASE WHEN aml.debit = 0 THEN '0,00' ELSE to_char(aml.debit, '000000000000000D99') END, '.', ',') AS Debit,
replace(CASE WHEN aml.credit = 0 THEN '0,00' ELSE to_char(aml.credit, '000000000000000D99') END, '.', ',') AS Credit,
CASE WHEN rec.name IS NULL THEN '' ELSE rec.name END AS EcritureLet,
@@ -388,7 +388,7 @@ class AccountFrFec(models.TransientModel):
rows_length = len(rows)
for i, row in enumerate(rows):
if not i == rows_length - 1:
- row.append(lineterminator)
+ row[-1] += lineterminator
writer.writerow(row)
fecvalue = fecfile.getvalue()
diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py
index 0413c45b9eb..70ee621d1e4 100644
--- a/addons/payment/models/payment_acquirer.py
+++ b/addons/payment/models/payment_acquirer.py
@@ -897,7 +897,7 @@ class PaymentTransaction(models.Model):
# custom create
custom_method_name = '%s_create' % acquirer.provider
- if hasattr(acquirer, custom_method_name):
+ if hasattr(self, custom_method_name):
values.update(getattr(self, custom_method_name)(values))
if not values.get('reference'):
diff --git a/addons/payment_authorize/models/authorize_request.py b/addons/payment_authorize/models/authorize_request.py
index 79d0a7589c5..922d9df1bf1 100644
--- a/addons/payment_authorize/models/authorize_request.py
+++ b/addons/payment_authorize/models/authorize_request.py
@@ -226,6 +226,12 @@ class AuthorizeAPI():
etree.SubElement(customer, "email").text = partner.email or ''
response = self._authorize_request(root)
res = dict()
+ if not response.find('customerProfileId'):
+ _logger.warning(
+ 'Unable to create customer payment profile, data missing from transaction. Transaction_id: %s - Partner_id: %s'
+ % (transaction_id, partner)
+ )
+ return res
res['profile_id'] = response.find('customerProfileId').text
res['payment_profile_id'] = response.find('customerPaymentProfileIdList/numericString').text
root_profile = self._base_tree('getCustomerPaymentProfileRequest')
diff --git a/addons/payment_authorize/models/payment.py b/addons/payment_authorize/models/payment.py
index 5a6ed0bf8e1..946f031484d 100644
--- a/addons/payment_authorize/models/payment.py
+++ b/addons/payment_authorize/models/payment.py
@@ -228,17 +228,18 @@ class TxAuthorize(models.Model):
(self.type == 'form_save' or self.acquirer_id.save_token == 'always'):
transaction = AuthorizeAPI(self.acquirer_id)
res = transaction.create_customer_profile_from_tx(self.partner_id, self.acquirer_reference)
- token_id = self.env['payment.token'].create({
- 'authorize_profile': res.get('profile_id'),
- 'name': res.get('name'),
- 'acquirer_ref': res.get('payment_profile_id'),
- 'acquirer_id': self.acquirer_id.id,
- 'partner_id': self.partner_id.id,
- })
- self.payment_token_id = token_id
+ if res:
+ token_id = self.env['payment.token'].create({
+ 'authorize_profile': res.get('profile_id'),
+ 'name': res.get('name'),
+ 'acquirer_ref': res.get('payment_profile_id'),
+ 'acquirer_id': self.acquirer_id.id,
+ 'partner_id': self.partner_id.id,
+ })
+ self.payment_token_id = token_id
- if self.payment_token_id:
- self.payment_token_id.verified = True
+ if self.payment_token_id:
+ self.payment_token_id.verified = True
return True
elif status_code == self._authorize_pending_tx_status:
self.write({'acquirer_reference': data.get('x_trans_id')})
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index be9993ef2ec..ce87fa885a0 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -867,7 +867,7 @@ class PosOrder(models.Model):
# a serialnumber always has a quantity of 1 product, a lot number takes the full quantity of the order line
qty = 1.0
if stock_production_lot.product_id.tracking == 'lot':
- qty = pos_pack_lot.pos_order_line_id.qty
+ qty = abs(pos_pack_lot.pos_order_line_id.qty)
qty_done += qty
pack_lots.append({'lot_id': stock_production_lot.id, 'qty': qty})
else:
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index 8ad90c368d0..caf09ced6c9 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -1473,7 +1473,7 @@ exports.Orderline = Backbone.Model.extend({
var lots_required = 1;
if (this.product.tracking == 'serial') {
- lots_required = this.quantity;
+ lots_required = Math.abs(this.quantity);
}
return lots_required;
@@ -1878,8 +1878,11 @@ var PacklotlineCollection = Backbone.Collection.extend({
set_quantity_by_lot: function() {
if (this.order_line.product.tracking == 'serial') {
- var valid_lots = this.get_valid_lots();
- this.order_line.set_quantity(valid_lots.length);
+ var valid_lots_quantity = this.get_valid_lots().length;
+ if (this.order_line.quantity < 0){
+ valid_lots_quantity = -valid_lots_quantity;
+ }
+ this.order_line.set_quantity(valid_lots_quantity);
}
}
});
diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py
index b1f861c041c..64e4c03ecbd 100644
--- a/addons/purchase_stock/models/purchase.py
+++ b/addons/purchase_stock/models/purchase.py
@@ -312,7 +312,9 @@ class PurchaseOrderLine(models.Model):
for move in self.move_ids.filtered(lambda x: x.state != 'cancel' and not x.location_dest_id.usage == "supplier"):
qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
template = {
- 'name': self.name or '',
+ # truncate to 2000 to avoid triggering index limit error
+ # TODO: remove index in master?
+ 'name': (self.name or '')[:2000],
'product_id': self.product_id.id,
'product_uom': self.product_uom.id,
'date': self.order_id.date_order,
diff --git a/addons/sale/models/account_invoice.py b/addons/sale/models/account_invoice.py
index 9d487a64415..9ec43653015 100644
--- a/addons/sale/models/account_invoice.py
+++ b/addons/sale/models/account_invoice.py
@@ -49,7 +49,7 @@ class AccountInvoice(models.Model):
inv_type = self.type or self.env.context.get('type', 'out_invoice')
if inv_type == 'out_invoice':
company = self.company_id or self.env.user.company_id
- self.comment = company.with_context(lang=self.partner_id.lang).sale_note
+ self.comment = company.with_context(lang=self.partner_id.lang).sale_note or (self._origin.company_id == company and self.comment)
@api.multi
def action_invoice_open(self):
diff --git a/addons/sms/models/mail_thread.py b/addons/sms/models/mail_thread.py
index 1fb62a72de4..a439487e4e9 100644
--- a/addons/sms/models/mail_thread.py
+++ b/addons/sms/models/mail_thread.py
@@ -39,8 +39,8 @@ class MailThread(models.AbstractModel):
if not partners:
partners = self._get_default_sms_recipients()
- # Collect numbers, we will consider the message to be sent if at least one number can be found
- numbers = list(set([i.mobile for i in partners if i.mobile]))
+ # Collect numbers, we will consider the message to be sent if at least one number can be found
+ numbers = list(set([i.mobile for i in partners if i.mobile]))
if numbers:
try:
diff --git a/addons/sms/wizard/send_sms.py b/addons/sms/wizard/send_sms.py
index 448d9fcc626..986fd1c9239 100644
--- a/addons/sms/wizard/send_sms.py
+++ b/addons/sms/wizard/send_sms.py
@@ -45,8 +45,8 @@ class SendSMS(models.TransientModel):
return number
if not phonenumbers.is_possible_number(phone_nbr) or not phonenumbers.is_valid_number(phone_nbr):
return number
- phone_fmt = phonenumbers.PhoneNumberFormat.INTERNATIONAL
- return phonenumbers.format_number(phone_nbr, phone_fmt).replace(' ', '')
+ phone_fmt = phonenumbers.PhoneNumberFormat.E164
+ return phonenumbers.format_number(phone_nbr, phone_fmt)
else:
return number
@@ -62,12 +62,11 @@ class SendSMS(models.TransientModel):
@api.model
def default_get(self, fields):
result = super(SendSMS, self).default_get(fields)
-
active_model = self.env.context.get('active_model')
- model = self.env[active_model]
- records = self._get_records(model)
- if getattr(records, '_get_default_sms_recipients'):
+ if not self.env.context.get('default_recipients') and active_model and hasattr(self.env[active_model], '_get_default_sms_recipients'):
+ model = self.env[active_model]
+ records = self._get_records(model)
partners = records._get_default_sms_recipients()
phone_numbers = []
no_phone_partners = []
@@ -84,12 +83,12 @@ class SendSMS(models.TransientModel):
return result
def action_send_sms(self):
- numbers = self.recipients.split(',')
+ numbers = [number.strip() for number in self.recipients.split(',') if number.strip()]
active_model = self.env.context.get('active_model')
- model = self.env[active_model]
- records = self._get_records(model)
- if getattr(records, 'message_post_send_sms'):
+ if active_model and hasattr(self.env[active_model], 'message_post_send_sms'):
+ model = self.env[active_model]
+ records = self._get_records(model)
records.message_post_send_sms(self.message, numbers=numbers)
else:
self.env['sms.api']._send_sms(numbers, self.message)
diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py
index 0dd607ce3b6..5f583ec0682 100644
--- a/addons/stock/models/product.py
+++ b/addons/stock/models/product.py
@@ -458,6 +458,10 @@ class ProductTemplate(models.Model):
def _is_cost_method_standard(self):
return True
+ @api.depends(
+ 'product_variant_ids',
+ 'product_variant_ids.stock_quant_ids',
+ )
def _compute_quantities(self):
res = self._compute_quantities_dict()
for template in self:
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index c242d7777db..0d0fb1a68cd 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -462,6 +462,7 @@ class StockMove(models.Model):
else:
view = self.env.ref('stock.view_stock_move_nosuggest_operations')
+ picking_type_id = self.picking_type_id or self.picking_id.picking_type_id
return {
'name': _('Detailed Operations'),
'type': 'ir.actions.act_window',
@@ -474,8 +475,8 @@ class StockMove(models.Model):
'res_id': self.id,
'context': dict(
self.env.context,
- show_lots_m2o=self.has_tracking != 'none' and (self.picking_type_id.use_existing_lots or self.state == 'done' or self.origin_returned_move_id.id), # able to create lots, whatever the value of ` use_create_lots`.
- show_lots_text=self.has_tracking != 'none' and self.picking_type_id.use_create_lots and not self.picking_type_id.use_existing_lots and self.state != 'done' and not self.origin_returned_move_id.id,
+ show_lots_m2o=self.has_tracking != 'none' and (picking_type_id.use_existing_lots or self.state == 'done' or self.origin_returned_move_id.id), # able to create lots, whatever the value of ` use_create_lots`.
+ show_lots_text=self.has_tracking != 'none' and picking_type_id.use_create_lots and not picking_type_id.use_existing_lots and self.state != 'done' and not self.origin_returned_move_id.id,
show_source_location=self.location_id.child_ids and self.picking_type_id.code != 'incoming',
show_destination_location=self.location_dest_id.child_ids and self.picking_type_id.code != 'outgoing',
show_package=not self.location_id.usage == 'supplier',
diff --git a/addons/stock/wizard/stock_picking_return_views.xml b/addons/stock/wizard/stock_picking_return_views.xml
index 3bbba44a176..37b8af9a1e9 100644
--- a/addons/stock/wizard/stock_picking_return_views.xml
+++ b/addons/stock/wizard/stock_picking_return_views.xml
@@ -27,7 +27,7 @@
-
+