diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index b6f2fee7fe4..4c087e6b84e 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -1231,6 +1231,21 @@ class AccountMoveLine(models.Model):
action['domain'] = [('id', 'in', ids)]
return action
+ @api.multi
+ def _payment_invoice_match(self):
+ '''
+ If a partial reconciliation involves payments and invoices
+ mark the invoices as paid by the payments
+ Specifically made for len(self) == 2
+ '''
+ if not self:
+ return
+ invoice_ids = self.mapped('invoice_id')
+ payment_ids = self.mapped('payment_id')
+
+ if payment_ids and invoice_ids:
+ payment_ids.write({'invoice_ids': [(4, inv.id, False) for inv in invoice_ids]})
+
class AccountPartialReconcile(models.Model):
_name = "account.partial.reconcile"
@@ -1530,6 +1545,7 @@ class AccountPartialReconcile(models.Model):
aml.append(vals['credit_move_id'])
# Get value of matched percentage from both move before reconciliating
lines = self.env['account.move.line'].browse(aml)
+ lines._payment_invoice_match()
if lines[0].account_id.internal_type in ('receivable', 'payable'):
percentage_before_rec = lines._get_matched_percentage()
# Reconcile
diff --git a/addons/account/models/company.py b/addons/account/models/company.py
index 0209ea5c849..ef148cc3495 100644
--- a/addons/account/models/company.py
+++ b/addons/account/models/company.py
@@ -122,12 +122,8 @@ Best Regards,'''))
@api.model
def _verify_fiscalyear_last_day(self, company_id, last_day, last_month):
- company = self.browse(company_id)
- last_day = last_day or (company and company.fiscalyear_last_day) or 31
- last_month = last_month or (company and company.fiscalyear_last_month) or 12
- current_year = datetime.now().year
- last_day_of_month = calendar.monthrange(current_year, last_month)[1]
- return last_day > last_day_of_month and last_day_of_month or last_day
+ # FIXME: Remove this method in master
+ return last_day
@api.multi
def compute_fiscalyear_dates(self, date):
diff --git a/addons/account/wizard/setup_wizards.py b/addons/account/wizard/setup_wizards.py
index 38d9d663847..84cef53d507 100644
--- a/addons/account/wizard/setup_wizards.py
+++ b/addons/account/wizard/setup_wizards.py
@@ -36,17 +36,6 @@ class FinancialYearOpeningWizard(models.TransientModel):
""" Forces fiscal year setup state to 'undone'."""
self.company_id.account_setup_fy_data_done = False
- @api.multi
- def write(self, vals):
- if 'fiscalyear_last_day' in vals or 'fiscalyear_last_month' in vals:
- for wizard in self:
- company = wizard.company_id
- vals['fiscalyear_last_day'] = company._verify_fiscalyear_last_day(
- company.id,
- vals.get('fiscalyear_last_day'),
- vals.get('fiscalyear_last_month'))
- return super(FinancialYearOpeningWizard, self).write(vals)
-
class OpeningAccountMoveWizard(models.TransientModel):
_name = 'account.opening'
diff --git a/addons/anonymization/wizard/anonymize_wizard.py b/addons/anonymization/wizard/anonymize_wizard.py
index 4ba71b03455..7bf914ba742 100644
--- a/addons/anonymization/wizard/anonymize_wizard.py
+++ b/addons/anonymization/wizard/anonymize_wizard.py
@@ -127,6 +127,9 @@ class IrModelFieldsAnonymizeWizard(models.TransientModel):
@api.multi
def anonymize_database(self):
"""Sets the 'anonymized' state to defined fields"""
+ # pylint: disable=W0101
+ raise UserError("""The Odoo Migration Platform no longer accepts anonymized databases.\n
+ If you wish for your data to remain private during migration, please contact us at upgrade@odoo.com""")
self.ensure_one()
# create a new history record:
diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py
index 68fe08bae7b..c3cd70392c3 100644
--- a/addons/crm/models/crm_lead.py
+++ b/addons/crm/models/crm_lead.py
@@ -701,6 +701,8 @@ class Lead(models.Model):
@api.model
def _get_duplicated_leads_by_emails(self, partner_id, email, include_lost=False):
""" Search for opportunities that have the same partner and that arent done or cancelled """
+ if not email:
+ return []
partner_match_domain = []
for email in set(email_split(email) + [email]):
partner_match_domain.append(('email_from', '=ilike', email))
diff --git a/addons/delivery/models/sale_order.py b/addons/delivery/models/sale_order.py
index e08f37b135e..d0ca0a7c424 100644
--- a/addons/delivery/models/sale_order.py
+++ b/addons/delivery/models/sale_order.py
@@ -88,6 +88,9 @@ class SaleOrder(models.Model):
def _create_delivery_line(self, carrier, price_unit):
SaleOrderLine = self.env['sale.order.line']
+ if self.partner_id:
+ # set delivery detail in the customer language
+ carrier = carrier.with_context(lang=self.partner_id.lang)
# Apply fiscal position
taxes = carrier.product_id.taxes_id.filtered(lambda t: t.company_id.id == self.company_id.id)
diff --git a/addons/hr_payroll_account/models/hr_payroll_account.py b/addons/hr_payroll_account/models/hr_payroll_account.py
index da168912095..989d5f260f4 100644
--- a/addons/hr_payroll_account/models/hr_payroll_account.py
+++ b/addons/hr_payroll_account/models/hr_payroll_account.py
@@ -53,6 +53,7 @@ class HrPayslip(models.Model):
@api.multi
def action_payslip_done(self):
+ res = super(HrPayslip, self).action_payslip_done()
precision = self.env['decimal.precision'].precision_get('Payroll')
for slip in self:
@@ -138,7 +139,7 @@ class HrPayslip(models.Model):
move = self.env['account.move'].create(move_dict)
slip.write({'move_id': move.id, 'date': date})
move.post()
- return super(HrPayslip, self).action_payslip_done()
+ return res
class HrSalaryRule(models.Model):
diff --git a/addons/hw_escpos/escpos/escpos.py b/addons/hw_escpos/escpos/escpos.py
index 7fc0e196b39..3573efb1d98 100644
--- a/addons/hw_escpos/escpos/escpos.py
+++ b/addons/hw_escpos/escpos/escpos.py
@@ -358,7 +358,7 @@ class Escpos:
else:
self._raw(string)
- raw += S_RASTER_N
+ raw += S_RASTER_N.encode('utf-8')
buffer = "%02X%02X%02X%02X" % (int((size[0]/size[1])/8), 0, size[1], 0)
raw += codecs.decode(buffer, 'hex')
buffer = ""
diff --git a/addons/l10n_in/views/report_invoice.xml b/addons/l10n_in/views/report_invoice.xml
index 7fb7337becf..f20fee72efd 100644
--- a/addons/l10n_in/views/report_invoice.xml
+++ b/addons/l10n_in/views/report_invoice.xml
@@ -108,7 +108,14 @@
|
|
|
- |
+ |
+ |
+
+ |
+ |
+ |
+ |
+
diff --git a/addons/mass_mailing/models/mail_thread.py b/addons/mass_mailing/models/mail_thread.py
index 8e4ee298c67..f26aee7ff4a 100644
--- a/addons/mass_mailing/models/mail_thread.py
+++ b/addons/mass_mailing/models/mail_thread.py
@@ -35,7 +35,7 @@ class MailThread(models.AbstractModel):
""" Override to update the parent mail statistics. The parent is found
by using the References header of the incoming message and looking for
matching message_id in mail.mail.statistics. """
- if message.get('References'):
+ if message.get('References') and routes:
message_ids = [x.strip() for x in decode_smtp_header(message['References']).split()]
self.env['mail.mail.statistics'].set_opened(mail_message_ids=message_ids)
self.env['mail.mail.statistics'].set_replied(mail_message_ids=message_ids)
diff --git a/addons/mass_mailing/models/mass_mailing_stats.py b/addons/mass_mailing/models/mass_mailing_stats.py
index 0a095943f99..8e48a4cb465 100644
--- a/addons/mass_mailing/models/mass_mailing_stats.py
+++ b/addons/mass_mailing/models/mass_mailing_stats.py
@@ -106,7 +106,7 @@ class MailMailStats(models.Model):
def set_opened(self, mail_mail_ids=None, mail_message_ids=None):
statistics = self._get_records(mail_mail_ids, mail_message_ids, [('opened', '=', False)])
- statistics.write({'opened': fields.Datetime.now()})
+ statistics.write({'opened': fields.Datetime.now(), 'bounced': False})
return statistics
def set_clicked(self, mail_mail_ids=None, mail_message_ids=None):
@@ -120,6 +120,7 @@ class MailMailStats(models.Model):
return statistics
def set_bounced(self, mail_mail_ids=None, mail_message_ids=None):
- statistics = self._get_records(mail_mail_ids, mail_message_ids, [('bounced', '=', False)])
+ statistics = self._get_records(
+ mail_mail_ids, mail_message_ids, [('bounced', '=', False), ('opened', '=', False)])
statistics.write({'bounced': fields.Datetime.now()})
return statistics
diff --git a/addons/payment/static/src/js/payment_form.js b/addons/payment/static/src/js/payment_form.js
index 372e8ba233d..72aa70c88b6 100644
--- a/addons/payment/static/src/js/payment_form.js
+++ b/addons/payment/static/src/js/payment_form.js
@@ -134,7 +134,7 @@ odoo.define('payment.payment_form', function (require) {
self.displayError(
_t('Server Error'),
_t("We are not able to add your payment method at the moment.") +
- data.data.message
+ message.data.message
);
});
}
@@ -179,7 +179,7 @@ odoo.define('payment.payment_form', function (require) {
self.displayError(
_t('Server Error'),
_t("We are not able to redirect you to the payment form. ") +
- data.data.message
+ message.data.message
);
});
}
diff --git a/addons/purchase/models/account_invoice.py b/addons/purchase/models/account_invoice.py
index f9ad010b155..25225bdc8f6 100644
--- a/addons/purchase/models/account_invoice.py
+++ b/addons/purchase/models/account_invoice.py
@@ -28,11 +28,12 @@ class AccountInvoice(models.Model):
purchase_line_ids = self.invoice_line_ids.mapped('purchase_line_id')
purchase_ids = self.invoice_line_ids.mapped('purchase_id').filtered(lambda r: r.order_line <= purchase_line_ids)
- result['domain'] = {'purchase_id': [
- ('invoice_status', '=', 'to invoice'),
- ('partner_id', 'child_of', self.partner_id.id),
- ('id', 'not in', purchase_ids.ids),
- ]}
+ domain = [('invoice_status', '=', 'to invoice')]
+ if self.partner_id:
+ domain += [('partner_id', 'child_of', self.partner_id.id)]
+ if purchase_ids:
+ domain += [('id', 'not in', purchase_ids.ids)]
+ result['domain'] = {'purchase_id': domain}
return result
def _prepare_invoice_line_from_po_line(self, line):
diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py
index 2dc109f5211..a23b478a61f 100644
--- a/addons/purchase/models/purchase.py
+++ b/addons/purchase/models/purchase.py
@@ -395,10 +395,11 @@ class PurchaseOrder(models.Model):
if order.state in ('draft', 'sent', 'to approve'):
for order_line in order.order_line:
if order_line.move_dest_ids:
- siblings_states = (order_line.move_dest_ids.mapped('move_orig_ids')).mapped('state')
+ move_dest_ids = order_line.move_dest_ids.filtered(lambda m: m.state not in ('done', 'cancel'))
+ siblings_states = (move_dest_ids.mapped('move_orig_ids')).mapped('state')
if all(state in ('done', 'cancel') for state in siblings_states):
- order_line.move_dest_ids.write({'procure_method': 'make_to_stock'})
- order_line.move_dest_ids._recompute_state()
+ move_dest_ids.write({'procure_method': 'make_to_stock'})
+ move_dest_ids._recompute_state()
for pick in order.picking_ids.filtered(lambda r: r.state != 'cancel'):
pick.action_cancel()
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index bc9fcd8bd76..8fc1e330335 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -666,6 +666,12 @@ class SaleOrder(models.Model):
'target': 'self',
'res_id': self.id,
}
+ else:
+ action = self.env.ref('sale.action_quotations', False)
+ if action:
+ result = action.read()[0]
+ result['res_id'] = self.id
+ return result
return super(SaleOrder, self).get_access_action(access_uid)
def get_mail_url(self):
diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py
index 03516c551bf..9c5bc7b6d89 100644
--- a/addons/stock/models/stock_picking.py
+++ b/addons/stock/models/stock_picking.py
@@ -715,8 +715,6 @@ class Picking(models.Model):
if product and product.tracking != 'none':
if not line.lot_name and not line.lot_id:
raise UserError(_('You need to supply a Lot/Serial number for product %s.') % product.display_name)
- elif line.qty_done == 0:
- raise UserError(_('You cannot validate a transfer if you have not processed any quantity for %s.') % product.display_name)
if no_quantities_done:
view = self.env.ref('stock.view_immediate_transfer')
diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py
index 32129bde80a..ceaf2ae8ea0 100644
--- a/addons/stock/tests/test_move.py
+++ b/addons/stock/tests/test_move.py
@@ -3158,7 +3158,7 @@ class StockMove(TransactionCase):
def test_immediate_validate_4(self):
""" In a picking with a single available tracked by lot move, clicking on validate without
- filling any quantities should open an UserError.
+ filling any quantities should pop up the immediate transfer wizard.
"""
partner = self.env['res.partner'].create({'name': 'Jean'})
lot1 = self.env['stock.production.lot'].create({
@@ -3184,12 +3184,11 @@ class StockMove(TransactionCase):
})
picking.action_confirm()
picking.action_assign()
- # No quantites/lot filled, it should raise.
- with self.assertRaises(UserError):
- picking.button_validate()
- picking.move_lines.move_line_ids[0].qty_done = 5.0
- # All the information are present (lots and quantities), the wizard won't be opened.
- picking.button_validate()
+ # No quantites filled, immediate transfer wizard should pop up.
+ immediate_trans_wiz_dict = picking.button_validate()
+ self.assertEqual(immediate_trans_wiz_dict.get('res_model'), 'stock.immediate.transfer')
+ immediate_trans_wiz = self.env[immediate_trans_wiz_dict['res_model']].browse(immediate_trans_wiz_dict['res_id'])
+ immediate_trans_wiz.process()
self.assertEqual(picking.move_lines.quantity_done, 5.0)
# Check move_lines data
diff --git a/addons/stock_landed_costs/__manifest__.py b/addons/stock_landed_costs/__manifest__.py
index e181f6d9bb4..32869aa169d 100644
--- a/addons/stock_landed_costs/__manifest__.py
+++ b/addons/stock_landed_costs/__manifest__.py
@@ -18,6 +18,7 @@ This module allows you to easily add extra costs on pickings and decide the spli
],
'data': [
'security/ir.model.access.csv',
+ 'security/stock_landed_cost_security.xml',
'data/stock_landed_cost_data.xml',
'views/product_views.xml',
'views/stock_landed_cost_views.xml',
diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py
index 533d3b15e04..2137b111a9e 100644
--- a/addons/stock_landed_costs/models/stock_landed_cost.py
+++ b/addons/stock_landed_costs/models/stock_landed_cost.py
@@ -51,6 +51,8 @@ class LandedCost(models.Model):
account_journal_id = fields.Many2one(
'account.journal', 'Account Journal',
required=True, states={'done': [('readonly', True)]})
+ company_id = fields.Many2one('res.company', string="Company",
+ related='account_journal_id.company_id')
@api.one
@api.depends('cost_lines.price_unit')
diff --git a/addons/stock_landed_costs/security/stock_landed_cost_security.xml b/addons/stock_landed_costs/security/stock_landed_cost_security.xml
new file mode 100644
index 00000000000..1182f9c687a
--- /dev/null
+++ b/addons/stock_landed_costs/security/stock_landed_cost_security.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ stock_landed_cost multi-company
+
+
+ ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]
+
+
+
+
\ No newline at end of file
diff --git a/addons/web_editor/static/src/js/widgets/widgets.js b/addons/web_editor/static/src/js/widgets/widgets.js
index de694e9b9a7..00e02e6e032 100644
--- a/addons/web_editor/static/src/js/widgets/widgets.js
+++ b/addons/web_editor/static/src/js/widgets/widgets.js
@@ -949,6 +949,9 @@ var VideoWidget = MediaWidget.extend({
return {errorCode: 1};
}
+ if (ytMatch) {
+ $video.attr('src', $video.attr('src') + '&rel=0');
+ }
if (options.loop && (ytMatch || vimMatch)) {
$video.attr('src', $video.attr('src') + '&loop=1');
}
diff --git a/doc/cla/corporate/brain-tec.md b/doc/cla/corporate/brain-tec.md
index f598f64eb1b..46e4e2d3646 100644
--- a/doc/cla/corporate/brain-tec.md
+++ b/doc/cla/corporate/brain-tec.md
@@ -38,3 +38,4 @@ Raúl Martín raul.martin@braintec-group.com https://github.com/BT-rmartin
Silvan Wyden silvan.wyden@braintec-group.com https://github.com/BT-swyden
Timka Piric Muratovic timka.piric@braintec-group.com https://github.com/BT-tpiric
Pascal Zenklusen pascal.zenklusen@braintec-group.com https://github.com/BT-pzenklusen
+Nadal Francisco Garcia nadal.francisco@braintec-group.com https://github.com/BT-nfrancisco
\ No newline at end of file
diff --git a/doc/cla/individual/as400it.md b/doc/cla/individual/as400it.md
new file mode 100644
index 00000000000..d42536df0d6
--- /dev/null
+++ b/doc/cla/individual/as400it.md
@@ -0,0 +1,11 @@
+Italy, 2018-08-08
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Marco Calcagni mcalcagni@dinamicheaziendali.it https://github.com/As400it
diff --git a/odoo/addons/base/data/res.lang.csv b/odoo/addons/base/data/res.lang.csv
index bea539d9325..cf11d7bb4c9 100644
--- a/odoo/addons/base/data/res.lang.csv
+++ b/odoo/addons/base/data/res.lang.csv
@@ -6,9 +6,9 @@
"base.lang_bs_BA","Bosnian / bosanski jezik","bs_BA","bs","Left-to-Right","[]",",",,"%d.%m.%Y","%H:%M:%S","True","7"
"base.lang_bg","Bulgarian / български език","bg_BG","bg","Left-to-Right","[3,0]",",",,"%d.%m.%Y","%H,%M,%S","True","1"
"base.lang_ca_ES","Catalan / Català","ca_ES","ca_ES","Left-to-Right","[3,0]",",",".","%d/%m/%Y","%H:%M:%S","True","1"
-"base.lang_zh_CN","Chinese (CN) / 简体中文","zh_CN","zh_CN","Left-to-Right","[3,0]",".",",","%Y年%m月%d日","%H时%M分%S秒","True","7"
+"base.lang_zh_CN","Chinese (Simplified) / 简体中文","zh_CN","zh_CN","Left-to-Right","[3,0]",".",",","%Y年%m月%d日","%H时%M分%S秒","True","7"
"base.lang_zh_HK","Chinese (HK)","zh_HK","zh_HK","Left-to-Right","[3,0]",".",",","%Y年%m月%d日 %A","%I時%M分%S秒","True","7"
-"base.lang_zh_TW","Chinese (TW) / 正體字","zh_TW","zh_TW","Left-to-Right","[3,0]",".",",","%Y年%m月%d日","%H時%M分%S秒","True","7"
+"base.lang_zh_TW","Chinese (Traditional) / 正體字","zh_TW","zh_TW","Left-to-Right","[3,0]",".",",","%Y年%m月%d日","%H時%M分%S秒","True","7"
"base.lang_hr","Croatian / hrvatski jezik","hr_HR","hr","Left-to-Right","[]",",",,"%d.%m.%Y","%H:%M:%S","True","1"
"base.lang_cs_CZ","Czech / Čeština","cs_CZ","cs_CZ","Left-to-Right","[3,0]",","," ","%d.%m.%Y","%H:%M:%S","True","1"
"base.lang_da_DK","Danish / Dansk","da_DK","da_DK","Left-to-Right","[3,0]",",",".","%d-%m-%Y","%H:%M:%S","True","1"
diff --git a/odoo/addons/base/models/res_config.py b/odoo/addons/base/models/res_config.py
index b693ca6be06..ab593780f61 100644
--- a/odoo/addons/base/models/res_config.py
+++ b/odoo/addons/base/models/res_config.py
@@ -575,8 +575,11 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
IrDefault.set(model, field, value)
# group fields: modify group / implied groups
+ current_settings = self.new(self.default_get(list(self.fields_get())))
with self.env.norecompute():
for name, groups, implied_group in classified['group']:
+ if self[name] == current_settings[name]:
+ continue
if self[name]:
groups.write({'implied_ids': [(4, implied_group.id)]})
else:
diff --git a/odoo/exceptions.py b/odoo/exceptions.py
index c318f9ecdac..6807485e80a 100644
--- a/odoo/exceptions.py
+++ b/odoo/exceptions.py
@@ -46,6 +46,10 @@ class RedirectWarning(Exception):
:param string button_text: text to put on the button that will trigger
the redirection.
"""
+ # using this RedirectWarning won't crash if used as an except_orm
+ @property
+ def name(self):
+ return self.args[0]
class AccessDenied(Exception):
diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py
index 639d9c7626e..8a33558c790 100644
--- a/odoo/tools/misc.py
+++ b/odoo/tools/misc.py
@@ -1148,10 +1148,7 @@ def formatLang(env, value, digits=None, grouping=True, monetary=False, dp=False,
return ''
lang = env.context.get('lang') or env.user.company_id.partner_id.lang or 'en_US'
- lang_objs = env['res.lang'].search([('code', '=', lang)])
- if not lang_objs:
- lang_objs = env['res.lang'].search([], limit=1)
- lang_obj = lang_objs[0]
+ lang_obj = env['res.lang']._lang_get(lang)
res = lang_obj.format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary)
diff --git a/requirements.txt b/requirements.txt
index 96736e9917d..44b4fcb11c0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,7 +8,7 @@ gevent==1.3.4 ; sys_platform != 'win32' and python_version >= '3.7'
greenlet==0.4.10 ; python_version < '3.7'
greenlet==0.4.13 ; python_version >= '3.7'
html2text==2016.9.19
-Jinja2==2.8
+Jinja2==2.8.1
lxml==3.7.1 ; sys_platform != 'win32' and python_version < '3.7'
lxml==4.2.3 ; sys_platform != 'win32' and python_version >= '3.7'
lxml ; sys_platform == 'win32'