diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 954a8dc0c6f..2fe9f9536a8 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -189,7 +189,7 @@ class AccountInvoice(models.Model):
@api.depends('move_id.line_ids.amount_residual')
def _compute_payments(self):
payment_lines = []
- for line in self.move_id.line_ids:
+ for line in self.move_id.line_ids.filtered(lambda l: l.account_id.id == self.account_id.id):
payment_lines.extend(filter(None, [rp.credit_move_id.id for rp in line.matched_credit_ids]))
payment_lines.extend(filter(None, [rp.debit_move_id.id for rp in line.matched_debit_ids]))
self.payment_move_line_ids = self.env['account.move.line'].browse(list(set(payment_lines)))
diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py
index 31a6110be9a..968f939d493 100644
--- a/addons/account/report/account_aged_partner_balance.py
+++ b/addons/account/report/account_aged_partner_balance.py
@@ -13,6 +13,10 @@ class ReportAgedPartnerBalance(models.AbstractModel):
_name = 'report.account.report_agedpartnerbalance'
def _get_partner_move_lines(self, account_type, date_from, target_move, period_length):
+ # This method can receive the context key 'include_nullified_amount' {Boolean}
+ # Do an invoice and a payment and unreconcile. The amount will be nullified
+ # By default, the partner wouldn't appear in this report.
+ # The context key allow it to appear
periods = {}
start = datetime.strptime(date_from, "%Y-%m-%d")
for i in range(5)[::-1]:
@@ -190,7 +194,7 @@ class ReportAgedPartnerBalance(models.AbstractModel):
values['name'] = _('Unknown Partner')
values['trust'] = False
- if at_least_one_amount:
+ if at_least_one_amount or self._context.get('include_nullified_amount'):
res.append(values)
return res, total, lines
diff --git a/addons/account/report/account_overdue_report.py b/addons/account/report/account_overdue_report.py
index fd032197233..8afa71b4414 100644
--- a/addons/account/report/account_overdue_report.py
+++ b/addons/account/report/account_overdue_report.py
@@ -25,7 +25,7 @@ class ReportOverdue(models.AbstractModel):
"FROM account_move_line l "
"JOIN account_account_type at ON (l.user_type_id = at.id) "
"JOIN account_move m ON (l.move_id = m.id) "
- "WHERE l.partner_id IN %s AND at.type IN ('receivable', 'payable') AND NOT l.reconciled GROUP BY l.date, l.name, l.ref, l.date_maturity, l.partner_id, at.type, l.blocked, l.amount_currency, l.currency_id, l.move_id, m.name", (((fields.date.today(), ) + (tuple(partner_ids),))))
+ "WHERE l.partner_id IN %s AND at.type IN ('receivable', 'payable') AND l.full_reconcile_id IS NULL GROUP BY l.date, l.name, l.ref, l.date_maturity, l.partner_id, at.type, l.blocked, l.amount_currency, l.currency_id, l.move_id, m.name", (((fields.date.today(), ) + (tuple(partner_ids),))))
for row in self.env.cr.dictfetchall():
res[row.pop('partner_id')].append(row)
return res
diff --git a/addons/account/report/account_partner_ledger.py b/addons/account/report/account_partner_ledger.py
index fa756c4b977..399557504d7 100644
--- a/addons/account/report/account_partner_ledger.py
+++ b/addons/account/report/account_partner_ledger.py
@@ -14,7 +14,7 @@ class ReportPartnerLedger(models.AbstractModel):
full_account = []
currency = self.env['res.currency']
query_get_data = self.env['account.move.line'].with_context(data['form'].get('used_context', {}))._query_get()
- reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".reconciled = false '
+ reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL '
params = [partner.id, tuple(data['computed']['move_state']), tuple(data['computed']['account_ids'])] + query_get_data[2]
query = """
SELECT "account_move_line".id, "account_move_line".date, j.code, acc.code as a_code, acc.name as a_name, "account_move_line".ref, m.name as move_name, "account_move_line".name, "account_move_line".debit, "account_move_line".credit, "account_move_line".amount_currency,"account_move_line".currency_id, c.symbol AS currency_code
@@ -51,7 +51,7 @@ class ReportPartnerLedger(models.AbstractModel):
return
result = 0.0
query_get_data = self.env['account.move.line'].with_context(data['form'].get('used_context', {}))._query_get()
- reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".reconciled = false '
+ reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL '
params = [partner.id, tuple(data['computed']['move_state']), tuple(data['computed']['account_ids'])] + query_get_data[2]
query = """SELECT sum(""" + field + """)
@@ -95,7 +95,7 @@ class ReportPartnerLedger(models.AbstractModel):
AND NOT a.deprecated""", (tuple(data['computed']['ACCOUNT_TYPE']),))
data['computed']['account_ids'] = [a for (a,) in self.env.cr.fetchall()]
params = [tuple(data['computed']['move_state']), tuple(data['computed']['account_ids'])] + query_get_data[2]
- reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".reconciled = false '
+ reconcile_clause = "" if data['form']['reconciled'] else ' AND "account_move_line".full_reconcile_id IS NULL '
query = """
SELECT DISTINCT "account_move_line".partner_id
FROM """ + query_get_data[0] + """, account_account AS account, account_move AS am
diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js
index 88f179d0644..e9dec85739f 100644
--- a/addons/board/static/src/js/dashboard.js
+++ b/addons/board/static/src/js/dashboard.js
@@ -276,6 +276,14 @@ var DashBoard = form_common.FormWidget.extend({
};
});
}
+ var calendar = am.inner_widget.views.calendar;
+ if (calendar) {
+ calendar.loaded.done(function () {
+ calendar.controller.open_event = function (id, title) {
+ new_form_action(Number(id));
+ };
+ });
+ }
}
});
am.do_action = self.do_action.bind(self);
diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py
index 8f57d844e0a..3b8ed12851f 100644
--- a/addons/l10n_fr_fec/wizard/account_fr_fec.py
+++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py
@@ -221,7 +221,7 @@ class AccountFrFec(models.TransientModel):
replace(MIN(aa.name), '|', '/') AS CompteLib,
CASE WHEN rp.ref IS null OR rp.ref = ''
THEN COALESCE('ID ' || rp.id, '')
- ELSE rp.ref
+ ELSE replace(rp.ref, '|', '/')
END
AS CompAuxNum,
COALESCE(replace(rp.name, '|', '/'), '') AS CompAuxLib,
@@ -279,7 +279,7 @@ class AccountFrFec(models.TransientModel):
replace(aa.name, '|', '/') AS CompteLib,
CASE WHEN rp.ref IS null OR rp.ref = ''
THEN COALESCE('ID ' || rp.id, '')
- ELSE rp.ref
+ ELSE replace(rp.ref, '|', '/')
END
AS CompAuxNum,
COALESCE(replace(rp.name, '|', '/'), '') AS CompAuxLib,
diff --git a/addons/membership/models/partner.py b/addons/membership/models/partner.py
index 8ee9bee2f5a..2c8227844be 100644
--- a/addons/membership/models/partner.py
+++ b/addons/membership/models/partner.py
@@ -51,7 +51,7 @@ class Partner(models.Model):
'member_lines.account_invoice_line.invoice_id.invoice_line_ids',
'member_lines.account_invoice_line.invoice_id.payment_ids',
'free_member',
- 'member_lines.date_to', 'member_lines.date_from',
+ 'member_lines.date_to', 'member_lines.date_from', 'member_lines.date_cancel',
'membership_state',
'associate_member.membership_state')
def _compute_membership_start(self):
@@ -65,7 +65,7 @@ class Partner(models.Model):
'member_lines.account_invoice_line.invoice_id.invoice_line_ids',
'member_lines.account_invoice_line.invoice_id.payment_ids',
'free_member',
- 'member_lines.date_to', 'member_lines.date_from',
+ 'member_lines.date_to', 'member_lines.date_from', 'member_lines.date_cancel',
'membership_state',
'associate_member.membership_state')
def _compute_membership_stop(self):
@@ -79,7 +79,7 @@ class Partner(models.Model):
'member_lines.account_invoice_line.invoice_id.invoice_line_ids',
'member_lines.account_invoice_line.invoice_id.payment_ids',
'free_member',
- 'member_lines.date_to', 'member_lines.date_from',
+ 'member_lines.date_to', 'member_lines.date_from', 'member_lines.date_cancel',
'membership_state',
'associate_member.membership_state')
def _compute_membership_cancel(self):
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index 0f8dbdb801e..6e12dc8b114 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -1979,7 +1979,9 @@ exports.Order = Backbone.Model.extend({
}
})
- unit_price = line.compute_all(mapped_included_taxes, unit_price, 1, this.pos.currency.rounding, true).total_excluded;
+ if (mapped_included_taxes.length > 0) {
+ unit_price = line.compute_all(mapped_included_taxes, unit_price, 1, this.pos.currency.rounding, true).total_excluded;
+ }
line.set_unit_price(unit_price);
}
diff --git a/addons/project_issue/models/project_issue.py b/addons/project_issue/models/project_issue.py
index d4f5e94209f..c6d883a6627 100644
--- a/addons/project_issue/models/project_issue.py
+++ b/addons/project_issue/models/project_issue.py
@@ -209,11 +209,11 @@ class ProjectIssue(models.Model):
@api.multi
def _track_template(self, tracking):
- self.ensure_one()
res = super(ProjectIssue, self)._track_template(tracking)
- changes, dummy = tracking[self.id]
- if 'stage_id' in changes and self.stage_id.mail_template_id:
- res['stage_id'] = (self.stage_id.mail_template_id, {'composition_mode': 'mass_mail'})
+ test_issue = self[0]
+ changes, tracking_value_ids = tracking[test_issue.id]
+ if 'stage_id' in changes and test_issue.stage_id.mail_template_id:
+ res['stage_id'] = (test_issue.stage_id.mail_template_id, {'composition_mode': 'mass_mail'})
return res
def _track_subtype(self, init_values):
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index f4b84ab8b06..d142f69a870 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -492,7 +492,7 @@ class SaleOrder(models.Model):
report_pages.append([])
# Append category to current report page
report_pages[-1].append({
- 'name': category and category.name or 'Uncategorized',
+ 'name': category and category.name or _('Uncategorized'),
'subtotal': category and category.subtotal,
'pagebreak': category and category.pagebreak,
'lines': list(lines)
diff --git a/addons/sales_team/security/sales_team_security.xml b/addons/sales_team/security/sales_team_security.xml
index 904f7a3c04d..9e2e3f9939a 100644
--- a/addons/sales_team/security/sales_team_security.xml
+++ b/addons/sales_team/security/sales_team_security.xml
@@ -22,14 +22,6 @@
-
-
- Own Salesteam
-
- [('member_ids', 'in', user.id)]
-
-
-
All Salesteam
diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py
index c5b3ba0d490..ad1f0ea07e5 100644
--- a/addons/stock/models/stock_warehouse.py
+++ b/addons/stock/models/stock_warehouse.py
@@ -653,11 +653,11 @@ class Warehouse(models.Model):
def _get_sequence_values(self):
return {
- 'in_type_id': {'name': self.name + _('Sequence in'), 'prefix': self.code + '/IN/', 'padding': 5},
- 'out_type_id': {'name': self.name + _('Sequence out'), 'prefix': self.code + '/OUT/', 'padding': 5},
- 'pack_type_id': {'name': self.name + _('Sequence packing'), 'prefix': self.code + '/PACK/', 'padding': 5},
- 'pick_type_id': {'name': self.name + _('Sequence picking'), 'prefix': self.code + '/PICK/', 'padding': 5},
- 'int_type_id': {'name': self.name + _('Sequence internal'), 'prefix': self.code + '/INT/', 'padding': 5},
+ 'in_type_id': {'name': self.name + ' ' + _('Sequence in'), 'prefix': self.code + '/IN/', 'padding': 5},
+ 'out_type_id': {'name': self.name + ' ' + _('Sequence out'), 'prefix': self.code + '/OUT/', 'padding': 5},
+ 'pack_type_id': {'name': self.name + ' ' + _('Sequence packing'), 'prefix': self.code + '/PACK/', 'padding': 5},
+ 'pick_type_id': {'name': self.name + ' ' + _('Sequence picking'), 'prefix': self.code + '/PICK/', 'padding': 5},
+ 'int_type_id': {'name': self.name + ' ' + _('Sequence internal'), 'prefix': self.code + '/INT/', 'padding': 5},
}
@api.multi
diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py
index c4a6beaf307..43c125415cf 100644
--- a/addons/stock_landed_costs/models/stock_landed_cost.py
+++ b/addons/stock_landed_costs/models/stock_landed_cost.py
@@ -15,7 +15,7 @@ class LandedCost(models.Model):
_inherit = 'mail.thread'
name = fields.Char(
- 'Name', default=lambda self: self.env['ir.sequence'].next_by_code('stock.landed.cost'),
+ 'Name', default=lambda self: _('New'),
copy=False, readonly=True, track_visibility='always')
date = fields.Date(
'Date', default=fields.Date.context_today,
@@ -51,6 +51,12 @@ class LandedCost(models.Model):
def _compute_total_amount(self):
self.amount_total = sum(line.price_unit for line in self.cost_lines)
+ @api.model
+ def create(self, vals):
+ if vals.get('name', _('New')) == _('New'):
+ vals['name'] = self.env['ir.sequence'].next_by_code('stock.landed.cost')
+ return super(LandedCost, self).create(vals)
+
@api.multi
def unlink(self):
self.button_cancel()
diff --git a/doc/cla/corporate/kazacube.md b/doc/cla/corporate/kazacube.md
new file mode 100644
index 00000000000..ae4d2868080
--- /dev/null
+++ b/doc/cla/corporate/kazacube.md
@@ -0,0 +1,16 @@
+Morocco, 2017-10-11
+
+KAZACUBE SARL agrees to the terms of the Odoo Corporate Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Damien DELANGLE ddelangle@kazacube.com https://github.com/kazacube-damien
+
+List of contributors:
+
+ddelangle@kazacube.com https://github.com/kazacube-damien
+cloudmaster@kazacube.com https://github.com/sharecodekzc
diff --git a/doc/cla/individual/SimoRubi.md b/doc/cla/individual/SimoRubi.md
new file mode 100644
index 00000000000..07a69054170
--- /dev/null
+++ b/doc/cla/individual/SimoRubi.md
@@ -0,0 +1,11 @@
+Italy, 2018-02-05
+
+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,
+
+Simone Rubino simone.rubino@agilebg.com https://github.com/SimoRubi
diff --git a/doc/cla/individual/kurkop.md b/doc/cla/individual/kurkop.md
index ead18fbc4cb..99527b55009 100644
--- a/doc/cla/individual/kurkop.md
+++ b/doc/cla/individual/kurkop.md
@@ -9,3 +9,4 @@ declaration.
Signed,
Juan Pablo Arias jpa@devco.co https://github.com/kurkop
+Juan Pablo Arias j@kurlabs.com https://github.com/kurkop
diff --git a/doc/reference/mobile.rst b/doc/reference/mobile.rst
index db9dc1343f4..4777631f4b0 100644
--- a/doc/reference/mobile.rst
+++ b/doc/reference/mobile.rst
@@ -22,7 +22,7 @@ Vibration, Notification, Toast through Odoo Web (via JavaScript). For this, you
do not need to be a mobile developer, if you know Odoo JavaScript API you can
access all available mobile features.
-.. warning:: This features works with **Odoo Enterprise 10.0+** only
+.. warning:: These features work with **Odoo Enterprise 10.0+** only
How does it work?
=================
diff --git a/odoo/addons/base/res/res_country_data.xml b/odoo/addons/base/res/res_country_data.xml
index c56247a98cb..8ff0caa1ef0 100644
--- a/odoo/addons/base/res/res_country_data.xml
+++ b/odoo/addons/base/res/res_country_data.xml
@@ -1226,6 +1226,7 @@
+
Peru
diff --git a/odoo/addons/base/res/res_currency_data.xml b/odoo/addons/base/res/res_currency_data.xml
index 8746f4d0b1b..e3088952ca0 100644
--- a/odoo/addons/base/res/res_currency_data.xml
+++ b/odoo/addons/base/res/res_currency_data.xml
@@ -781,7 +781,7 @@
MAD
- د.م.
+ DH
0.01
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index e7579f5c909..b2ff0c1eb58 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -228,6 +228,13 @@ class TestFields(common.TransactionCase):
self.assertEqual(ewan.parent, cath)
self.assertEqual(ewan.name, "Erwan")
+ # write on non-stored inverse field on severals records
+ foo1 = Category.create({'name': 'Foo'})
+ foo2 = Category.create({'name': 'Foo'})
+ (foo1 + foo2).write({'display_name': 'Bar'})
+ self.assertEqual(foo1.name, 'Bar')
+ self.assertEqual(foo2.name, 'Bar')
+
record = self.env['test_new_api.compute.inverse']
# create/write on 'foo' should only invoke the compute method
diff --git a/odoo/fields.py b/odoo/fields.py
index 4d07fa26642..7ed3d5762c0 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -69,6 +69,10 @@ def copy_cache(records, env):
for record_id in record_ids:
if record_id in src_cache:
# copy the cached value as such
+ if isinstance(src_cache[record_id], FailedValue):
+ # But not if it's a FailedValue, which often is an access error
+ # because the other environment (eg. sudo()) is well expected to have access.
+ continue
value = dst_cache[record_id] = src_cache[record_id]
if field.relational and isinstance(value, tuple):
todo[field.comodel_name].update(value)
@@ -1535,6 +1539,9 @@ class Date(Field):
""" Convert a :class:`date` value into the format expected by the ORM. """
return value.strftime(DATE_FORMAT) if value else False
+ def convert_to_column(self, value, record, values=None):
+ return super(Date, self).convert_to_column(value or None, record, values)
+
def convert_to_cache(self, value, record, validate=True):
if not value:
return False
@@ -1604,6 +1611,9 @@ class Datetime(Field):
""" Convert a :class:`datetime` value into the format expected by the ORM. """
return value.strftime(DATETIME_FORMAT) if value else False
+ def convert_to_column(self, value, record, values=None):
+ return super(Datetime, self).convert_to_column(value or None, record, values)
+
def convert_to_cache(self, value, record, validate=True):
if not value:
return False
diff --git a/odoo/models.py b/odoo/models.py
index ae54c55aae7..b9fd0b014c6 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -3525,15 +3525,28 @@ class BaseModel(object):
self._write(old_vals)
if new_vals:
- # put the values of pure new-style fields into cache, and inverse them
self.modified(set(new_vals) - set(old_vals))
- for record in self:
- record._cache.update(record._convert_to_cache(new_vals, update=True))
+
+ # put the values of fields into cache, and inverse them
for key in new_vals:
- self._fields[key].determine_inverse(self)
+ field = self._fields[key]
+ # If a field is not stored, its inverse method will probably
+ # write on its dependencies, which will invalidate the field
+ # on all records. We therefore inverse the field one record
+ # at a time.
+ batches = [self] if field.store else list(self)
+ for records in batches:
+ for record in records:
+ record._cache.update(
+ record._convert_to_cache(new_vals, update=True)
+ )
+ field.determine_inverse(records)
+
self.modified(set(new_vals) - set(old_vals))
+
# check Python constraints for inversed fields
self._validate_fields(set(new_vals) - set(old_vals))
+
# recompute new-style fields
if self.env.recompute and self._context.get('recompute', True):
self.recompute()
diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py
index 8d6703a5d17..016559a69ed 100644
--- a/odoo/tools/translate.py
+++ b/odoo/tools/translate.py
@@ -463,7 +463,7 @@ def quote(s):
.replace('\n', '\\n"\n"')
re_escaped_char = re.compile(r"(\\.)")
-re_escaped_replacements = {'n': '\n', }
+re_escaped_replacements = {'n': '\n', 't': '\t',}
def _sub_replacement(match_obj):
return re_escaped_replacements.get(match_obj.group(1)[1], match_obj.group(1)[1])