[FIX] P3: list -> iterable builtins (#16811)
In Python 3: * various builtins and dict methods were changed to return view/iterable objects rather than lists * and the separate Python 2 view/iterable builtins and methods were removed altogether This is problematic when using these items as list (which the happens repeatedly in Odoo), but more viciously when iterating *multiple times* over them (which also happens, which I've messed up multiple times while writing this, and which is a pain to debug even when you've just created the issue). Convert all code using these to semantics-matching cross-version helper functions to get the LCD behaviour between P2 and P3, and forbid the builtins via lint. issue #8530
This commit is contained in:
@@ -383,7 +383,7 @@ class AccountJournal(models.Model):
|
||||
account_code_prefix = company.bank_account_code_prefix or ''
|
||||
else:
|
||||
account_code_prefix = company.cash_account_code_prefix or company.bank_account_code_prefix or ''
|
||||
for num in pycompat.range(1, 100):
|
||||
for num in range(1, 100):
|
||||
new_code = str(account_code_prefix.ljust(code_digits - 1, '0')) + str(num)
|
||||
rec = self.env['account.account'].search([('code', '=', new_code), ('company_id', '=', company.id)], limit=1)
|
||||
if not rec:
|
||||
@@ -412,7 +412,7 @@ class AccountJournal(models.Model):
|
||||
if not vals.get('code'):
|
||||
journal_code_base = (vals['type'] == 'cash' and 'CSH' or 'BNK')
|
||||
journals = self.env['account.journal'].search([('code', 'like', journal_code_base + '%'), ('company_id', '=', company_id)])
|
||||
for num in pycompat.range(1, 100):
|
||||
for num in range(1, 100):
|
||||
# journal_code has a maximal size of 5, hence we can enforce the boundary num < 100
|
||||
journal_code = journal_code_base + str(num)
|
||||
if journal_code not in journals.mapped('code'):
|
||||
|
||||
@@ -808,7 +808,7 @@ class AccountBankStatementLine(models.Model):
|
||||
whose value is the same as described in process_reconciliation except that ids are used instead of recordsets.
|
||||
"""
|
||||
AccountMoveLine = self.env['account.move.line']
|
||||
for st_line, datum in zip(self, data):
|
||||
for st_line, datum in pycompat.izip(self, data):
|
||||
payment_aml_rec = AccountMoveLine.browse(datum.get('payment_aml_ids', []))
|
||||
for aml_dict in datum.get('counterpart_aml_dicts', []):
|
||||
aml_dict['move_line'] = AccountMoveLine.browse(aml_dict['counterpart_aml_id'])
|
||||
@@ -879,7 +879,7 @@ class AccountBankStatementLine(models.Model):
|
||||
for aml_dict in (counterpart_aml_dicts + new_aml_dicts):
|
||||
if aml_dict.get('tax_ids') and isinstance(aml_dict['tax_ids'][0], pycompat.integer_types):
|
||||
# Transform the value in the format required for One2many and Many2many fields
|
||||
aml_dict['tax_ids'] = map(lambda id: (4, id, None), aml_dict['tax_ids'])
|
||||
aml_dict['tax_ids'] = [(4, id, None) for id in aml_dict['tax_ids']]
|
||||
|
||||
# Fully reconciled moves are just linked to the bank statement
|
||||
total = self.amount
|
||||
|
||||
@@ -71,7 +71,7 @@ class AccountInvoice(models.Model):
|
||||
inv_types = inv_type if isinstance(inv_type, list) else [inv_type]
|
||||
company_id = self._context.get('company_id', self.env.user.company_id.id)
|
||||
domain = [
|
||||
('type', 'in', filter(None, map(TYPE2JOURNAL.get, inv_types))),
|
||||
('type', 'in', [TYPE2JOURNAL[ty] for ty in inv_types if ty in TYPE2JOURNAL]),
|
||||
('company_id', '=', company_id),
|
||||
]
|
||||
return self.env['account.journal'].search(domain, limit=1)
|
||||
@@ -192,11 +192,11 @@ class AccountInvoice(models.Model):
|
||||
@api.one
|
||||
@api.depends('move_id.line_ids.amount_residual')
|
||||
def _compute_payments(self):
|
||||
payment_lines = []
|
||||
payment_lines = set()
|
||||
for line in self.move_id.line_ids:
|
||||
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)))
|
||||
payment_lines.update(line.mapped('matched_credit_ids.credit_move_id.id'))
|
||||
payment_lines.update(line.mapped('matched_debit_ids.debit_move_id.id'))
|
||||
self.payment_move_line_ids = self.env['account.move.line'].browse(list(payment_lines))
|
||||
|
||||
name = fields.Char(string='Reference/Description', index=True,
|
||||
readonly=True, states={'draft': [('readonly', False)]}, copy=False, help='The name that will be used on account move lines')
|
||||
@@ -335,7 +335,7 @@ class AccountInvoice(models.Model):
|
||||
'_onchange_partner_id': ['account_id', 'payment_term_id', 'fiscal_position_id', 'partner_bank_id'],
|
||||
'_onchange_journal_id': ['currency_id'],
|
||||
}
|
||||
for onchange_method, changed_fields in onchanges.items():
|
||||
for onchange_method, changed_fields in pycompat.items(onchanges):
|
||||
if any(f not in vals for f in changed_fields):
|
||||
invoice = self.new(vals)
|
||||
getattr(invoice, onchange_method)()
|
||||
@@ -456,7 +456,7 @@ class AccountInvoice(models.Model):
|
||||
tax_grouped = invoice.get_taxes_values()
|
||||
|
||||
# Create new tax lines
|
||||
for tax in tax_grouped.values():
|
||||
for tax in pycompat.values(tax_grouped):
|
||||
account_invoice_tax.create(tax)
|
||||
|
||||
# dummy write on self to trigger recomputations
|
||||
@@ -475,7 +475,7 @@ class AccountInvoice(models.Model):
|
||||
def _onchange_invoice_line_ids(self):
|
||||
taxes_grouped = self.get_taxes_values()
|
||||
tax_lines = self.tax_line_ids.browse([])
|
||||
for tax in taxes_grouped.values():
|
||||
for tax in pycompat.values(taxes_grouped):
|
||||
tax_lines += tax_lines.new(tax)
|
||||
self.tax_line_ids = tax_lines
|
||||
return
|
||||
@@ -820,7 +820,7 @@ class AccountInvoice(models.Model):
|
||||
else:
|
||||
line2[tmp] = l
|
||||
line = []
|
||||
for key, val in line2.items():
|
||||
for key, val in pycompat.items(line2):
|
||||
line.append((0, 0, val))
|
||||
return line
|
||||
|
||||
@@ -1017,7 +1017,7 @@ class AccountInvoice(models.Model):
|
||||
result = []
|
||||
for line in lines:
|
||||
values = {}
|
||||
for name, field in line._fields.iteritems():
|
||||
for name, field in pycompat.items(line._fields):
|
||||
if name in MAGIC_COLUMNS:
|
||||
continue
|
||||
elif field.type == 'many2one':
|
||||
@@ -1155,8 +1155,8 @@ class AccountInvoice(models.Model):
|
||||
for line in self.tax_line_ids:
|
||||
res.setdefault(line.tax_id.tax_group_id, 0.0)
|
||||
res[line.tax_id.tax_group_id] += line.amount
|
||||
res = sorted(res.items(), key=lambda l: l[0].sequence)
|
||||
res = map(lambda l: (l[0].name, l[1]), res)
|
||||
res = sorted(pycompat.items(res), key=lambda l: l[0].sequence)
|
||||
res = [(l[0].name, l[1]) for l in res]
|
||||
return res
|
||||
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ class account_journal(models.Model):
|
||||
""", (tuple(self.ids),))
|
||||
number_to_reconcile = self.env.cr.fetchone()[0]
|
||||
# optimization to read sum of balance from account_move_line
|
||||
account_ids = tuple(filter(None, [self.default_debit_account_id.id, self.default_credit_account_id.id]))
|
||||
account_ids = tuple(ac for ac in [self.default_debit_account_id.id, self.default_credit_account_id.id] if ac)
|
||||
if account_ids:
|
||||
amount_field = 'balance' if not self.currency_id else 'amount_currency'
|
||||
query = """SELECT sum(%s) FROM account_move_line WHERE account_id in %%s;""" % (amount_field,)
|
||||
|
||||
@@ -1696,7 +1696,7 @@ class AccountPartialReconcile(models.Model):
|
||||
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.keys()]
|
||||
partial_rec_ids = [x.id for x in partial_rec_set]
|
||||
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
|
||||
|
||||
@@ -5,6 +5,9 @@ from odoo import api, fields, models, _
|
||||
from odoo import SUPERUSER_ID
|
||||
|
||||
import logging
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def migrate_set_tags_and_taxes_updatable(cr, registry, module):
|
||||
@@ -322,7 +325,7 @@ class AccountChartTemplate(models.Model):
|
||||
|
||||
# writing account values after creation of accounts
|
||||
company.transfer_account_id = account_template_ref[transfer_account_id.id]
|
||||
for key, value in generated_tax_res['account_dict'].items():
|
||||
for key, value in pycompat.items(generated_tax_res['account_dict']):
|
||||
if value['refund_account_id'] or value['account_id'] or value['cash_basis_account']:
|
||||
AccountTaxObj.browse(key).write({
|
||||
'refund_account_id': account_ref.get(value['refund_account_id'], False),
|
||||
|
||||
@@ -4,7 +4,7 @@ from operator import itemgetter
|
||||
import time
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, pycompat
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.addons.base.res.res_partner import WARNING_MESSAGE, WARNING_HELP
|
||||
|
||||
@@ -72,7 +72,7 @@ class AccountFiscalPosition(models.Model):
|
||||
ref_dict = {}
|
||||
for line in self.account_ids:
|
||||
ref_dict[line.account_src_id] = line.account_dest_id
|
||||
for key, acc in accounts.items():
|
||||
for key, acc in list(pycompat.items(accounts)):
|
||||
if acc in ref_dict:
|
||||
accounts[key] = ref_dict[acc]
|
||||
return accounts
|
||||
@@ -245,7 +245,7 @@ class ResPartner(models.Model):
|
||||
res = self._cr.fetchall()
|
||||
if not res:
|
||||
return [('id', '=', '0')]
|
||||
return [('id', 'in', map(itemgetter(0), res))]
|
||||
return [('id', 'in', [r[0] for r in res])]
|
||||
|
||||
@api.model
|
||||
def _credit_search(self, operator, operand):
|
||||
@@ -292,7 +292,7 @@ class ResPartner(models.Model):
|
||||
""" % where_clause
|
||||
self.env.cr.execute(query, where_clause_params)
|
||||
price_totals = self.env.cr.dictfetchall()
|
||||
for partner, child_ids in all_partners_and_children.items():
|
||||
for partner, child_ids in pycompat.items(all_partners_and_children):
|
||||
partner.total_invoiced = sum(price['total'] for price in price_totals if price['partner_id'] in child_ids)
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -44,7 +44,7 @@ class ReportTrialBalance(models.AbstractModel):
|
||||
currency = account.currency_id and account.currency_id or account.company_id.currency_id
|
||||
res['code'] = account.code
|
||||
res['name'] = account.name
|
||||
if account.id in account_result.keys():
|
||||
if account.id in account_result:
|
||||
res['debit'] = account_result[account.id].get('debit')
|
||||
res['credit'] = account_result[account.id].get('credit')
|
||||
res['balance'] = account_result[account.id].get('balance')
|
||||
|
||||
@@ -27,7 +27,7 @@ class ReportGeneralLedger(models.AbstractModel):
|
||||
"""
|
||||
cr = self.env.cr
|
||||
MoveLine = self.env['account.move.line']
|
||||
move_lines = dict(map(lambda x: (x, []), accounts.ids))
|
||||
move_lines = {x: [] for x in accounts.ids}
|
||||
|
||||
# Prepare initial sql query and Get the initial move lines
|
||||
if init_balance:
|
||||
|
||||
@@ -24,7 +24,7 @@ class ReportJournal(models.AbstractModel):
|
||||
query += 'am.name'
|
||||
query += ', "account_move_line".move_id, acc.code'
|
||||
self.env.cr.execute(query, tuple(params))
|
||||
ids = map(lambda x: x[0], self.env.cr.fetchall())
|
||||
ids = (x[0] for x in self.env.cr.fetchall())
|
||||
return self.env['account.move.line'].browse(ids)
|
||||
|
||||
def _sum_debit(self, data, journal_id):
|
||||
|
||||
@@ -8,7 +8,7 @@ class ReportOverdue(models.AbstractModel):
|
||||
_name = 'report.account.report_overdue'
|
||||
|
||||
def _get_account_move_lines(self, partner_ids):
|
||||
res = dict(map(lambda x:(x,[]), partner_ids))
|
||||
res = {x: [] for x in partner_ids}
|
||||
self.env.cr.execute("SELECT m.name AS move_id, l.date, l.name, l.ref, l.date_maturity, l.partner_id, l.blocked, l.amount_currency, l.currency_id, "
|
||||
"CASE WHEN at.type = 'receivable' "
|
||||
"THEN SUM(l.debit) "
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import time
|
||||
from odoo import api, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class ReportFinancial(models.AbstractModel):
|
||||
@@ -18,7 +19,7 @@ class ReportFinancial(models.AbstractModel):
|
||||
|
||||
res = {}
|
||||
for account in accounts:
|
||||
res[account.id] = dict((fn, 0.0) for fn in mapping.keys())
|
||||
res[account.id] = dict.fromkeys(mapping, 0.0)
|
||||
if accounts:
|
||||
tables, where_clause, where_params = self.env['account.move.line']._query_get()
|
||||
tables = tables.replace('"', '') if tables else "account_move_line"
|
||||
@@ -26,7 +27,7 @@ class ReportFinancial(models.AbstractModel):
|
||||
if where_clause.strip():
|
||||
wheres.append(where_clause.strip())
|
||||
filters = " AND ".join(wheres)
|
||||
request = "SELECT account_id as id, " + ', '.join(mapping.values()) + \
|
||||
request = "SELECT account_id as id, " + ', '.join(pycompat.values(mapping)) + \
|
||||
" FROM " + tables + \
|
||||
" WHERE account_id IN %s " \
|
||||
+ filters + \
|
||||
@@ -53,26 +54,26 @@ class ReportFinancial(models.AbstractModel):
|
||||
if report.type == 'accounts':
|
||||
# it's the sum of the linked accounts
|
||||
res[report.id]['account'] = self._compute_account_balance(report.account_ids)
|
||||
for value in res[report.id]['account'].values():
|
||||
for value in pycompat.values(res[report.id]['account']):
|
||||
for field in fields:
|
||||
res[report.id][field] += value.get(field)
|
||||
elif report.type == 'account_type':
|
||||
# it's the sum the leaf accounts with such an account type
|
||||
accounts = self.env['account.account'].search([('user_type_id', 'in', report.account_type_ids.ids)])
|
||||
res[report.id]['account'] = self._compute_account_balance(accounts)
|
||||
for value in res[report.id]['account'].values():
|
||||
for value in pycompat.values(res[report.id]['account']):
|
||||
for field in fields:
|
||||
res[report.id][field] += value.get(field)
|
||||
elif report.type == 'account_report' and report.account_report_id:
|
||||
# it's the amount of the linked report
|
||||
res2 = self._compute_report_balance(report.account_report_id)
|
||||
for key, value in res2.items():
|
||||
for key, value in pycompat.items(res2):
|
||||
for field in fields:
|
||||
res[report.id][field] += value[field]
|
||||
elif report.type == 'sum':
|
||||
# it's the sum of the children of this account.report
|
||||
res2 = self._compute_report_balance(report.children_ids)
|
||||
for key, value in res2.items():
|
||||
for key, value in pycompat.items(res2):
|
||||
for field in fields:
|
||||
res[report.id][field] += value[field]
|
||||
return res
|
||||
@@ -84,11 +85,11 @@ class ReportFinancial(models.AbstractModel):
|
||||
res = self.with_context(data.get('used_context'))._compute_report_balance(child_reports)
|
||||
if data['enable_filter']:
|
||||
comparison_res = self.with_context(data.get('comparison_context'))._compute_report_balance(child_reports)
|
||||
for report_id, value in comparison_res.items():
|
||||
for report_id, value in pycompat.items(comparison_res):
|
||||
res[report_id]['comp_bal'] = value['balance']
|
||||
report_acc = res[report_id].get('account')
|
||||
if report_acc:
|
||||
for account_id, val in comparison_res[report_id].get('account').items():
|
||||
for account_id, val in pycompat.items(comparison_res[report_id].get('account')):
|
||||
report_acc[account_id]['comp_bal'] = val['balance']
|
||||
|
||||
for report in child_reports:
|
||||
@@ -113,7 +114,7 @@ class ReportFinancial(models.AbstractModel):
|
||||
|
||||
if res[report.id].get('account'):
|
||||
sub_lines = []
|
||||
for account_id, value in res[report.id]['account'].items():
|
||||
for account_id, value in pycompat.items(res[report.id]['account']):
|
||||
#if there are accounts to display, we add them to the lines with a level equals to their level in
|
||||
#the COA + 1 (to avoid having them with a too low level that would conflicts with the level of data
|
||||
#financial reports for Assets, liabilities...)
|
||||
|
||||
@@ -2,6 +2,9 @@ from odoo.addons.account.tests.account_test_classes import AccountingTestCase
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class TestReconciliation(AccountingTestCase):
|
||||
|
||||
"""Tests for reconciliation (account.tax)
|
||||
@@ -336,8 +339,8 @@ class TestReconciliation(AccountingTestCase):
|
||||
self.assertTrue(exchange_loss_line, 'There should be one move line of 0.01 EUR in credit')
|
||||
# The journal items of the reconciliation should have their debit and credit total equal
|
||||
# Besides, the total debit and total credit should be 60.61 EUR (2.00 USD)
|
||||
self.assertEquals(sum([res['debit'] for res in result.values()]), 60.61)
|
||||
self.assertEquals(sum([res['credit'] for res in result.values()]), 60.61)
|
||||
self.assertEquals(sum(res['debit'] for res in pycompat.values(result)), 60.61)
|
||||
self.assertEquals(sum(res['credit'] for res in pycompat.items(result)), 60.61)
|
||||
counterpart_exchange_loss_line = None
|
||||
for line in exchange_loss_line.move_id.line_id:
|
||||
if line.account_id.id == self.account_fx_expense_id:
|
||||
@@ -473,4 +476,4 @@ class TestReconciliation(AccountingTestCase):
|
||||
self.assertEquals(round(aml.debit, 2), line['debit'])
|
||||
self.assertEquals(round(aml.credit, 2), line['credit'])
|
||||
self.assertEquals(round(aml.amount_currency, 2), line['amount_currency'])
|
||||
self.assertEquals(aml.currency_id.id, line['currency_id'])
|
||||
self.assertEquals(aml.currency_id.id, line['currency_id'])
|
||||
|
||||
@@ -7,7 +7,7 @@ from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF, pycompat
|
||||
from odoo.tools import float_compare, float_is_zero
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ class AccountAssetAsset(models.Model):
|
||||
vals = self.onchange_category_id_values(self.category_id.id)
|
||||
# We cannot use 'write' on an object that doesn't exist yet
|
||||
if vals:
|
||||
for k, v in vals['value'].iteritems():
|
||||
for k, v in pycompat.items(vals['value']):
|
||||
setattr(self, k, v)
|
||||
|
||||
def onchange_category_id_values(self, category_id):
|
||||
@@ -583,7 +583,7 @@ class AccountAssetDepreciationLine(models.Model):
|
||||
message = ''
|
||||
if message_description:
|
||||
message = '<span>%s</span>' % message_description
|
||||
for name, values in tracked_values.iteritems():
|
||||
for name, values in pycompat.items(tracked_values):
|
||||
message += '<div> • <b>%s</b>: ' % name
|
||||
message += '%s</div>' % values
|
||||
return message
|
||||
|
||||
@@ -22,6 +22,6 @@ class AssetDepreciationConfirmationWizard(models.TransientModel):
|
||||
'view_mode': 'tree,form',
|
||||
'res_model': 'account.move',
|
||||
'view_id': False,
|
||||
'domain': "[('id','in',[" + ','.join(map(str, created_move_ids)) + "])]",
|
||||
'domain': "[('id','in',[" + ','.join(str(id) for id in created_move_ids) + "])]",
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ class ReportAssertAccount(models.AbstractModel):
|
||||
:rtype: [(key, value)]
|
||||
"""
|
||||
if cols is None:
|
||||
cols = item.keys()
|
||||
return [(col, item.get(col)) for col in cols if col in item.keys()]
|
||||
cols = list(item)
|
||||
return [(col, item.get(col)) for col in cols if col in item]
|
||||
|
||||
localdict = {
|
||||
'cr': self.env.cr,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
from odoo import fields, models, api, _
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class AccountVoucher(models.Model):
|
||||
@@ -357,7 +358,7 @@ class AccountVoucherLine(models.Model):
|
||||
self.company_id.id,
|
||||
self.voucher_id.currency_id.id,
|
||||
self.voucher_id.voucher_type)
|
||||
for fname, fvalue in onchange_res['value'].iteritems():
|
||||
for fname, fvalue in pycompat.items(onchange_res['value']):
|
||||
setattr(self, fname, fvalue)
|
||||
|
||||
def _get_account(self, product, fpos, type):
|
||||
|
||||
@@ -76,7 +76,7 @@ class IrModelFieldsAnonymization(models.Model):
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
# check field state: all should be clear before we can modify a field:
|
||||
if not len(vals.keys()) == 1 and vals.get('state') == 'clear':
|
||||
if not len(vals) == 1 and vals.get('state') == 'clear':
|
||||
self._check_write()
|
||||
if vals.get('field_name') and vals.get('model_name'):
|
||||
vals['model_id'], vals['field_id'] = self._get_model_and_field_ids(vals)
|
||||
|
||||
@@ -270,7 +270,7 @@ class IrModelFieldsAnonymizeWizard(models.TransientModel):
|
||||
data = pickle.loads(base64.decodestring(self.file_import))
|
||||
|
||||
fixes = self.env['ir.model.fields.anonymization.migration.fix'].search_read([
|
||||
('target_version', '=', '.'.join(map(str, version_info[:2])))
|
||||
('target_version', '=', '.'.join(str(v) for v in version_info[:2]))
|
||||
], ['model_name', 'field_name', 'query', 'query_type', 'sequence'])
|
||||
fixes = group(fixes, ('model_name', 'field_name'))
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from odoo import http, _
|
||||
from odoo.addons.auth_signup.models.res_users import SignupError
|
||||
from odoo.addons.web.controllers.main import ensure_db, Home
|
||||
from odoo.http import request
|
||||
from odoo.tools import pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -86,7 +87,7 @@ class AuthSignupHome(Home):
|
||||
try:
|
||||
# retrieve the user info (name, login or email) corresponding to a signup token
|
||||
token_infos = request.env['res.partner'].sudo().signup_retrieve_info(qcontext.get('token'))
|
||||
for k, v in token_infos.items():
|
||||
for k, v in pycompat.items(token_infos):
|
||||
qcontext.setdefault(k, v)
|
||||
except:
|
||||
qcontext['error'] = _("Invalid signup token")
|
||||
@@ -96,7 +97,7 @@ class AuthSignupHome(Home):
|
||||
def do_signup(self, qcontext):
|
||||
""" Shared helper that creates a res.partner out of a token """
|
||||
values = { key: qcontext.get(key) for key in ('login', 'name', 'password') }
|
||||
assert values.values(), "The form was not properly filled in."
|
||||
assert values, "The form was not properly filled in."
|
||||
assert values.get('password') == qcontext.get('confirm_password'), "Passwords do not match; please retype them."
|
||||
supported_langs = [lang['code'] for lang in request.env['res.lang'].sudo().search_read([], ['code'])]
|
||||
if request.lang in supported_langs:
|
||||
|
||||
@@ -17,7 +17,7 @@ class SignupError(Exception):
|
||||
def random_token():
|
||||
# the token has an entropy of about 120 bits (6 bits/char * 20 chars)
|
||||
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
return ''.join(random.SystemRandom().choice(chars) for _ in pycompat.range(20))
|
||||
return ''.join(random.SystemRandom().choice(chars) for _ in range(20))
|
||||
|
||||
def now(**kwargs):
|
||||
dt = datetime.now() + timedelta(**kwargs)
|
||||
|
||||
@@ -5,7 +5,7 @@ import re
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
STREET_FIELDS = ('street_name', 'street_number', 'street_number2')
|
||||
|
||||
@@ -135,7 +135,7 @@ class Partner(models.Model):
|
||||
vals[field_name] = street_raw
|
||||
# assign the values to the fields
|
||||
# /!\ Note that a write(vals) would cause a recursion since it would bypass the cache
|
||||
for k, v in vals.items():
|
||||
for k, v in pycompat.items(vals):
|
||||
partner[k] = v
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models, SUPERUSER_ID
|
||||
from odoo.modules.registry import Registry
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -263,7 +264,7 @@ class BaseAutomation(models.Model):
|
||||
if res:
|
||||
if 'value' in res:
|
||||
res['value'].pop('id', None)
|
||||
self.update({key: val for key, val in res['value'].iteritems() if key in self._fields})
|
||||
self.update({key: val for key, val in pycompat.items(res['value']) if key in self._fields})
|
||||
if 'domain' in res:
|
||||
result.setdefault('domain', {}).update(res['domain'])
|
||||
if 'warning' in res:
|
||||
|
||||
@@ -8,6 +8,7 @@ import uuid
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -193,7 +194,7 @@ class BaseGengoTranslations(models.TransientModel):
|
||||
term_ids.write(vals)
|
||||
jobs = response.get('jobs', [])
|
||||
if jobs:
|
||||
for t_id, job in jobs.items():
|
||||
for t_id, job in pycompat.items(jobs):
|
||||
self._update_terms_job(job)
|
||||
|
||||
return
|
||||
|
||||
@@ -31,10 +31,10 @@ def geo_query_address(street=None, zip=None, city=None, state=None, country=None
|
||||
# put country qualifier in front, otherwise GMap gives wrong results,
|
||||
# e.g. 'Congo, Democratic Republic of the' => 'Democratic Republic of the Congo'
|
||||
country = '{1} {0}'.format(*country.split(',', 1))
|
||||
return tools.ustr(', '.join(filter(None, [street,
|
||||
("%s %s" % (zip or '', city or '')).strip(),
|
||||
state,
|
||||
country])))
|
||||
return tools.ustr(', '.join(
|
||||
field for field in [street, ("%s %s" % (zip or '', city or '')).strip(), state, country]
|
||||
if field
|
||||
))
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
|
||||
@@ -15,7 +15,7 @@ from odoo import api, fields, models
|
||||
from odoo.tools.translate import _
|
||||
from odoo.tools.mimetypes import guess_mimetype
|
||||
from odoo.tools.misc import ustr
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, pycompat
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
@@ -49,7 +49,7 @@ FILE_TYPE_DICT = {
|
||||
}
|
||||
EXTENSIONS = {
|
||||
'.' + ext: handler
|
||||
for mime, (ext, handler, req) in FILE_TYPE_DICT.iteritems()
|
||||
for mime, (ext, handler, req) in pycompat.items(FILE_TYPE_DICT)
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ class Import(models.TransientModel):
|
||||
}]
|
||||
model_fields = Model.fields_get()
|
||||
blacklist = models.MAGIC_COLUMNS + [Model.CONCURRENCY_CHECK_FIELD]
|
||||
for name, field in model_fields.iteritems():
|
||||
for name, field in pycompat.items(model_fields):
|
||||
if name in blacklist:
|
||||
continue
|
||||
# an empty string means the field is deprecated, @deprecated must
|
||||
@@ -134,7 +134,7 @@ class Import(models.TransientModel):
|
||||
continue
|
||||
# states = {state: [(attr, value), (attr2, value2)], state2:...}
|
||||
if not any(attr == 'readonly' and value is False
|
||||
for attr, value in itertools.chain.from_iterable(states.itervalues())):
|
||||
for attr, value in itertools.chain.from_iterable(pycompat.values(states))):
|
||||
continue
|
||||
field_value = {
|
||||
'id': name,
|
||||
@@ -208,7 +208,7 @@ class Import(models.TransientModel):
|
||||
def _read_xls_book(self, book):
|
||||
sheet = book.sheet_by_index(0)
|
||||
# emulate Sheet.get_rows for pre-0.9.4
|
||||
for row in itertools.imap(sheet.row, range(sheet.nrows)):
|
||||
for row in pycompat.imap(sheet.row, range(sheet.nrows)):
|
||||
values = []
|
||||
for cell in row:
|
||||
if cell.ctype is xlrd.XL_CELL_NUMBER:
|
||||
@@ -543,13 +543,13 @@ class Import(models.TransientModel):
|
||||
else:
|
||||
mapper = operator.itemgetter(*indices)
|
||||
# Get only list of actually imported fields
|
||||
import_fields = filter(None, fields)
|
||||
import_fields = [f for f in fields if f]
|
||||
|
||||
rows_to_import = self._read_file(options)
|
||||
if options.get('headers'):
|
||||
rows_to_import = itertools.islice(rows_to_import, 1, None)
|
||||
data = [
|
||||
list(row) for row in itertools.imap(mapper, rows_to_import)
|
||||
list(row) for row in pycompat.imap(mapper, rows_to_import)
|
||||
# don't try inserting completely empty rows (e.g. from
|
||||
# filtering out o2m fields)
|
||||
if any(row)
|
||||
@@ -566,7 +566,7 @@ class Import(models.TransientModel):
|
||||
value = value[1:-1]
|
||||
negative = True
|
||||
float_regex = re.compile(r'([-]?[0-9.,]+)')
|
||||
split_value = filter(None, float_regex.split(value))
|
||||
split_value = [g for g in float_regex.split(value) if g]
|
||||
if len(split_value) > 2:
|
||||
# This is probably not a float
|
||||
return False
|
||||
@@ -603,7 +603,7 @@ class Import(models.TransientModel):
|
||||
def _parse_import_data(self, data, import_fields, options):
|
||||
# Get fields of type date/datetime
|
||||
all_fields = self.env[self.res_model].fields_get()
|
||||
for name, field in all_fields.iteritems():
|
||||
for name, field in pycompat.items(all_fields):
|
||||
if field['type'] in ('date', 'datetime') and name in import_fields:
|
||||
# Parse date
|
||||
index = import_fields.index(name)
|
||||
|
||||
@@ -19,6 +19,8 @@ from odf import opendocument
|
||||
from odf.table import Table, TableRow, TableCell
|
||||
from odf.text import P
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class ODSReader(object):
|
||||
|
||||
@@ -95,4 +97,4 @@ class ODSReader(object):
|
||||
return self.SHEETS[name]
|
||||
|
||||
def getFirstSheet(self):
|
||||
return next(iter(self.SHEETS.itervalues()))
|
||||
return next(iter(pycompat.values(self.SHEETS)))
|
||||
|
||||
@@ -9,7 +9,6 @@ from odoo.tests.common import TransactionCase, can_import
|
||||
from odoo.modules.module import get_module_resource
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
ID_FIELD = {
|
||||
'id': 'id',
|
||||
'name': 'id',
|
||||
@@ -260,7 +259,7 @@ class TestPreview(TransactionCase):
|
||||
['qux', '5', '6'],
|
||||
])
|
||||
# Ensure we only have the response fields we expect
|
||||
self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug'])
|
||||
self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug'])
|
||||
|
||||
@unittest.skipUnless(can_import('xlrd'), "XLRD module not available")
|
||||
def test_xls_success(self):
|
||||
@@ -290,7 +289,7 @@ class TestPreview(TransactionCase):
|
||||
['qux', '5', '6'],
|
||||
])
|
||||
# Ensure we only have the response fields we expect
|
||||
self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug'])
|
||||
self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug'])
|
||||
|
||||
@unittest.skipUnless(can_import('xlrd.xlsx'), "XLRD/XLSX not available")
|
||||
def test_xlsx_success(self):
|
||||
@@ -320,7 +319,7 @@ class TestPreview(TransactionCase):
|
||||
['qux', '5', '6'],
|
||||
])
|
||||
# Ensure we only have the response fields we expect
|
||||
self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options','advanced_mode', 'debug'])
|
||||
self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options','advanced_mode', 'debug'])
|
||||
|
||||
@unittest.skipUnless(can_import('odf'), "ODFPY not available")
|
||||
def test_ods_success(self):
|
||||
@@ -350,7 +349,7 @@ class TestPreview(TransactionCase):
|
||||
['aux', '5', '6'],
|
||||
])
|
||||
# Ensure we only have the response fields we expect
|
||||
self.assertItemsEqual(result.keys(), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug'])
|
||||
self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug'])
|
||||
|
||||
|
||||
class test_convert_import_data(TransactionCase):
|
||||
|
||||
@@ -8,7 +8,7 @@ from os.path import join as opj
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.modules import load_information_from_description_file
|
||||
from odoo.tools import convert_file, exception_to_unicode
|
||||
from odoo.tools import convert_file, exception_to_unicode, pycompat
|
||||
from odoo.tools.osutil import tempdir
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -113,6 +113,6 @@ class IrModule(models.Model):
|
||||
_logger.exception('Error while importing module')
|
||||
errors[mod_name] = exception_to_unicode(e)
|
||||
r = ["Successfully imported module '%s'" % mod for mod in success]
|
||||
for mod, error in errors.items():
|
||||
for mod, error in pycompat.items(errors):
|
||||
r.append("Error while importing module '%s': %r" % (mod, error))
|
||||
return '\n'.join(r), module_names
|
||||
|
||||
@@ -23,6 +23,6 @@ class IrUiView(models.Model):
|
||||
GROUP BY coalesce(v.inherit_id, v.id)
|
||||
""", [model])
|
||||
|
||||
ids = map(itemgetter(0), self._cr.fetchall())
|
||||
ids = (row[0] for row in self._cr.fetchall())
|
||||
views = self.with_context(load_all_views=True).browse(ids)
|
||||
return views._check_xml() and result
|
||||
|
||||
@@ -154,7 +154,7 @@ class ResPartner(models.Model):
|
||||
match = self.__check_vat_ch_re2.match(vat)
|
||||
if match:
|
||||
# For new TVA numbers, do a mod11 check
|
||||
num = filter(lambda s: s.isdigit(), match.group(1)) # get the digits only
|
||||
num = [s for s in match.group(1) if s.isdigit()] # get the digits only
|
||||
factor = (5, 4, 3, 2, 7, 6, 5, 4)
|
||||
csum = sum([int(num[i]) * factor[i] for i in range(8)])
|
||||
check = (11 - (csum % 11)) % 11
|
||||
|
||||
@@ -34,7 +34,7 @@ def calendar_id2real_id(calendar_id=None, with_date=False):
|
||||
:return: real event id
|
||||
"""
|
||||
if calendar_id and isinstance(calendar_id, (basestring)):
|
||||
res = filter(None, calendar_id.split('-'))
|
||||
res = [bit for bit in calendar_id.split('-') if bit]
|
||||
if len(res) == 2:
|
||||
real_id = res[0]
|
||||
if with_date:
|
||||
@@ -127,7 +127,7 @@ class Attendee(models.Model):
|
||||
def create(self, values):
|
||||
if not values.get("email") and values.get("common_name"):
|
||||
common_nameval = values.get("common_name").split(':')
|
||||
email = filter(lambda x: x.__contains__('@'), common_nameval) # TODO JEM : should be refactored
|
||||
email = [x for x in common_nameval if '@' in x] # TODO JEM : should be refactored
|
||||
values['email'] = email and email[0] or ''
|
||||
values['common_name'] = values.get("common_name")
|
||||
return super(Attendee, self).create(values)
|
||||
@@ -354,7 +354,7 @@ class AlarmManager(models.AbstractModel):
|
||||
|
||||
all_meetings = self.get_next_potential_limit_alarm('email', seconds=cron_interval)
|
||||
|
||||
for meeting in self.env['calendar.event'].browse(all_meetings.keys()):
|
||||
for meeting in self.env['calendar.event'].browse(all_meetings):
|
||||
max_delta = all_meetings[meeting.id]['max_duration']
|
||||
|
||||
if meeting.recurrency:
|
||||
@@ -468,7 +468,7 @@ class Alarm(models.Model):
|
||||
name = fields.Char('Name', required=True)
|
||||
type = fields.Selection([('notification', 'Notification'), ('email', 'Email')], 'Type', required=True, default='email')
|
||||
duration = fields.Integer('Remind Before', required=True, default=1)
|
||||
interval = fields.Selection(list(_interval_selection.iteritems()), 'Unit', required=True, default='hours')
|
||||
interval = fields.Selection(list(pycompat.items(_interval_selection)), 'Unit', required=True, default='hours')
|
||||
duration_minutes = fields.Integer('Duration in minutes', compute='_compute_duration_minutes', store=True, help="Duration in minutes")
|
||||
|
||||
@api.onchange('duration', 'interval')
|
||||
@@ -541,7 +541,7 @@ class Meeting(models.Model):
|
||||
""" Get recurrent start and stop dates based on Rule string"""
|
||||
start_dates = self._get_recurrent_date_by_event(date_field='start')
|
||||
stop_dates = self._get_recurrent_date_by_event(date_field='stop')
|
||||
return zip(start_dates, stop_dates)
|
||||
return list(pycompat.izip(start_dates, stop_dates))
|
||||
|
||||
@api.multi
|
||||
def _get_recurrent_date_by_event(self, date_field='start'):
|
||||
@@ -550,7 +550,7 @@ class Meeting(models.Model):
|
||||
date_field: the field containing the reference date information for recurrency computation
|
||||
"""
|
||||
self.ensure_one()
|
||||
if date_field in self._fields.keys() and self._fields[date_field].type in ('date', 'datetime'):
|
||||
if date_field in self._fields and self._fields[date_field].type in ('date', 'datetime'):
|
||||
reference_date = self[date_field]
|
||||
else:
|
||||
reference_date = self.start
|
||||
@@ -1157,7 +1157,7 @@ class Meeting(models.Model):
|
||||
data['final_date'] = rule._until and rule._until.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
#repeat weekly
|
||||
if rule._byweekday:
|
||||
for i in pycompat.range(0, 7):
|
||||
for i in range(0, 7):
|
||||
if i in rule._byweekday:
|
||||
data[day_list[i]] = True
|
||||
data['rrule_type'] = 'weekly'
|
||||
@@ -1286,7 +1286,7 @@ class Meeting(models.Model):
|
||||
@api.multi
|
||||
def _get_message_unread(self):
|
||||
id_map = {x: calendar_id2real_id(x) for x in self.ids}
|
||||
real = self.browse(set(id_map.values()))
|
||||
real = self.browse(set(pycompat.values(id_map)))
|
||||
super(Meeting, real)._get_message_unread()
|
||||
for event in self:
|
||||
if event.id == id_map[event.id]:
|
||||
@@ -1298,7 +1298,7 @@ class Meeting(models.Model):
|
||||
@api.multi
|
||||
def _get_message_needaction(self):
|
||||
id_map = {x: calendar_id2real_id(x) for x in self.ids}
|
||||
real = self.browse(set(id_map.values()))
|
||||
real = self.browse(set(pycompat.values(id_map)))
|
||||
super(Meeting, real)._get_message_needaction()
|
||||
for event in self:
|
||||
if event.id == id_map[event.id]:
|
||||
@@ -1339,7 +1339,7 @@ class Meeting(models.Model):
|
||||
|
||||
@api.multi
|
||||
def get_metadata(self):
|
||||
real = self.browse(set({x: calendar_id2real_id(x) for x in self.ids}.values()))
|
||||
real = self.browse({calendar_id2real_id(x) for x in self.ids})
|
||||
return super(Meeting, real).get_metadata()
|
||||
|
||||
@api.model
|
||||
@@ -1460,7 +1460,7 @@ class Meeting(models.Model):
|
||||
if fields and (f not in fields):
|
||||
fields2.append(f)
|
||||
|
||||
select = map(lambda x: (x, calendar_id2real_id(x)), self.ids)
|
||||
select = [(x, calendar_id2real_id(x)) for x in self.ids]
|
||||
real_events = self.browse([real_id for calendar_id, real_id in select])
|
||||
real_data = super(Meeting, real_events).read(fields=fields2, load=load)
|
||||
real_data = dict((d['id'], d) for d in real_data)
|
||||
@@ -1493,7 +1493,7 @@ class Meeting(models.Model):
|
||||
if user_id == self.env.user.id or partner_id in r.get("partner_ids", []):
|
||||
continue
|
||||
if r['privacy'] == 'private':
|
||||
for f in r.keys():
|
||||
for f in r:
|
||||
recurrent_fields = self._get_recurrent_fields()
|
||||
public_fields = list(set(recurrent_fields + ['id', 'allday', 'start', 'stop', 'display_start', 'display_stop', 'duration', 'user_id', 'state', 'interval', 'count', 'recurrent_id_date', 'rrule']))
|
||||
if f not in public_fields:
|
||||
|
||||
@@ -19,13 +19,13 @@ class Message(models.Model):
|
||||
if isinstance(args[index][2], basestring):
|
||||
args[index] = (args[index][0], args[index][1], get_real_ids(args[index][2]))
|
||||
elif isinstance(args[index][2], list):
|
||||
args[index] = (args[index][0], args[index][1], map(lambda x: get_real_ids(x), args[index][2]))
|
||||
args[index] = (args[index][0], args[index][1], [get_real_ids(x) for x in args[index][2]])
|
||||
return super(Message, self).search(args, offset=offset, limit=limit, order=order, count=count)
|
||||
|
||||
@api.model
|
||||
def _find_allowed_model_wise(self, doc_model, doc_dict):
|
||||
if doc_model == 'calendar.event':
|
||||
order = self._context.get('order', self._order)
|
||||
for virtual_id in self.env[doc_model].browse(doc_dict.keys()).get_recurrent_ids([], order=order):
|
||||
for virtual_id in self.env[doc_model].browse(doc_dict).get_recurrent_ids([], order=order):
|
||||
doc_dict.setdefault(virtual_id, doc_dict[get_real_ids(virtual_id)])
|
||||
return super(Message, self)._find_allowed_model_wise(doc_model, doc_dict)
|
||||
|
||||
@@ -433,9 +433,6 @@ class Lead(models.Model):
|
||||
res = _get_first_not_null(attr, opportunities)
|
||||
return res.id if res else False
|
||||
|
||||
def _concat_all(attr, opportunities):
|
||||
return '\n\n'.join(filter(None, (opp[attr] for opp in opportunities)))
|
||||
|
||||
# process the fields' values
|
||||
data = {}
|
||||
for field_name in fields:
|
||||
@@ -447,7 +444,7 @@ class Lead(models.Model):
|
||||
elif field.type == 'many2one':
|
||||
data[field_name] = _get_first_not_null_id(field_name, self) # take the first not null
|
||||
elif field.type == 'text':
|
||||
data[field_name] = _concat_all(field_name, self) # contact field of all opportunities
|
||||
data[field_name] = '\n\n'.join(it for it in self.mapped(field_name) if it)
|
||||
else:
|
||||
data[field_name] = _get_first_not_null(field_name, self)
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ from .validate_email import validate_email
|
||||
from odoo import api, fields, models
|
||||
from odoo import SUPERUSER_ID, _
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
from odoo.tools import mute_logger, pycompat
|
||||
|
||||
_logger = logging.getLogger('base.partner.merge')
|
||||
|
||||
@@ -269,7 +268,7 @@ class MergePartnerAutomatic(models.TransientModel):
|
||||
return item
|
||||
# get all fields that are not computed or x2many
|
||||
values = dict()
|
||||
for column, field in model_fields.iteritems():
|
||||
for column, field in pycompat.items(model_fields):
|
||||
if field.type not in ('many2many', 'one2many') and field.compute is None:
|
||||
for item in itertools.chain(src_partners, [dst_partner]):
|
||||
if item[column]:
|
||||
@@ -407,7 +406,7 @@ class MergePartnerAutomatic(models.TransientModel):
|
||||
"""
|
||||
return any(
|
||||
self.env[model].search_count([(field, 'in', aggr_ids)])
|
||||
for model, field in models.iteritems()
|
||||
for model, field in pycompat.items(models)
|
||||
)
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -5,6 +5,7 @@ import pytz
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.addons.mail.models.mail_template import format_tz
|
||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.translate import html_translate
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
@@ -393,7 +394,7 @@ class EventRegistration(models.Model):
|
||||
'partner_id': partner_id.id,
|
||||
'event_id': event_id and event_id.id or False,
|
||||
}
|
||||
data.update({key: registration[key] for key in registration.keys() if key in self._fields})
|
||||
data.update({key: value for key, value in pycompat.items(registration) if key in self._fields})
|
||||
return data
|
||||
|
||||
@api.one
|
||||
|
||||
@@ -98,10 +98,10 @@ class EventMailScheduler(models.Model):
|
||||
def execute(self):
|
||||
if self.interval_type == 'after_sub':
|
||||
# update registration lines
|
||||
lines = []
|
||||
reg_ids = [mail_reg.registration_id for mail_reg in self.mail_registration_ids]
|
||||
for registration in filter(lambda item: item not in reg_ids, self.event_id.registration_ids):
|
||||
lines.append((0, 0, {'registration_id': registration.id}))
|
||||
lines = [
|
||||
(0, 0, {'registration_id': registration.id})
|
||||
for registration in (self.event_id.registration_ids - self.mapped('mail_registration_ids.registration_id'))
|
||||
]
|
||||
if lines:
|
||||
self.write({'mail_registration_ids': lines})
|
||||
# execute scheduler on registrations
|
||||
|
||||
@@ -6,6 +6,8 @@ from odoo.exceptions import UserError
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class FleetVehicleCost(models.Model):
|
||||
_name = 'fleet.vehicle.cost'
|
||||
@@ -262,7 +264,7 @@ class FleetVehicleLogContract(models.Model):
|
||||
res[contract.vehicle_id.id] = 1
|
||||
|
||||
Vehicle = self.env['fleet.vehicle']
|
||||
for vehicle, value in res.items():
|
||||
for vehicle, value in pycompat.items(res):
|
||||
Vehicle.browse(vehicle).message_post(body=_('%s contract(s) need(s) to be renewed and/or closed!') % value)
|
||||
return contracts.write({'state': 'toclose'})
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ class Goal(models.Model):
|
||||
for goal in self:
|
||||
goals_by_definition.setdefault(goal.definition_id, []).append(goal)
|
||||
|
||||
for definition, goals in goals_by_definition.items():
|
||||
for definition, goals in pycompat.items(goals_by_definition):
|
||||
goals_to_write = {}
|
||||
if definition.computation_mode == 'manually':
|
||||
for goal in goals:
|
||||
@@ -305,9 +305,9 @@ class Goal(models.Model):
|
||||
subqueries.setdefault((start_date, end_date), {}).update({goal.id:safe_eval(definition.batch_user_expression, {'user': goal.user_id})})
|
||||
|
||||
# the global query should be split by time periods (especially for recurrent goals)
|
||||
for (start_date, end_date), query_goals in subqueries.items():
|
||||
for (start_date, end_date), query_goals in pycompat.items(subqueries):
|
||||
subquery_domain = list(general_domain)
|
||||
subquery_domain.append((field_name, 'in', list(set(query_goals.values()))))
|
||||
subquery_domain.append((field_name, 'in', list(set(pycompat.values(query_goals)))))
|
||||
if start_date:
|
||||
subquery_domain.append((field_date_name, '>=', start_date))
|
||||
if end_date:
|
||||
@@ -351,7 +351,7 @@ class Goal(models.Model):
|
||||
|
||||
goals_to_write.update(goal._get_write_values(new_value))
|
||||
|
||||
for goal, values in goals_to_write.iteritems():
|
||||
for goal, values in pycompat.items(goals_to_write):
|
||||
if not values:
|
||||
continue
|
||||
goal.write(values)
|
||||
|
||||
@@ -20,7 +20,7 @@ class Meeting(models.Model):
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
sync_fields = set(self.get_fields_need_update_google())
|
||||
if (set(values.keys()) and sync_fields) and 'oe_update_date' not in values.keys() and 'NewMeeting' not in self._context:
|
||||
if (set(values) and sync_fields) and 'oe_update_date' not in values and 'NewMeeting' not in self._context:
|
||||
values['oe_update_date'] = fields.Datetime.now()
|
||||
return super(Meeting, self).write(values)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import pytz
|
||||
import urllib2
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.tools import exception_to_unicode
|
||||
from odoo.tools import exception_to_unicode, pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -23,15 +23,15 @@ class Meta(type):
|
||||
""" This Meta class allow to define class as a structure, and so instancied variable
|
||||
in __init__ to avoid to have side effect alike 'static' variable """
|
||||
def __new__(typ, name, parents, attrs):
|
||||
methods = dict((k, v) for k, v in attrs.iteritems()
|
||||
if callable(v))
|
||||
attrs = dict((k, v) for k, v in attrs.iteritems()
|
||||
if not callable(v))
|
||||
methods = {k: v for k, v in pycompat.items(attrs)
|
||||
if callable(v)}
|
||||
attrs = {k: v for k, v in pycompat.items(attrs)
|
||||
if not callable(v)}
|
||||
|
||||
def init(self, **kw):
|
||||
for key, val in attrs.iteritems():
|
||||
for key, val in pycompat.items(attrs):
|
||||
setattr(self, key, val)
|
||||
for key, val in kw.iteritems():
|
||||
for key, val in pycompat.items(kw):
|
||||
assert key in attrs
|
||||
setattr(self, key, val)
|
||||
|
||||
@@ -40,9 +40,7 @@ class Meta(type):
|
||||
return type.__new__(typ, name, parents, methods)
|
||||
|
||||
|
||||
class Struct(object):
|
||||
__metaclass__ = Meta
|
||||
|
||||
Struct = Meta('Struct', (object,), {})
|
||||
|
||||
class OdooEvent(Struct):
|
||||
event = False
|
||||
@@ -161,7 +159,7 @@ class SyncOperation(object):
|
||||
def __init__(self, src, info, **kw):
|
||||
self.src = src
|
||||
self.info = info
|
||||
for key, val in kw.items():
|
||||
for key, val in pycompat.items(kw):
|
||||
setattr(self, key, val)
|
||||
|
||||
def __str__(self):
|
||||
@@ -689,7 +687,7 @@ class GoogleCalendar(models.AbstractModel):
|
||||
|
||||
my_google_attendees = CalendarAttendee.with_context(context_novirtual).search([
|
||||
('partner_id', '=', my_partner_id),
|
||||
('google_internal_event_id', 'in', all_event_from_google.keys())
|
||||
('google_internal_event_id', 'in', pycompat.keys(all_event_from_google))
|
||||
])
|
||||
my_google_att_ids = my_google_attendees.ids
|
||||
|
||||
@@ -753,7 +751,7 @@ class GoogleCalendar(models.AbstractModel):
|
||||
ev_to_sync.OE.status = event.active
|
||||
ev_to_sync.OE.synchro = att.oe_synchro_date
|
||||
|
||||
for event in all_event_from_google.values():
|
||||
for event in pycompat.values(all_event_from_google):
|
||||
event_id = event.get('id')
|
||||
base_event_id = event_id.rsplit('_', 1)[0]
|
||||
|
||||
@@ -788,7 +786,7 @@ class GoogleCalendar(models.AbstractModel):
|
||||
# DO ACTION #
|
||||
######################
|
||||
for base_event in event_to_synchronize:
|
||||
event_to_synchronize[base_event] = sorted(event_to_synchronize[base_event].iteritems(), key=operator.itemgetter(0))
|
||||
event_to_synchronize[base_event] = sorted(pycompat.items(event_to_synchronize[base_event]), key=operator.itemgetter(0))
|
||||
for current_event in event_to_synchronize[base_event]:
|
||||
self.env.cr.commit()
|
||||
event = current_event[1] # event is an Sync Event !
|
||||
|
||||
@@ -7,7 +7,7 @@ from odoo import api, fields, models
|
||||
from odoo import tools, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.modules.module import get_module_resource
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -250,7 +250,7 @@ class Employee(models.Model):
|
||||
if auto_follow_fields is None:
|
||||
auto_follow_fields = ['user_id']
|
||||
user_field_lst = []
|
||||
for name, field in self._fields.items():
|
||||
for name, field in pycompat.items(self._fields):
|
||||
if name in auto_follow_fields and name in updated_fields and field.comodel_name == 'res.users':
|
||||
user_field_lst.append(name)
|
||||
return user_field_lst
|
||||
|
||||
@@ -5,7 +5,7 @@ import re
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import email_split, float_is_zero
|
||||
from odoo.tools import email_split, float_is_zero, pycompat
|
||||
|
||||
import odoo.addons.decimal_precision as dp
|
||||
|
||||
@@ -237,12 +237,12 @@ class HrExpense(models.Model):
|
||||
})
|
||||
|
||||
#convert eml into an osv-valid format
|
||||
lines = map(lambda x: (0, 0, expense._prepare_move_line(x)), move_lines)
|
||||
lines = [(0, 0, expense._prepare_move_line(x)) for x in move_lines]
|
||||
move.with_context(dont_create_taxes=True).write({'line_ids': lines})
|
||||
expense.sheet_id.write({'account_move_id': move.id})
|
||||
if expense.payment_mode == 'company_account':
|
||||
expense.sheet_id.paid_expense_sheets()
|
||||
for move in move_group_by_sheet.values():
|
||||
for move in pycompat.values(move_group_by_sheet):
|
||||
move.post()
|
||||
return True
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ class HolidaysType(models.Model):
|
||||
if not count and not order and self._context.get('employee_id'):
|
||||
leaves = self.browse(leave_ids)
|
||||
sort_key = lambda l: (not l.limit, l.virtual_remaining_leaves)
|
||||
return map(int, leaves.sorted(key=sort_key, reverse=True))
|
||||
return leaves.sorted(key=sort_key, reverse=True).ids
|
||||
return leave_ids
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import babel
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.addons import decimal_precision as dp
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class HrPayslip(models.Model):
|
||||
@@ -203,7 +204,8 @@ class HrPayslip(models.Model):
|
||||
'contract_id': contract.id,
|
||||
}
|
||||
|
||||
res += [attendances] + leaves.values()
|
||||
res.append(attendances)
|
||||
res.extend(pycompat.values(leaves))
|
||||
return res
|
||||
|
||||
@api.model
|
||||
@@ -367,7 +369,7 @@ class HrPayslip(models.Model):
|
||||
#blacklist this rule and its children
|
||||
blacklist += [id for id, seq in rule._recursive_search_of_rules()]
|
||||
|
||||
return [value for code, value in result_dict.items()]
|
||||
return [value for code, value in pycompat.items(result_dict)]
|
||||
|
||||
# YTI TODO To rename. This method is not really an onchange, as it is not in any view
|
||||
# employee_id and contract_id could be browse records
|
||||
@@ -378,9 +380,9 @@ class HrPayslip(models.Model):
|
||||
'value': {
|
||||
'line_ids': [],
|
||||
#delete old input lines
|
||||
'input_line_ids': map(lambda x: (2, x,), self.input_line_ids.ids),
|
||||
'input_line_ids': [(2, x,) for x in self.input_line_ids.ids],
|
||||
#delete old worked days lines
|
||||
'worked_days_line_ids': map(lambda x: (2, x,), self.worked_days_line_ids.ids),
|
||||
'worked_days_line_ids': [(2, x,) for x in self.worked_days_line_ids.ids],
|
||||
#'details_by_salary_head':[], TODO put me back
|
||||
'name': '',
|
||||
'contract_id': False,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class PayslipDetailsReport(models.AbstractModel):
|
||||
@@ -37,9 +38,9 @@ class PayslipDetailsReport(models.AbstractModel):
|
||||
result.setdefault(x[2], {})
|
||||
result[x[2]].setdefault(x[1], [])
|
||||
result[x[2]][x[1]].append(x[0])
|
||||
for payslip_id, lines_dict in result.iteritems():
|
||||
for payslip_id, lines_dict in pycompat.items(result):
|
||||
res.setdefault(payslip_id, [])
|
||||
for rule_categ_id, line_ids in lines_dict.iteritems():
|
||||
for rule_categ_id, line_ids in pycompat.items(lines_dict):
|
||||
rule_categories = RuleCateg.browse(rule_categ_id)
|
||||
lines = PayslipLine.browse(line_ids)
|
||||
level = 0
|
||||
@@ -69,9 +70,9 @@ class PayslipDetailsReport(models.AbstractModel):
|
||||
result.setdefault(line.slip_id.id, {})
|
||||
result[line.slip_id.id].setdefault(line.register_id, line)
|
||||
result[line.slip_id.id][line.register_id] |= line
|
||||
for payslip_id, lines_dict in result.iteritems():
|
||||
for payslip_id, lines_dict in pycompat.items(result):
|
||||
res.setdefault(payslip_id, [])
|
||||
for register, lines in lines_dict.iteritems():
|
||||
for register, lines in pycompat.items(lines_dict):
|
||||
res[payslip_id].append({
|
||||
'register_name': register.name,
|
||||
'total': sum(lines.mapped('total')),
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from datetime import datetime
|
||||
|
||||
from odoo import api, fields, models, tools, SUPERUSER_ID
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.translate import _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
@@ -248,7 +249,7 @@ class Applicant(models.Model):
|
||||
self = self.with_context(default_department_id=vals.get('department_id'))
|
||||
if vals.get('job_id') or self._context.get('default_job_id'):
|
||||
job_id = vals.get('job_id') or self._context.get('default_job_id')
|
||||
for key, value in self._onchange_job_id_internal(job_id)['value'].iteritems():
|
||||
for key, value in pycompat.items(self._onchange_job_id_internal(job_id)['value']):
|
||||
if key not in vals:
|
||||
vals[key] = value
|
||||
if vals.get('user_id'):
|
||||
|
||||
@@ -80,7 +80,7 @@ class Blackbox(Thread):
|
||||
|
||||
def _wrap_low_level_message_around(self, high_level_message):
|
||||
bcc = self._lrc(high_level_message)
|
||||
high_level_message_bytes = map(ord, high_level_message)
|
||||
high_level_message_bytes = (ord(b) for b in high_level_message)
|
||||
|
||||
low_level_message = bytearray()
|
||||
low_level_message.append(0x02)
|
||||
|
||||
@@ -168,7 +168,7 @@ class StyleStack:
|
||||
def to_escpos(self):
|
||||
""" converts the current style to an escpos command string """
|
||||
cmd = ''
|
||||
ordered_cmds = sorted(self.cmds.keys(), key=lambda x: self.cmds[x]['_order'])
|
||||
ordered_cmds = sorted(self.cmds, key=lambda x: self.cmds[x]['_order'])
|
||||
for style in ordered_cmds:
|
||||
cmd += self.cmds[style][self.get(style)]
|
||||
return cmd
|
||||
@@ -788,7 +788,7 @@ class Escpos:
|
||||
if encoding in remaining:
|
||||
del remaining[encoding]
|
||||
if len(remaining) >= 1:
|
||||
encoding = remaining.items()[0][0]
|
||||
(encoding, _) = remaining.popitem()
|
||||
else:
|
||||
encoding = 'cp437'
|
||||
encoded = '\xb1' # could not encode, output error character
|
||||
|
||||
@@ -6,6 +6,7 @@ import re
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from odoo import api, fields, models, modules, tools
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class ImLivechatChannel(models.Model):
|
||||
@@ -95,7 +96,7 @@ class ImLivechatChannel(models.Model):
|
||||
for record in self:
|
||||
dt = fields.Datetime.to_string(datetime.utcnow() - timedelta(days=7))
|
||||
repartition = record.channel_ids.rating_get_grades([('create_date', '>=', dt)])
|
||||
total = sum(repartition.values())
|
||||
total = sum(pycompat.values(repartition))
|
||||
if total > 0:
|
||||
happy = repartition['great']
|
||||
record.rating_percentage_satisfaction = ((happy*100) / total) if happy > 0 else 0
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class ChannelPartner(models.Model):
|
||||
@@ -77,7 +78,7 @@ class MailChannel(models.Model):
|
||||
last_msg = self.env['mail.message'].search([("channel_ids", "in", [channel.id])], limit=1)
|
||||
if last_msg:
|
||||
channel_infos_dict[channel.id]['last_message_date'] = last_msg.date
|
||||
return channel_infos_dict.values()
|
||||
return list(pycompat.values(channel_infos_dict))
|
||||
|
||||
@api.model
|
||||
def channel_fetch_slot(self):
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from datetime import date
|
||||
|
||||
from odoo import api, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class EmployeesYearlySalaryReport(models.AbstractModel):
|
||||
@@ -97,7 +98,7 @@ class EmployeesYearlySalaryReport(models.AbstractModel):
|
||||
|
||||
def salary_list(self, salaries):
|
||||
cat_salary_all = []
|
||||
for category_name, amount in salaries.items():
|
||||
for category_name, amount in pycompat.items(salaries):
|
||||
cat_salary = []
|
||||
total = 0.0
|
||||
cat_salary.append(category_name)
|
||||
|
||||
@@ -2,7 +2,7 @@ from collections import OrderedDict
|
||||
import csv
|
||||
|
||||
import xlrd
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
def _e(s):
|
||||
if type(s) is unicode:
|
||||
@@ -32,33 +32,33 @@ class LuxTaxGenerator:
|
||||
self.suffix = self.sheet_info.cell_value(4, 2)
|
||||
|
||||
def iter_tax_codes(self):
|
||||
keys = map(lambda c: c.value, self.sheet_tax_codes.row(0))
|
||||
keys = [c.value for c in self.sheet_tax_codes.row(0)]
|
||||
yield keys
|
||||
for i in range(1, self.sheet_tax_codes.nrows):
|
||||
row = map(lambda c: c.value, self.sheet_tax_codes.row(i))
|
||||
d = OrderedDict(zip(keys, row))
|
||||
row = (c.value for c in self.sheet_tax_codes.row(i))
|
||||
d = OrderedDict(pycompat.izip(keys, row))
|
||||
d['sign'] = int(d['sign'])
|
||||
d['sequence'] = int(d['sequence'])
|
||||
yield d
|
||||
|
||||
def iter_taxes(self):
|
||||
keys = map(lambda c: c.value, self.sheet_taxes.row(0))
|
||||
keys = [c.value for c in self.sheet_taxes.row(0)]
|
||||
yield keys
|
||||
for i in range(1, self.sheet_taxes.nrows):
|
||||
row = map(lambda c: c.value, self.sheet_taxes.row(i))
|
||||
yield OrderedDict(zip(keys, row))
|
||||
row = (c.value for c in self.sheet_taxes.row(i))
|
||||
yield OrderedDict(pycompat.izip(keys, row))
|
||||
|
||||
def iter_fiscal_pos_map(self):
|
||||
keys = map(lambda c: c.value, self.sheet_fiscal_pos_map.row(0))
|
||||
keys = [c.value for c in self.sheet_fiscal_pos_map.row(0)]
|
||||
yield keys
|
||||
for i in range(1, self.sheet_fiscal_pos_map.nrows):
|
||||
row = map(lambda c: c.value, self.sheet_fiscal_pos_map.row(i))
|
||||
yield OrderedDict(zip(keys, row))
|
||||
row = (c.value for c in self.sheet_fiscal_pos_map.row(i))
|
||||
yield OrderedDict(pycompat.izip(keys, row))
|
||||
|
||||
def tax_codes_to_csv(self):
|
||||
writer = csv.writer(open('account.tax.code.template-%s.csv' %
|
||||
self.suffix, 'wb'))
|
||||
tax_codes_iterator = self.iter_tax_codes()
|
||||
tax_codes_iterator = self.iter_tax_codes
|
||||
keys = next(tax_codes_iterator)
|
||||
writer.writerow(keys)
|
||||
|
||||
@@ -69,7 +69,7 @@ class LuxTaxGenerator:
|
||||
if tax_code in tax_codes:
|
||||
raise RuntimeError('duplicate tax code %s' % tax_code)
|
||||
tax_codes[tax_code] = row['id']
|
||||
writer.writerow(map(_e, row.values()))
|
||||
writer.writerow(pycompat.imap(_e, pycompat.values(row)))
|
||||
|
||||
# read taxes and add leaf tax codes
|
||||
new_tax_codes = {} # id: parent_code
|
||||
@@ -168,7 +168,7 @@ class LuxTaxGenerator:
|
||||
cur_seq = seq + 1000
|
||||
else:
|
||||
cur_seq = seq
|
||||
writer.writerow(map(_e, row.values()[3:]) + [cur_seq])
|
||||
writer.writerow(list(pycompat.imap(_e, list(pycompat.values(row))[3:])) + [cur_seq])
|
||||
|
||||
def fiscal_pos_map_to_csv(self):
|
||||
writer = csv.writer(open('account.fiscal.'
|
||||
@@ -178,7 +178,7 @@ class LuxTaxGenerator:
|
||||
keys = next(fiscal_pos_map_iterator)
|
||||
writer.writerow(keys)
|
||||
for row in fiscal_pos_map_iterator:
|
||||
writer.writerow(map(_e, row.values()))
|
||||
writer.writerow(pycompat.imap(_e, pycompat.values(row)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -13,7 +13,7 @@ from urlparse import urlparse
|
||||
from werkzeug import url_encode, unescape
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools import ustr
|
||||
from odoo.tools import ustr, pycompat
|
||||
|
||||
URL_REGEX = r'(\bhref=[\'"](?!mailto:)([^\'"]+)[\'"])'
|
||||
|
||||
@@ -165,7 +165,7 @@ class link_tracker(models.Model):
|
||||
create_vals['url'] = VALIDATE_URL(vals['url'])
|
||||
|
||||
search_domain = []
|
||||
for fname, value in create_vals.iteritems():
|
||||
for fname, value in pycompat.items(create_vals):
|
||||
search_domain.append((fname, '=', value))
|
||||
|
||||
result = self.search(search_domain, limit=1)
|
||||
|
||||
@@ -10,6 +10,8 @@ from odoo import api, fields, models, _
|
||||
from odoo.exceptions import AccessError, ValidationError
|
||||
import odoo.addons.decimal_precision as dp
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class LunchOrder(models.Model):
|
||||
"""
|
||||
@@ -24,10 +26,10 @@ class LunchOrder(models.Model):
|
||||
prev_order = self.env['lunch.order.line'].search([('user_id', '=', self.env.uid), ('product_id.active', '!=', False)], limit=20, order='id desc')
|
||||
# If we return return prev_order.ids, we will have duplicates (identical orders).
|
||||
# Therefore, this following part removes duplicates based on product_id and note.
|
||||
return {
|
||||
return list(pycompat.values({
|
||||
(order.product_id, order.note): order.id
|
||||
for order in prev_order
|
||||
}.values()
|
||||
}))
|
||||
|
||||
user_id = fields.Many2one('res.users', 'User', readonly=True,
|
||||
states={'new': [('readonly', False)]},
|
||||
@@ -86,10 +88,10 @@ class LunchOrder(models.Model):
|
||||
prev_order = self.env['lunch.order.line'].search([('user_id', '=', self.env.uid), ('product_id.active', '!=', False)], limit=20, order='date desc, id desc')
|
||||
# If we use prev_order.ids, we will have duplicates (identical orders).
|
||||
# Therefore, this following part removes duplicates based on product_id and note.
|
||||
self.previous_order_ids = {
|
||||
self.previous_order_ids = list(pycompat.values({
|
||||
(order.product_id, order.note): order.id
|
||||
for order in prev_order
|
||||
}.values()
|
||||
}))
|
||||
|
||||
if self.previous_order_ids:
|
||||
lunch_data = []
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
"""html2text: Turn HTML into equivalent Markdown-structured text."""
|
||||
from odoo.tools import pycompat
|
||||
|
||||
__version__ = "2.36"
|
||||
__author__ = "Aaron Swartz (me@aaronsw.com)"
|
||||
__copyright__ = "(C) 2004-2008 Aaron Swartz. GNU GPL 3."
|
||||
@@ -8,7 +10,6 @@ __contributors__ = ["Martin 'Joey' Schulze", "Ricardo Reyes", "Kevin Jay North"]
|
||||
# TODO:
|
||||
# Support decoded entities with unifiable.
|
||||
|
||||
if not hasattr(__builtins__, 'True'): True, False = 1, 0
|
||||
import re, sys, urllib, htmlentitydefs, codecs
|
||||
import sgmllib
|
||||
import urlparse
|
||||
@@ -52,7 +53,7 @@ unifiable = {'rsquo':"'", 'lsquo':"'", 'rdquo':'"', 'ldquo':'"',
|
||||
|
||||
unifiable_n = {}
|
||||
|
||||
for k in unifiable.keys():
|
||||
for k in unifiable:
|
||||
unifiable_n[name2cp(k)] = unifiable[k]
|
||||
|
||||
def charref(name):
|
||||
@@ -61,13 +62,13 @@ def charref(name):
|
||||
else:
|
||||
c = int(name)
|
||||
|
||||
if not UNICODE_SNOB and c in unifiable_n.keys():
|
||||
if not UNICODE_SNOB and c in unifiable_n:
|
||||
return unifiable_n[c]
|
||||
else:
|
||||
return unichr(c)
|
||||
|
||||
def entityref(c):
|
||||
if not UNICODE_SNOB and c in unifiable.keys():
|
||||
if not UNICODE_SNOB and c in unifiable:
|
||||
return unifiable[c]
|
||||
else:
|
||||
try: name2cp(c)
|
||||
@@ -400,7 +401,7 @@ class _html2text(sgmllib.SGMLParser):
|
||||
self.a = newa
|
||||
|
||||
if self.abbr_list and force == "end":
|
||||
for abbr, definition in self.abbr_list.items():
|
||||
for abbr, definition in pycompat.items(self.abbr_list):
|
||||
self.out(" *[" + abbr + "]: " + definition + "\n")
|
||||
|
||||
self.p_p = 0
|
||||
|
||||
@@ -9,7 +9,7 @@ import uuid
|
||||
from odoo import _, api, fields, models, modules, tools
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import ormcache
|
||||
from odoo.tools import ormcache, pycompat
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
@@ -614,12 +614,12 @@ class Channel(models.Model):
|
||||
GROUP BY mail_channel_id
|
||||
""", (tuple(self.ids),))
|
||||
channels_preview = dict((r['message_id'], r) for r in self._cr.dictfetchall())
|
||||
last_messages = self.env['mail.message'].browse(channels_preview.keys()).message_format()
|
||||
last_messages = self.env['mail.message'].browse(channels_preview).message_format()
|
||||
for message in last_messages:
|
||||
channel = channels_preview[message['id']]
|
||||
del(channel['message_id'])
|
||||
channel['last_message'] = message
|
||||
return channels_preview.values()
|
||||
return list(pycompat.values(channels_preview))
|
||||
|
||||
#------------------------------------------------------
|
||||
# Commands
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class Followers(models.Model):
|
||||
@@ -41,7 +42,7 @@ class Followers(models.Model):
|
||||
using the subtypes given in the parameters
|
||||
"""
|
||||
res_model_id = self.env['ir.model']._get(res_model).id
|
||||
force_mode = force or (all(data for data in partner_data.values()) and all(data for data in channel_data.values()))
|
||||
force_mode = force or (all(data for data in pycompat.values(partner_data)) and all(data for data in pycompat.values(channel_data)))
|
||||
generic = []
|
||||
specific = {}
|
||||
existing = {} # {res_id: follower_ids}
|
||||
@@ -51,7 +52,7 @@ class Followers(models.Model):
|
||||
followers = self.sudo().search([
|
||||
'&',
|
||||
'&', ('res_model_id', '=', res_model_id), ('res_id', 'in', res_ids),
|
||||
'|', ('partner_id', 'in', partner_data.keys()), ('channel_id', 'in', channel_data.keys())])
|
||||
'|', ('partner_id', 'in', list(partner_data)), ('channel_id', 'in', list(channel_data))])
|
||||
|
||||
if force_mode:
|
||||
followers.unlink()
|
||||
@@ -69,20 +70,20 @@ class Followers(models.Model):
|
||||
external_default_subtypes = default_subtypes.filtered(lambda subtype: not subtype.internal)
|
||||
|
||||
if force_mode:
|
||||
employee_pids = self.env['res.users'].sudo().search([('partner_id', 'in', partner_data.keys()), ('share', '=', False)]).mapped('partner_id').ids
|
||||
for pid, data in partner_data.iteritems():
|
||||
employee_pids = self.env['res.users'].sudo().search([('partner_id', 'in', list(partner_data)), ('share', '=', False)]).mapped('partner_id').ids
|
||||
for pid, data in pycompat.items(partner_data):
|
||||
if not data:
|
||||
if pid not in employee_pids:
|
||||
partner_data[pid] = external_default_subtypes.ids
|
||||
else:
|
||||
partner_data[pid] = default_subtypes.ids
|
||||
for cid, data in channel_data.iteritems():
|
||||
for cid, data in pycompat.items(channel_data):
|
||||
if not data:
|
||||
channel_data[cid] = default_subtypes.ids
|
||||
|
||||
# create new followers, batch ok
|
||||
gen_new_pids = [pid for pid in partner_data.keys() if pid not in p_exist]
|
||||
gen_new_cids = [cid for cid in channel_data.keys() if cid not in c_exist]
|
||||
gen_new_pids = [pid for pid in partner_data if pid not in p_exist]
|
||||
gen_new_cids = [cid for cid in channel_data if cid not in c_exist]
|
||||
for pid in gen_new_pids:
|
||||
generic.append([0, 0, {'res_model_id': res_model_id, 'partner_id': pid, 'subtype_ids': [(6, 0, partner_data.get(pid) or default_subtypes.ids)]}])
|
||||
for cid in gen_new_cids:
|
||||
@@ -94,8 +95,8 @@ class Followers(models.Model):
|
||||
command = []
|
||||
doc_followers = existing.get(res_id, list())
|
||||
|
||||
new_pids = set(partner_data.keys()) - set([sub.partner_id.id for sub in doc_followers if sub.partner_id]) - set(gen_new_pids)
|
||||
new_cids = set(channel_data.keys()) - set([sub.channel_id.id for sub in doc_followers if sub.channel_id]) - set(gen_new_cids)
|
||||
new_pids = set(partner_data) - set([sub.partner_id.id for sub in doc_followers if sub.partner_id]) - set(gen_new_pids)
|
||||
new_cids = set(channel_data) - set([sub.channel_id.id for sub in doc_followers if sub.channel_id]) - set(gen_new_cids)
|
||||
|
||||
# subscribe new followers
|
||||
for new_pid in new_pids:
|
||||
|
||||
@@ -13,6 +13,7 @@ from email.utils import formataddr
|
||||
from odoo import _, api, fields, models
|
||||
from odoo import tools
|
||||
from odoo.addons.base.ir.ir_mail_server import MailDeliveryException
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -206,7 +207,7 @@ class MailMail(models.Model):
|
||||
groups[mail.mail_server_id.id].append(mail.id)
|
||||
sys_params = self.env['ir.config_parameter'].sudo()
|
||||
batch_size = int(sys_params.get_param('mail.session.batch.size', 1000))
|
||||
for server_id, record_ids in groups.iteritems():
|
||||
for server_id, record_ids in pycompat.items(groups):
|
||||
for mail_batch in tools.split_every(batch_size, record_ids):
|
||||
yield server_id, mail_batch
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from email.utils import formataddr
|
||||
from odoo import _, api, fields, models, SUPERUSER_ID, tools
|
||||
from odoo.exceptions import UserError, AccessError
|
||||
from odoo.osv import expression
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -280,7 +280,7 @@ class Message(models.Model):
|
||||
partners = self.env['res.partner'].sudo()
|
||||
attachments = self.env['ir.attachment']
|
||||
trackings = self.env['mail.tracking.value']
|
||||
for key, message in message_tree.iteritems():
|
||||
for key, message in pycompat.items(message_tree):
|
||||
if message.author_id:
|
||||
partners |= message.author_id
|
||||
if message.subtype_id and message.partner_ids: # take notified people of message with a subtype
|
||||
@@ -434,7 +434,7 @@ class Message(models.Model):
|
||||
|
||||
@api.model
|
||||
def _find_allowed_model_wise(self, doc_model, doc_dict):
|
||||
doc_ids = doc_dict.keys()
|
||||
doc_ids = list(doc_dict)
|
||||
allowed_doc_ids = self.env[doc_model].with_context(active_test=False).search([('id', 'in', doc_ids)]).ids
|
||||
return set([message_id for allowed_doc_id in allowed_doc_ids for message_id in doc_dict[allowed_doc_id]])
|
||||
|
||||
@@ -442,7 +442,7 @@ class Message(models.Model):
|
||||
def _find_allowed_doc_ids(self, model_ids):
|
||||
IrModelAccess = self.env['ir.model.access']
|
||||
allowed_ids = set()
|
||||
for doc_model, doc_dict in model_ids.iteritems():
|
||||
for doc_model, doc_dict in pycompat.items(model_ids):
|
||||
if not IrModelAccess.check(doc_model, 'read', False):
|
||||
continue
|
||||
allowed_ids |= self._find_allowed_model_wise(doc_model, doc_dict)
|
||||
@@ -603,17 +603,17 @@ class Message(models.Model):
|
||||
# Author condition (READ, WRITE, CREATE (private))
|
||||
author_ids = []
|
||||
if operation == 'read' or operation == 'write':
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
author_ids = [mid for mid, message in pycompat.items(message_values)
|
||||
if message.get('author_id') and message.get('author_id') == self.env.user.partner_id.id]
|
||||
elif operation == 'create':
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
author_ids = [mid for mid, message in pycompat.items(message_values)
|
||||
if not message.get('model') and not message.get('res_id')]
|
||||
|
||||
# Parent condition, for create (check for received notifications for the created message parent)
|
||||
notified_ids = []
|
||||
if operation == 'create':
|
||||
# TDE: probably clean me
|
||||
parent_ids = [message.get('parent_id') for mid, message in message_values.iteritems()
|
||||
parent_ids = [message.get('parent_id') for mid, message in pycompat.items(message_values)
|
||||
if message.get('parent_id')]
|
||||
self._cr.execute("""SELECT DISTINCT m.id, partner_rel.res_partner_id, channel_partner.partner_id FROM "%s" m
|
||||
LEFT JOIN "mail_message_res_partner_rel" partner_rel
|
||||
@@ -626,37 +626,37 @@ class Message(models.Model):
|
||||
ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = (%%s)
|
||||
WHERE m.id = ANY (%%s)""" % self._table, (self.env.user.partner_id.id, self.env.user.partner_id.id, parent_ids,))
|
||||
not_parent_ids = [mid[0] for mid in self._cr.fetchall() if any([mid[1], mid[2]])]
|
||||
notified_ids += [mid for mid, message in message_values.iteritems()
|
||||
notified_ids += [mid for mid, message in pycompat.items(message_values)
|
||||
if message.get('parent_id') in not_parent_ids]
|
||||
|
||||
# Recipients condition, for read and write (partner_ids) and create (message_follower_ids)
|
||||
other_ids = set(self.ids).difference(set(author_ids), set(notified_ids))
|
||||
model_record_ids = _generate_model_record_ids(message_values, other_ids)
|
||||
if operation in ['read', 'write']:
|
||||
notified_ids = [mid for mid, message in message_values.iteritems() if message.get('notified')]
|
||||
notified_ids = [mid for mid, message in pycompat.items(message_values) if message.get('notified')]
|
||||
elif operation == 'create':
|
||||
for doc_model, doc_ids in model_record_ids.items():
|
||||
for doc_model, doc_ids in pycompat.items(model_record_ids):
|
||||
followers = self.env['mail.followers'].sudo().search([
|
||||
('res_model', '=', doc_model),
|
||||
('res_id', 'in', list(doc_ids)),
|
||||
('partner_id', '=', self.env.user.partner_id.id),
|
||||
])
|
||||
fol_mids = [follower.res_id for follower in followers]
|
||||
notified_ids += [mid for mid, message in message_values.iteritems()
|
||||
notified_ids += [mid for mid, message in pycompat.items(message_values)
|
||||
if message.get('model') == doc_model and message.get('res_id') in fol_mids]
|
||||
|
||||
# CRUD: Access rights related to the document
|
||||
other_ids = other_ids.difference(set(notified_ids))
|
||||
model_record_ids = _generate_model_record_ids(message_values, other_ids)
|
||||
document_related_ids = []
|
||||
for model, doc_ids in model_record_ids.items():
|
||||
for model, doc_ids in pycompat.items(model_record_ids):
|
||||
DocumentModel = self.env[model]
|
||||
mids = DocumentModel.browse(doc_ids).exists()
|
||||
if hasattr(DocumentModel, 'check_mail_message_access'):
|
||||
DocumentModel.check_mail_message_access(mids.ids, operation) # ?? mids ?
|
||||
else:
|
||||
self.env['mail.thread'].check_mail_message_access(mids.ids, operation, model_name=model)
|
||||
document_related_ids += [mid for mid, message in message_values.iteritems()
|
||||
document_related_ids += [mid for mid, message in pycompat.items(message_values)
|
||||
if message.get('model') == model and message.get('res_id') in mids.ids]
|
||||
|
||||
# Calculate remaining ids: if not void, raise an error
|
||||
|
||||
@@ -356,7 +356,7 @@ class MailTemplate(models.Model):
|
||||
return multi_mode and results or results[res_ids[0]]
|
||||
|
||||
# prepare template variables
|
||||
records = self.env[model].browse(filter(None, res_ids)) # filter to avoid browsing [None]
|
||||
records = self.env[model].browse(it for it in res_ids if it) # filter to avoid browsing [None]
|
||||
res_to_rec = dict.fromkeys(res_ids, None)
|
||||
for record in records:
|
||||
res_to_rec[record.id] = record
|
||||
@@ -366,7 +366,7 @@ class MailTemplate(models.Model):
|
||||
'user': self.env.user,
|
||||
'ctx': self._context, # context kw would clash with mako internals
|
||||
}
|
||||
for res_id, record in res_to_rec.iteritems():
|
||||
for res_id, record in pycompat.items(res_to_rec):
|
||||
variables['object'] = record
|
||||
try:
|
||||
render_result = template.render(variables)
|
||||
@@ -378,7 +378,7 @@ class MailTemplate(models.Model):
|
||||
results[res_id] = render_result
|
||||
|
||||
if post_process:
|
||||
for res_id, result in results.iteritems():
|
||||
for res_id, result in pycompat.items(results):
|
||||
results[res_id] = self.render_post_process(result)
|
||||
|
||||
return multi_mode and results or results[res_ids[0]]
|
||||
@@ -399,7 +399,7 @@ class MailTemplate(models.Model):
|
||||
self.ensure_one()
|
||||
|
||||
langs = self.render_template(self.lang, self.model, res_ids)
|
||||
for res_id, lang in langs.iteritems():
|
||||
for res_id, lang in pycompat.items(langs):
|
||||
if lang:
|
||||
template = self.with_context(lang=lang)
|
||||
else:
|
||||
@@ -418,11 +418,11 @@ class MailTemplate(models.Model):
|
||||
|
||||
if self.use_default_to or self._context.get('tpl_force_default_to'):
|
||||
default_recipients = self.env['mail.thread'].message_get_default_recipients(res_model=self.model, res_ids=res_ids)
|
||||
for res_id, recipients in default_recipients.iteritems():
|
||||
for res_id, recipients in pycompat.items(default_recipients):
|
||||
results[res_id].pop('partner_to', None)
|
||||
results[res_id].update(recipients)
|
||||
|
||||
for res_id, values in results.iteritems():
|
||||
for res_id, values in pycompat.items(results):
|
||||
partner_ids = values.get('partner_ids', list())
|
||||
if self._context.get('tpl_partners_only'):
|
||||
mails = tools.email_split(values.pop('email_to', '')) + tools.email_split(values.pop('email_cc', ''))
|
||||
@@ -461,11 +461,11 @@ class MailTemplate(models.Model):
|
||||
|
||||
# templates: res_id -> template; template -> res_ids
|
||||
templates_to_res_ids = {}
|
||||
for res_id, template in res_ids_to_templates.iteritems():
|
||||
for res_id, template in pycompat.items(res_ids_to_templates):
|
||||
templates_to_res_ids.setdefault(template, []).append(res_id)
|
||||
|
||||
results = dict()
|
||||
for template, template_res_ids in templates_to_res_ids.iteritems():
|
||||
for template, template_res_ids in pycompat.items(templates_to_res_ids):
|
||||
Template = self.env['mail.template']
|
||||
# generate fields value for all res_ids linked to the current template
|
||||
if template.lang:
|
||||
@@ -475,7 +475,7 @@ class MailTemplate(models.Model):
|
||||
generated_field_values = Template.render_template(
|
||||
getattr(template, field), template.model, template_res_ids,
|
||||
post_process=(field == 'body_html'))
|
||||
for res_id, field_value in generated_field_values.iteritems():
|
||||
for res_id, field_value in pycompat.items(generated_field_values):
|
||||
results.setdefault(res_id, dict())[field] = field_value
|
||||
# compute recipients
|
||||
if any(field in fields for field in ['email_to', 'partner_to', 'email_cc']):
|
||||
|
||||
@@ -235,10 +235,10 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
# auto_subscribe: take values and defaults into account
|
||||
create_values = dict(values)
|
||||
for key, val in self._context.iteritems():
|
||||
for key, val in pycompat.items(self._context):
|
||||
if key.startswith('default_') and key[8:] not in create_values:
|
||||
create_values[key[8:]] = val
|
||||
thread.message_auto_subscribe(create_values.keys(), values=create_values)
|
||||
thread.message_auto_subscribe(list(create_values), values=create_values)
|
||||
|
||||
# track values
|
||||
if not self._context.get('mail_notrack'):
|
||||
@@ -246,7 +246,7 @@ class MailThread(models.AbstractModel):
|
||||
track_thread = thread.with_context(lang=self.env.user.lang)
|
||||
else:
|
||||
track_thread = thread
|
||||
tracked_fields = track_thread._get_tracked_fields(values.keys())
|
||||
tracked_fields = track_thread._get_tracked_fields(list(values))
|
||||
if tracked_fields:
|
||||
initial_values = {thread.id: dict.fromkeys(tracked_fields, False)}
|
||||
track_thread.message_track(tracked_fields, initial_values)
|
||||
@@ -266,7 +266,7 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
tracked_fields = None
|
||||
if not self._context.get('mail_notrack'):
|
||||
tracked_fields = track_self._get_tracked_fields(values.keys())
|
||||
tracked_fields = track_self._get_tracked_fields(list(values))
|
||||
if tracked_fields:
|
||||
initial_values = dict((record.id, dict((key, getattr(record, key)) for key in tracked_fields))
|
||||
for record in track_self)
|
||||
@@ -275,7 +275,7 @@ class MailThread(models.AbstractModel):
|
||||
result = super(MailThread, self).write(values)
|
||||
|
||||
# update followers
|
||||
self.message_auto_subscribe(values.keys(), values=values)
|
||||
self.message_auto_subscribe(list(values), values=values)
|
||||
|
||||
# Perform the tracking
|
||||
if tracked_fields:
|
||||
@@ -385,7 +385,7 @@ class MailThread(models.AbstractModel):
|
||||
always tracked fields and modified on_change fields
|
||||
"""
|
||||
tracked_fields = []
|
||||
for name, field in self._fields.items():
|
||||
for name, field in pycompat.items(self._fields):
|
||||
if getattr(field, 'track_visibility', False):
|
||||
tracked_fields.append(name)
|
||||
|
||||
@@ -413,10 +413,10 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
@api.multi
|
||||
def _message_track_post_template(self, tracking):
|
||||
if not any(change for rec_id, (change, tracking_value_ids) in tracking.iteritems()):
|
||||
if not any(change for rec_id, (change, tracking_value_ids) in pycompat.items(tracking)):
|
||||
return True
|
||||
templates = self._track_template(tracking)
|
||||
for field_name, (template, post_kwargs) in templates.iteritems():
|
||||
for field_name, (template, post_kwargs) in pycompat.items(templates):
|
||||
if not template:
|
||||
continue
|
||||
if isinstance(template, basestring):
|
||||
@@ -447,7 +447,7 @@ class MailThread(models.AbstractModel):
|
||||
display_values_ids = []
|
||||
|
||||
# generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}}
|
||||
for col_name, col_info in tracked_fields.items():
|
||||
for col_name, col_info in pycompat.items(tracked_fields):
|
||||
track_visibility = getattr(self._fields[col_name], 'track_visibility', 'onchange')
|
||||
initial_value = initial[col_name]
|
||||
new_value = getattr(self, col_name)
|
||||
@@ -565,7 +565,7 @@ class MailThread(models.AbstractModel):
|
||||
@api.model
|
||||
def _generate_notification_token(self, base_link, params):
|
||||
secret = self.env['ir.config_parameter'].sudo().get_param('database.secret')
|
||||
token = '%s?%s' % (base_link, ' '.join('%s=%s' % (key, params[key]) for key in sorted(params.keys())))
|
||||
token = '%s?%s' % (base_link, ' '.join('%s=%s' % (key, params[key]) for key in sorted(params)))
|
||||
hm = hmac.new(str(secret), token, hashlib.sha1).hexdigest()
|
||||
return hm
|
||||
|
||||
@@ -765,20 +765,20 @@ class MailThread(models.AbstractModel):
|
||||
aliases[alias.alias_parent_thread_id] = '%s@%s' % (alias.alias_name, alias_domain)
|
||||
doc_names.update(
|
||||
dict((ng_res[0], ng_res[1])
|
||||
for ng_res in self.env[model_name].sudo().browse(aliases.keys()).name_get()))
|
||||
for ng_res in self.env[model_name].sudo().browse(aliases).name_get()))
|
||||
# left ids: use catchall
|
||||
left_ids = set(res_ids).difference(set(aliases.keys()))
|
||||
left_ids = set(res_ids).difference(set(aliases))
|
||||
if left_ids:
|
||||
catchall_alias = self.env['ir.config_parameter'].sudo().get_param("mail.catchall.alias")
|
||||
if catchall_alias:
|
||||
aliases.update(dict((res_id, '%s@%s' % (catchall_alias, alias_domain)) for res_id in left_ids))
|
||||
# compute name of reply-to
|
||||
company_name = self.env.user.company_id.name
|
||||
for res_id in aliases.keys():
|
||||
for res_id in aliases:
|
||||
email_name = '%s%s' % (company_name, doc_names.get(res_id) and (' ' + doc_names[res_id]) or '')
|
||||
email_addr = aliases[res_id]
|
||||
res[res_id] = formataddr((email_name, email_addr))
|
||||
left_ids = set(res_ids).difference(set(aliases.keys()))
|
||||
left_ids = set(res_ids).difference(set(aliases))
|
||||
if left_ids:
|
||||
res.update(dict((res_id, default) for res_id in res_ids))
|
||||
return res
|
||||
@@ -813,7 +813,7 @@ class MailThread(models.AbstractModel):
|
||||
def message_capable_models(self):
|
||||
""" Used by the plugin addon, based for plugin_outlook and others. """
|
||||
ret_dict = {}
|
||||
for model_name, model in self.env.iteritems():
|
||||
for model_name, model in pycompat.items(self.env):
|
||||
if hasattr(model, "message_process") and hasattr(model, "message_post"):
|
||||
ret_dict[model_name] = model._description
|
||||
return ret_dict
|
||||
@@ -823,7 +823,7 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
:param string message: an email.message instance """
|
||||
s = ', '.join([tools.decode_smtp_header(message.get(h)) for h in header_fields if message.get(h)])
|
||||
return filter(lambda x: x, self._find_partner_from_emails(tools.email_split(s)))
|
||||
return [x for x in self._find_partner_from_emails(tools.email_split(s)) if x]
|
||||
|
||||
def _routing_warn(self, error_message, warn_suffix, message_id, route, raise_exception):
|
||||
""" Tools method used in message_route_verify: whether to log a warning or raise an error """
|
||||
@@ -1836,7 +1836,7 @@ class MailThread(models.AbstractModel):
|
||||
if self._context.get('mail_post_autofollow') and self.ids and partner_ids:
|
||||
partner_to_subscribe = partner_ids
|
||||
if self._context.get('mail_post_autofollow_partner_ids'):
|
||||
partner_to_subscribe = filter(lambda item: item in self._context.get('mail_post_autofollow_partner_ids'), partner_ids)
|
||||
partner_to_subscribe = [p for p in partner_ids if p in self._context.get('mail_post_autofollow_partner_ids')]
|
||||
self.message_subscribe(list(partner_to_subscribe), force=False)
|
||||
|
||||
# _mail_flat_thread: automatically set free messages to the first posted message
|
||||
@@ -2047,7 +2047,7 @@ class MailThread(models.AbstractModel):
|
||||
if auto_follow_fields is None:
|
||||
auto_follow_fields = ['user_id']
|
||||
user_field_lst = []
|
||||
for name, field in self._fields.items():
|
||||
for name, field in pycompat.items(self._fields):
|
||||
if name in auto_follow_fields and name in updated_fields and getattr(field, 'track_visibility', False) and field.comodel_name == 'res.users':
|
||||
user_field_lst.append(name)
|
||||
return user_field_lst
|
||||
@@ -2142,10 +2142,10 @@ class MailThread(models.AbstractModel):
|
||||
for partner in to_add_users.mapped('partner_id'):
|
||||
new_partners.setdefault(partner.id, None)
|
||||
|
||||
for pid, subtypes in new_partners.items():
|
||||
for pid, subtypes in pycompat.items(new_partners):
|
||||
subtypes = list(subtypes) if subtypes is not None else None
|
||||
self.message_subscribe(partner_ids=[pid], subtype_ids=subtypes, force=(subtypes != None))
|
||||
for cid, subtypes in new_channels.items():
|
||||
for cid, subtypes in pycompat.items(new_channels):
|
||||
subtypes = list(subtypes) if subtypes is not None else None
|
||||
self.message_subscribe(channel_ids=[cid], subtype_ids=subtypes, force=(subtypes != None))
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.misc import split_every
|
||||
|
||||
from odoo import _, api, fields, models, registry, SUPERUSER_ID
|
||||
@@ -174,7 +175,7 @@ class Partner(models.Model):
|
||||
|
||||
emails = self.env['mail.mail']
|
||||
recipients_nbr, recipients_max = 0, 50
|
||||
for email_type, recipient_template_values in recipients.iteritems():
|
||||
for email_type, recipient_template_values in pycompat.items(recipients):
|
||||
if recipient_template_values['followers']:
|
||||
# generate notification email content
|
||||
template_fol_values = dict(base_template_ctx, **recipient_template_values) # fixme: set button_unfollow to none
|
||||
|
||||
@@ -29,14 +29,15 @@ class TestMailFollowers(TestMail):
|
||||
self.assertFalse(specific)
|
||||
self.assertEqual(len(generic), 2)
|
||||
|
||||
self.assertEqual(set([generic[0][2]['res_model_id'], generic[1][2]['res_model_id']]),
|
||||
set([mail_channel_model_id]))
|
||||
self.assertEqual(set(filter(None, [generic[0][2].get('channel_id'), generic[1][2].get('channel_id')])),
|
||||
set([test_channel.id]))
|
||||
self.assertEqual(set(filter(None, [generic[0][2].get('partner_id'), generic[1][2].get('partner_id')])),
|
||||
set([self.user_employee.partner_id.id]))
|
||||
self.assertEqual(set(generic[0][2]['subtype_ids'][0][2] + generic[1][2]['subtype_ids'][0][2]),
|
||||
set([self.mt_mg_nodef.id, self.mt_al_nodef.id]))
|
||||
items = [it[2] for it in generic]
|
||||
self.assertEqual({item['res_model_id'] for item in items},
|
||||
{mail_channel_model_id})
|
||||
self.assertEqual({item['channel_id'] for item in items if item.get('channel_id')},
|
||||
{test_channel.id})
|
||||
self.assertEqual({item['partner_id'] for item in items if item.get('partner_id')},
|
||||
{self.user_employee.partner_id.id})
|
||||
self.assertEqual(set(items[0]['subtype_ids'][0][2] + items[1]['subtype_ids'][0][2]),
|
||||
{self.mt_mg_nodef.id, self.mt_al_nodef.id})
|
||||
|
||||
def test_m2o_command_update_selective(self):
|
||||
test_channel = self.env['mail.channel'].create({'name': 'Test'})
|
||||
@@ -55,7 +56,7 @@ class TestMailFollowers(TestMail):
|
||||
self.assertEqual(generic[0][2]['channel_id'], test_channel.id)
|
||||
self.assertEqual(set(generic[0][2]['subtype_ids'][0][2]), set(self.default_group_subtypes.ids))
|
||||
|
||||
self.assertEqual(specific.keys(), [self.test_public.id])
|
||||
self.assertEqual(list(specific), [self.test_public.id])
|
||||
self.assertEqual(specific[self.test_public.id][0][2]['res_model_id'], mail_channel_model_id)
|
||||
self.assertEqual(specific[self.test_public.id][0][2]['partner_id'], self.user_employee.partner_id.id)
|
||||
self.assertEqual(set(specific[self.test_public.id][0][2]['subtype_ids'][0][2]), set([self.mt_mg_nodef.id]))
|
||||
|
||||
@@ -93,7 +93,7 @@ class MailComposer(models.TransientModel):
|
||||
result['res_id'] = self.env.user.partner_id.id
|
||||
|
||||
if fields is not None:
|
||||
[result.pop(field, None) for field in result.keys() if field not in fields]
|
||||
[result.pop(field, None) for field in list(result) if field not in fields]
|
||||
return result
|
||||
|
||||
@api.model
|
||||
@@ -140,7 +140,7 @@ class MailComposer(models.TransientModel):
|
||||
for mid, rmod, rid in self._cr.fetchall():
|
||||
message_values[mid] = {'model': rmod, 'res_id': rid}
|
||||
# remove from the set to check the ids that mail_compose_message accepts
|
||||
author_ids = [mid for mid, message in message_values.iteritems()
|
||||
author_ids = [mid for mid, message in pycompat.items(message_values)
|
||||
if message.get('model') and not message.get('res_id')]
|
||||
self = self.browse(list(set(self.ids) - set(author_ids))) # not sure slef = ...
|
||||
|
||||
@@ -248,7 +248,7 @@ class MailComposer(models.TransientModel):
|
||||
for res_ids in sliced_res_ids:
|
||||
batch_mails = Mail
|
||||
all_mail_values = wizard.get_mail_values(res_ids)
|
||||
for res_id, mail_values in all_mail_values.iteritems():
|
||||
for res_id, mail_values in pycompat.items(all_mail_values):
|
||||
if wizard.composition_mode == 'mass_mail':
|
||||
batch_mails |= Mail.create(mail_values)
|
||||
else:
|
||||
@@ -338,7 +338,7 @@ class MailComposer(models.TransientModel):
|
||||
def onchange_template_id_wrapper(self):
|
||||
self.ensure_one()
|
||||
values = self.onchange_template_id(self.template_id.id, self.composition_mode, self.model, self.res_id)['value']
|
||||
for fname, value in values.iteritems():
|
||||
for fname, value in pycompat.items(values):
|
||||
setattr(self, fname, value)
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -9,6 +9,7 @@ import re
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
import odoo.addons.decimal_precision as dp
|
||||
@@ -466,10 +467,10 @@ class MarketingCampaignWorkitem(models.Model):
|
||||
matching_workitems = []
|
||||
for id, res_id, model in res:
|
||||
workitem_map.setdefault(model, {}).setdefault(res_id, set()).add(id)
|
||||
for model, id_map in workitem_map.iteritems():
|
||||
for model, id_map in pycompat.items(workitem_map):
|
||||
Model = self.env[model]
|
||||
condition_name[0] = Model._rec_name
|
||||
condition = [('id', 'in', id_map.keys()), condition_name]
|
||||
condition = [('id', 'in', list(id_map)), condition_name]
|
||||
for record in Model.search(condition):
|
||||
matching_workitems.extend(id_map[record.id])
|
||||
return [('id', 'in', list(set(matching_workitems)))]
|
||||
|
||||
@@ -258,9 +258,9 @@ class MassMailingCampaign(models.Model):
|
||||
# Update standard results with default results
|
||||
result = []
|
||||
for state_value, state_name in states:
|
||||
res = filter(lambda x: x['stage_id'] == (state_value, state_name), read_group_res)
|
||||
res = [x for x in read_group_res if x['stage_id'] == (state_value, state_name)]
|
||||
if not res:
|
||||
res = filter(lambda x: x['stage_id'] == state_value, read_group_all_states)
|
||||
res = [x for x in read_group_all_states if x['stage_id'] == state_value]
|
||||
res[0]['stage_id'] = [state_value, state_name]
|
||||
result.append(res[0])
|
||||
return result
|
||||
@@ -456,7 +456,7 @@ class MassMailing(models.Model):
|
||||
def _onchange_model_and_list(self):
|
||||
if self.mailing_model == 'mail.mass_mailing.list':
|
||||
if self.contact_list_ids:
|
||||
self.mailing_domain = "[('list_ids', 'in', [%s]), ('opt_out', '=', False)]" % (','.join(map(str,self.contact_list_ids.ids)),)
|
||||
self.mailing_domain = "[('list_ids', 'in', [%s]), ('opt_out', '=', False)]" % (','.join(str(id) for id in self.contact_list_ids.ids),)
|
||||
else:
|
||||
self.mailing_domain = "[(0, '=', 1)]"
|
||||
elif 'opt_out' in self.env[self.mailing_model]._fields and not self.mailing_domain:
|
||||
@@ -497,9 +497,9 @@ class MassMailing(models.Model):
|
||||
# Update standard results with default results
|
||||
result = []
|
||||
for state_value, state_name in states:
|
||||
res = filter(lambda x: x['state'] == state_value, read_group_res)
|
||||
res = [x for x in read_group_res if x['state'] == state_value]
|
||||
if not res:
|
||||
res = filter(lambda x: x['state'] == state_value, read_group_all_states)
|
||||
res = [x for x in read_group_all_states if x['state'] == state_value]
|
||||
res[0]['state'] = [state_value, state_name]
|
||||
result.append(res[0])
|
||||
return result
|
||||
|
||||
@@ -5,6 +5,7 @@ from dateutil import relativedelta
|
||||
import datetime
|
||||
|
||||
from odoo import api, exceptions, fields, models, _
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class MrpWorkcenter(models.Model):
|
||||
@@ -73,7 +74,7 @@ class MrpWorkcenter(models.Model):
|
||||
if res_group['state'] in ('pending', 'ready', 'progress'):
|
||||
result_duration_expected[res_group['workcenter_id'][0]] += res_group['duration_expected']
|
||||
for workcenter in self:
|
||||
workcenter.workorder_count = sum(count for state, count in result[workcenter.id].items() if state not in ('done', 'cancel'))
|
||||
workcenter.workorder_count = sum(count for state, count in pycompat.items(result[workcenter.id]) if state not in ('done', 'cancel'))
|
||||
workcenter.workorder_pending_count = result[workcenter.id].get('pending', 0)
|
||||
workcenter.workcenter_load = result_duration_expected[workcenter.id]
|
||||
workcenter.workorder_ready_count = result[workcenter.id].get('ready', 0)
|
||||
|
||||
@@ -25,7 +25,7 @@ class StockPickingType(models.Model):
|
||||
data = self.env['mrp.production'].read_group(domains[field] +
|
||||
[('state', 'not in', ('done', 'cancel')), ('picking_type_id', 'in', self.ids)],
|
||||
['picking_type_id'], ['picking_type_id'])
|
||||
count = dict(map(lambda x: (x['picking_type_id'] and x['picking_type_id'][0], x['picking_type_id_count']), data))
|
||||
count = {x['picking_type_id'] and x['picking_type_id'][0]: x['picking_type_id_count'] for x in data}
|
||||
for record in mrp_picking_types:
|
||||
record[field] = count.get(record.id, 0)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class MrpProductProduce(models.TransientModel):
|
||||
res['product_qty'] = quantity
|
||||
res['product_id'] = production.product_id.id
|
||||
res['product_uom_id'] = production.product_uom_id.id
|
||||
res['consume_line_ids'] = map(lambda x: (0,0,x), lines) + map(lambda x:(4, x), existing_lines)
|
||||
res['consume_line_ids'] = [(0,0,x) for x in lines] + [(4, x) for x in existing_lines]
|
||||
return res
|
||||
|
||||
serial = fields.Boolean('Requires Serial')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class MakeInvoice(models.TransientModel):
|
||||
@@ -24,7 +25,7 @@ class MakeInvoice(models.TransientModel):
|
||||
# but that second call will not do anything, since the repairs are already invoiced.
|
||||
repairs.action_repair_invoice_create()
|
||||
return {
|
||||
'domain': [('id', 'in', new_invoice.values())],
|
||||
'domain': [('id', 'in', list(pycompat.values(new_invoice)))],
|
||||
'name': 'Invoices',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'tree,form',
|
||||
|
||||
@@ -9,7 +9,7 @@ import urllib2
|
||||
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import html2plaintext
|
||||
from odoo.tools import html2plaintext, pycompat
|
||||
|
||||
from ..py_etherpad import EtherpadLiteClient
|
||||
|
||||
@@ -102,7 +102,7 @@ class PadCommon(models.AbstractModel):
|
||||
|
||||
# In case the pad is created programmatically, the content is not filled in yet since it is
|
||||
# normally initialized by the JS layer
|
||||
for k, field in self._fields.iteritems():
|
||||
for k, field in pycompat.items(self._fields):
|
||||
if hasattr(field, 'pad_content_field') and k not in vals:
|
||||
ctx = {
|
||||
'model': self._name,
|
||||
@@ -115,7 +115,7 @@ class PadCommon(models.AbstractModel):
|
||||
|
||||
# Set the pad content in vals
|
||||
def _set_pad_value(self, vals):
|
||||
for k, v in vals.items():
|
||||
for k, v in list(pycompat.items(vals)):
|
||||
field = self._fields[k]
|
||||
if hasattr(field, 'pad_content_field'):
|
||||
vals[field.pad_content_field] = self.pad_get_content(v)
|
||||
@@ -125,7 +125,7 @@ class PadCommon(models.AbstractModel):
|
||||
self.ensure_one()
|
||||
if not default:
|
||||
default = {}
|
||||
for k, field in self._fields.iteritems():
|
||||
for k, field in pycompat.items(self._fields):
|
||||
if hasattr(field, 'pad_content_field'):
|
||||
pad = self.pad_generate_url()
|
||||
default[k] = pad.get('url')
|
||||
|
||||
@@ -4,7 +4,7 @@ import hmac
|
||||
import logging
|
||||
|
||||
from odoo import api, exceptions, fields, models, _
|
||||
from odoo.tools import consteq, float_round, image_resize_images, ustr
|
||||
from odoo.tools import consteq, float_round, image_resize_images, ustr, pycompat
|
||||
from odoo.addons.base.module import module
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
@@ -155,7 +155,7 @@ class PaymentAcquirer(models.Model):
|
||||
""" If the field has 'required_if_provider="<provider>"' attribute, then it
|
||||
required if record.provider is <provider>. """
|
||||
for acquirer in self:
|
||||
if any(getattr(f, 'required_if_provider', None) == acquirer.provider and not acquirer[k] for k, f in self._fields.items()):
|
||||
if any(getattr(f, 'required_if_provider', None) == acquirer.provider and not acquirer[k] for k, f in pycompat.items(self._fields)):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -686,7 +686,7 @@ class PaymentToken(models.Model):
|
||||
if hasattr(self, custom_method_name):
|
||||
values.update(getattr(self, custom_method_name)(values))
|
||||
# remove all non-model fields used by (provider)_create method to avoid warning
|
||||
fields_wl = set(self._fields.keys()) & set(values.keys())
|
||||
fields_wl = set(self._fields) & set(values)
|
||||
values = {field: values[field] for field in fields_wl}
|
||||
return super(PaymentToken, self).create(values)
|
||||
|
||||
|
||||
@@ -8,11 +8,14 @@ import hashlib
|
||||
import hmac
|
||||
import logging
|
||||
import urlparse
|
||||
from itertools import chain
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.addons.payment.models.payment_acquirer import ValidationError
|
||||
from odoo.addons.payment_adyen.controllers.main import AdyenController
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -46,7 +49,10 @@ class AcquirerAdyen(models.Model):
|
||||
return val.replace('\\', '\\\\').replace(':', '\\:')
|
||||
|
||||
def signParams(parms):
|
||||
signing_string = ':'.join(map(escapeVal, parms.keys() + parms.values()))
|
||||
signing_string = ':'.join(
|
||||
escapeVal(v)
|
||||
for v in chain(pycompat.keys(parms), pycompat.values(parms))
|
||||
)
|
||||
hm = hmac.new(hmac_key, signing_string, hashlib.sha256)
|
||||
return base64.b64encode(hm.digest())
|
||||
|
||||
@@ -74,7 +80,7 @@ class AcquirerAdyen(models.Model):
|
||||
|
||||
hmac_key = binascii.a2b_hex(self.adyen_skin_hmac_key.encode('ascii'))
|
||||
raw_values = {k: values.get(k.encode('ascii'), '') for k in keys if k in values}
|
||||
raw_values_ordered = OrderedDict(sorted(raw_values.items(), key=lambda t: t[0]))
|
||||
raw_values_ordered = OrderedDict(sorted(pycompat.items(raw_values), key=lambda t: t[0]))
|
||||
|
||||
return signParams(raw_values_ordered)
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class AuthorizeAPI():
|
||||
response = urlopen(request).read()
|
||||
response = strip_ns(response, XMLNS)
|
||||
if response.find('messages/resultCode').text == 'Error':
|
||||
messages = map(lambda m: m.text, response.findall('messages/message/text'))
|
||||
messages = [m.text for m in response.findall('messages/message/text')]
|
||||
raise ValidationError('Authorize.net Error Message(s):\n %s' % '\n'.join(messages))
|
||||
return response
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import odoo
|
||||
from odoo.addons.payment.models.payment_acquirer import ValidationError
|
||||
from odoo.addons.payment.tests.common import PaymentAcquirerCommon
|
||||
from odoo.addons.payment_authorize.controllers.main import AuthorizeController
|
||||
from odoo.tools import mute_logger
|
||||
from odoo.tools import mute_logger, pycompat
|
||||
|
||||
|
||||
@odoo.tests.common.at_install(True)
|
||||
@@ -91,7 +91,7 @@ class AuthorizeForm(AuthorizeCommon):
|
||||
tree = objectify.fromstring(res)
|
||||
self.assertEqual(tree.get('action'), 'https://test.authorize.net/gateway/transact.dll', 'Authorize: wrong form POST url')
|
||||
for el in tree.iterfind('input'):
|
||||
values = el.values()
|
||||
values = list(pycompat.values(el.attrib))
|
||||
if values[1] in ['submit', 'x_fp_hash', 'return_url', 'x_state', 'x_ship_to_state']:
|
||||
continue
|
||||
self.assertEqual(
|
||||
|
||||
@@ -6,6 +6,7 @@ import werkzeug
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.tools import pycompat
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -26,6 +27,6 @@ class BuckarooController(http.Controller):
|
||||
""" Buckaroo."""
|
||||
_logger.info('Buckaroo: entering form_feedback with post data %s', pprint.pformat(post)) # debug
|
||||
request.env['payment.transaction'].sudo().form_feedback(post, 'buckaroo')
|
||||
post = dict((key.upper(), value) for key, value in post.items())
|
||||
post = {key.upper(): value for key, value in pycompat.items(post)}
|
||||
return_url = post.get('ADD_RETURNDATA') or '/'
|
||||
return werkzeug.utils.redirect(return_url)
|
||||
|
||||
@@ -7,6 +7,8 @@ import urlparse
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.addons.payment.models.payment_acquirer import ValidationError
|
||||
from odoo.addons.payment_buckaroo.controllers.main import BuckarooController
|
||||
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.float_utils import float_compare
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -19,7 +21,7 @@ def normalize_keys_upper(data):
|
||||
convert everything to upper case to be able to easily detected the presence
|
||||
of a parameter by checking the uppercase key only
|
||||
"""
|
||||
return dict((key.upper(), val) for key, val in data.items())
|
||||
return {key.upper(): val for key, val in pycompat.items(data)}
|
||||
|
||||
|
||||
class AcquirerBuckaroo(models.Model):
|
||||
@@ -65,13 +67,13 @@ class AcquirerBuckaroo(models.Model):
|
||||
values = dict(values or {})
|
||||
|
||||
if inout == 'out':
|
||||
for key in values.keys():
|
||||
for key in list(values):
|
||||
# case insensitive keys
|
||||
if key.upper() == 'BRQ_SIGNATURE':
|
||||
del values[key]
|
||||
break
|
||||
|
||||
items = sorted(values.items(), key=lambda pair: pair[0].lower())
|
||||
items = sorted(pycompat.items(values), key=lambda pair: pair[0].lower())
|
||||
sign = ''.join('%s=%s' % (k, urllib.unquote_plus(v)) for k, v in items)
|
||||
else:
|
||||
sign = ''.join('%s=%s' % (k, get_value(k)) for k in keys)
|
||||
|
||||
@@ -143,7 +143,7 @@ class PaymentAcquirerOgone(models.Model):
|
||||
]
|
||||
return key.upper() in keys
|
||||
|
||||
items = sorted((k.upper(), v) for k, v in values.items())
|
||||
items = sorted((k.upper(), v) for k, v in pycompat.items(values))
|
||||
sign = ''.join('%s=%s%s' % (k, v, key) for k, v in items if v and filter_key(k))
|
||||
sign = sign.encode("utf-8")
|
||||
shasign = sha1(sign).hexdigest()
|
||||
|
||||
@@ -37,7 +37,7 @@ class PaypalController(http.Controller):
|
||||
:return: tuple containing the STATUS str and the key/value pairs
|
||||
parsed as a dict
|
||||
"""
|
||||
lines = filter(None, response.split('\n'))
|
||||
lines = [line for line in response.split('\n') if line]
|
||||
status = lines.pop(0)
|
||||
|
||||
pdt_post = {}
|
||||
|
||||
@@ -7,7 +7,7 @@ from functools import partial
|
||||
import psycopg2
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.tools import float_is_zero
|
||||
from odoo.tools import float_is_zero, pycompat
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.http import request
|
||||
import odoo.addons.decimal_precision as dp
|
||||
@@ -308,7 +308,7 @@ class PosOrder(models.Model):
|
||||
|
||||
# round tax lines per order
|
||||
if rounding_method == 'round_globally':
|
||||
for group_key, group_value in grouped_data.iteritems():
|
||||
for group_key, group_value in pycompat.items(grouped_data):
|
||||
if group_key[0] == 'tax':
|
||||
for line in group_value:
|
||||
line['credit'] = cur.round(line['credit'])
|
||||
@@ -326,7 +326,7 @@ class PosOrder(models.Model):
|
||||
order.write({'state': 'done', 'account_move': move.id})
|
||||
|
||||
all_lines = []
|
||||
for group_key, group_data in grouped_data.iteritems():
|
||||
for group_key, group_data in pycompat.items(grouped_data):
|
||||
for value in group_data:
|
||||
all_lines.append((0, 0, value),)
|
||||
if move: # In case no order was changed
|
||||
@@ -697,7 +697,7 @@ class PosOrder(models.Model):
|
||||
qty_done = pack_operation.product_qty
|
||||
else:
|
||||
has_wrong_lots = True
|
||||
pack_operation.write({'pack_lot_ids': map(lambda x: (0, 0, x), pack_lots), 'qty_done': qty_done})
|
||||
pack_operation.write({'pack_lot_ids': [(0, 0, x) for x in pack_lots], 'qty_done': qty_done})
|
||||
return has_wrong_lots
|
||||
|
||||
def add_payment(self, data):
|
||||
@@ -989,7 +989,7 @@ class ReportSaleDetails(models.AbstractModel):
|
||||
'total_paid': user_currency.round(total),
|
||||
'payments': payments,
|
||||
'company_name': self.env.user.company_id.name,
|
||||
'taxes': taxes.values(),
|
||||
'taxes': list(pycompat.values(taxes)),
|
||||
'products': sorted([{
|
||||
'product_id': product.id,
|
||||
'product_name': product.name,
|
||||
@@ -998,7 +998,7 @@ class ReportSaleDetails(models.AbstractModel):
|
||||
'price_unit': price_unit,
|
||||
'discount': discount,
|
||||
'uom': product.uom_id.name
|
||||
} for (product, price_unit, discount), qty in products_sold.items()], key=lambda l: l['product_name'])
|
||||
} for (product, price_unit, discount), qty in pycompat.items(products_sold)], key=lambda l: l['product_name'])
|
||||
}
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -20,7 +20,7 @@ class PosInvoiceReport(models.AbstractModel):
|
||||
not_invoiced_orders_ids = list(set(docids) - set(invoiced_posorders_ids))
|
||||
if not_invoiced_orders_ids:
|
||||
not_invoiced_posorders = PosOrder.browse(not_invoiced_orders_ids)
|
||||
not_invoiced_orders_names = list(map(lambda a: a.name, not_invoiced_posorders))
|
||||
not_invoiced_orders_names = [a.name for a in not_invoiced_posorders]
|
||||
raise UserError(_('No link to an invoice for %s.') % ', '.join(not_invoiced_orders_names))
|
||||
|
||||
return self.env['ir.actions.report'].sudo().render_template('account.report_invoice', {'docs': self.env['account.invoice'].sudo().browse(ids_to_print)})
|
||||
|
||||
@@ -8,6 +8,8 @@ from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
import odoo.addons.decimal_precision as dp
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class Pricelist(models.Model):
|
||||
_name = "product.pricelist"
|
||||
@@ -19,7 +21,7 @@ class Pricelist(models.Model):
|
||||
|
||||
def _get_default_item_ids(self):
|
||||
ProductPricelistItem = self.env['product.pricelist.item']
|
||||
vals = ProductPricelistItem.default_get(ProductPricelistItem._fields.keys())
|
||||
vals = ProductPricelistItem.default_get(list(ProductPricelistItem._fields))
|
||||
vals.update(compute_price='formula')
|
||||
return [[0, False, vals]]
|
||||
|
||||
@@ -88,7 +90,7 @@ class Pricelist(models.Model):
|
||||
results = {}
|
||||
for pricelist in pricelists:
|
||||
subres = pricelist._compute_price_rule(products_qty_partner, date=date, uom_id=uom_id)
|
||||
for product_id, price in subres.items():
|
||||
for product_id, price in pycompat.items(subres):
|
||||
results.setdefault(product_id, {})
|
||||
results[product_id][pricelist.id] = price
|
||||
return results
|
||||
@@ -126,7 +128,7 @@ class Pricelist(models.Model):
|
||||
while categ:
|
||||
categ_ids[categ.id] = True
|
||||
categ = categ.parent_id
|
||||
categ_ids = categ_ids.keys()
|
||||
categ_ids = list(categ_ids)
|
||||
|
||||
is_product_template = products[0]._name == "product.template"
|
||||
if is_product_template:
|
||||
@@ -251,7 +253,14 @@ class Pricelist(models.Model):
|
||||
""" For a given pricelist, return price for products
|
||||
Returns: dict{product_id: product price}, in the given pricelist """
|
||||
self.ensure_one()
|
||||
return dict((product_id, res_tuple[0]) for product_id, res_tuple in self._compute_price_rule(zip(products, quantities, partners), date=date, uom_id=uom_id).iteritems())
|
||||
return {
|
||||
product_id: res_tuple[0]
|
||||
for product_id, res_tuple in pycompat.items(self._compute_price_rule(
|
||||
list(pycompat.izip(products, quantities, partners)),
|
||||
date=date,
|
||||
uom_id=uom_id
|
||||
))
|
||||
}
|
||||
|
||||
def get_product_price(self, product, quantity, partner, date=False, uom_id=False):
|
||||
""" For a given pricelist, return price for a given product """
|
||||
@@ -272,7 +281,7 @@ class Pricelist(models.Model):
|
||||
@api.multi
|
||||
def price_get(self, prod_id, qty, partner=None):
|
||||
""" Multi pricelist, mono product - returns price per pricelist """
|
||||
return dict((key, price[0]) for key, price in self.price_rule_get(prod_id, qty, partner=partner).items())
|
||||
return {key: price[0] for key, price in pycompat.items(self.price_rule_get(prod_id, qty, partner=partner))}
|
||||
|
||||
@api.multi
|
||||
def price_rule_get_multi(self, products_by_qty_by_partner):
|
||||
@@ -288,7 +297,8 @@ class Pricelist(models.Model):
|
||||
@api.model
|
||||
def _price_get_multi(self, pricelist, products_by_qty_by_partner):
|
||||
""" Mono pricelist, multi product - return price per product """
|
||||
return pricelist.get_products_price(zip(**products_by_qty_by_partner))
|
||||
return pricelist.get_products_price(
|
||||
list(pycompat.izip(**products_by_qty_by_partner)))
|
||||
|
||||
def _get_partner_pricelist(self, partner_id, company_id=None):
|
||||
""" Retrieve the applicable pricelist for a given partner in a given company.
|
||||
|
||||
@@ -27,7 +27,7 @@ class report_product_pricelist(models.AbstractModel):
|
||||
}
|
||||
|
||||
def _get_quantity(self, data):
|
||||
return sorted([data['form'][key] for key in data['form'].keys() if key.startswith('qty') and data['form'][key]])
|
||||
return sorted([data['form'][key] for key in data['form'] if key.startswith('qty') and data['form'][key]])
|
||||
|
||||
def _get_categories(self, pricelist, products, quantities):
|
||||
categ_data = []
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import datetime
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class StockProductionLot(models.Model):
|
||||
@@ -31,10 +32,10 @@ class StockProductionLot(models.Model):
|
||||
'removal_date': 'removal_time',
|
||||
'alert_date': 'alert_time'
|
||||
}
|
||||
res = dict.fromkeys(mapped_fields.keys(), False)
|
||||
res = dict.fromkeys(mapped_fields, False)
|
||||
product = self.env['product.product'].browse(product_id) or self.product_id
|
||||
if product:
|
||||
for field in mapped_fields.keys():
|
||||
for field in mapped_fields:
|
||||
duration = getattr(product, mapped_fields[field])
|
||||
if duration:
|
||||
date = datetime.datetime.now() + datetime.timedelta(days=duration)
|
||||
@@ -45,7 +46,7 @@ class StockProductionLot(models.Model):
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
dates = self._get_dates(vals.get('product_id'))
|
||||
for d in dates.keys():
|
||||
for d in dates:
|
||||
if not vals.get(d):
|
||||
vals[d] = dates[d]
|
||||
return super(StockProductionLot, self).create(vals)
|
||||
@@ -53,5 +54,5 @@ class StockProductionLot(models.Model):
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product(self):
|
||||
dates_dict = self._get_dates()
|
||||
for field, value in dates_dict.items():
|
||||
for field, value in pycompat.items(dates_dict):
|
||||
setattr(self, field, value)
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
import time
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = "product.product"
|
||||
@@ -67,8 +69,8 @@ class ProductProduct(models.Model):
|
||||
prod_re[prod.id] = re_ind
|
||||
re_ind += 1
|
||||
res_val = tot_products._compute_product_margin_fields_values(field_names=[x for x in fields if fields in fields_list])
|
||||
for key in res_val.keys():
|
||||
for l in res_val[key].keys():
|
||||
for key in res_val:
|
||||
for l in res_val[key]:
|
||||
re = res[prod_re[key]]
|
||||
if re.get(l):
|
||||
re[l] += res_val[key][l]
|
||||
@@ -137,6 +139,6 @@ class ProductProduct(models.Model):
|
||||
res[val.id]['expected_margin'] = res[val.id]['sale_expected'] - res[val.id]['normal_cost']
|
||||
res[val.id]['total_margin_rate'] = res[val.id]['turnover'] and res[val.id]['total_margin'] * 100 / res[val.id]['turnover'] or 0.0
|
||||
res[val.id]['expected_margin_rate'] = res[val.id]['sale_expected'] and res[val.id]['expected_margin'] * 100 / res[val.id]['sale_expected'] or 0.0
|
||||
for k, v in res[val.id].items():
|
||||
for k, v in pycompat.items(res[val.id]):
|
||||
setattr(val, k, v)
|
||||
return res
|
||||
|
||||
@@ -614,7 +614,7 @@ class Task(models.Model):
|
||||
email_list = tools.email_split((msg.get('to') or '') + ',' + (msg.get('cc') or ''))
|
||||
# check left-part is not already an alias
|
||||
aliases = self.mapped('project_id.alias_name')
|
||||
return filter(lambda x: x.split('@')[0] not in aliases, email_list)
|
||||
return [x for x in email_list if x.split('@')[0] not in aliases]
|
||||
|
||||
@api.model
|
||||
def message_new(self, msg, custom_values=None):
|
||||
@@ -630,7 +630,7 @@ class Task(models.Model):
|
||||
|
||||
task = super(Task, self).message_new(msg, custom_values=defaults)
|
||||
email_list = task.email_split(msg)
|
||||
partner_ids = filter(None, task._find_partner_from_emails(email_list, force_create=False))
|
||||
partner_ids = [p for p in task._find_partner_from_emails(email_list, force_create=False) if p]
|
||||
task.message_subscribe(partner_ids)
|
||||
return task
|
||||
|
||||
@@ -655,7 +655,7 @@ class Task(models.Model):
|
||||
pass
|
||||
|
||||
email_list = self.email_split(msg)
|
||||
partner_ids = filter(None, self._find_partner_from_emails(email_list, force_create=False))
|
||||
partner_ids = [p for p in self._find_partner_from_emails(email_list, force_create=False) if p]
|
||||
self.message_subscribe(partner_ids)
|
||||
return super(Task, self).message_update(msg, update_vals=update_vals)
|
||||
|
||||
@@ -677,7 +677,7 @@ class Task(models.Model):
|
||||
except Exception:
|
||||
pass
|
||||
if self.project_id:
|
||||
current_objects = filter(None, headers.get('X-Odoo-Objects', '').split(','))
|
||||
current_objects = [h for h in headers.get('X-Odoo-Objects', '').split(',') if h]
|
||||
current_objects.insert(0, 'project.project-%s, ' % self.project_id.id)
|
||||
headers['X-Odoo-Objects'] = ','.join(current_objects)
|
||||
if self.tag_ids:
|
||||
|
||||
@@ -278,7 +278,7 @@ class ProjectIssue(models.Model):
|
||||
def email_split(self, msg):
|
||||
email_list = tools.email_split((msg.get('to') or '') + ',' + (msg.get('cc') or ''))
|
||||
# check left-part is not already an alias
|
||||
return filter(lambda x: x.split('@')[0] not in self.mapped('project_id.alias_name'), email_list)
|
||||
return [x for x in email_list if x.split('@')[0] not in self.mapped('project_id.alias_name')]
|
||||
|
||||
@api.model
|
||||
def message_new(self, msg, custom_values=None):
|
||||
@@ -303,7 +303,7 @@ class ProjectIssue(models.Model):
|
||||
|
||||
issue = super(ProjectIssue, self.with_context(create_context)).message_new(msg, custom_values=defaults)
|
||||
email_list = issue.email_split(msg)
|
||||
partner_ids = filter(None, issue._find_partner_from_emails(email_list))
|
||||
partner_ids = [p for p in issue._find_partner_from_emails(email_list) if p]
|
||||
issue.message_subscribe(partner_ids)
|
||||
return issue
|
||||
|
||||
@@ -311,7 +311,7 @@ class ProjectIssue(models.Model):
|
||||
def message_update(self, msg, update_vals=None):
|
||||
""" Override to update the issue according to the email. """
|
||||
email_list = self.email_split(msg)
|
||||
partner_ids = filter(None, self._find_partner_from_emails(email_list))
|
||||
partner_ids = [p for p in self._find_partner_from_emails(email_list) if p]
|
||||
self.message_subscribe(partner_ids)
|
||||
return super(ProjectIssue, self).message_update(msg, update_vals=update_vals)
|
||||
|
||||
@@ -351,7 +351,7 @@ class ProjectIssue(models.Model):
|
||||
except Exception:
|
||||
pass
|
||||
if self.project_id:
|
||||
current_objects = filter(None, headers.get('X-Odoo-Objects', '').split(','))
|
||||
current_objects = [h for h in headers.get('X-Odoo-Objects', '').split(',') if h]
|
||||
current_objects.insert(0, 'project.project-%s, ' % self.project_id.id)
|
||||
headers['X-Odoo-Objects'] = ','.join(current_objects)
|
||||
if self.tag_ids:
|
||||
|
||||
@@ -455,7 +455,7 @@ class PurchaseOrder(models.Model):
|
||||
pick_ids = sum([order.picking_ids.ids for order in self], [])
|
||||
#choose the view_mode accordingly
|
||||
if len(pick_ids) > 1:
|
||||
result['domain'] = "[('id','in',[" + ','.join(map(str, pick_ids)) + "])]"
|
||||
result['domain'] = "[('id','in',[" + ','.join(str(id) for id in pick_ids) + "])]"
|
||||
elif len(pick_ids) == 1:
|
||||
res = self.env.ref('stock.view_picking_form', False)
|
||||
result['views'] = [(res and res.id or False, 'form')]
|
||||
|
||||
@@ -6,6 +6,7 @@ import uuid
|
||||
from odoo import api, fields, models, tools, _
|
||||
|
||||
from odoo.modules.module import get_resource_path
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class Rating(models.Model):
|
||||
@@ -214,7 +215,7 @@ class RatingMixin(models.AbstractModel):
|
||||
values.update((d['rating'], d['rating_count']) for d in data)
|
||||
# add other stats
|
||||
if add_stats:
|
||||
rating_number = sum(values.values())
|
||||
rating_number = sum(pycompat.values(values))
|
||||
result = {
|
||||
'repartition': values,
|
||||
'avg': sum(float(key * values[key]) for key in values) / rating_number if rating_number > 0 else 0,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
@@ -73,13 +74,13 @@ class Project(models.Model):
|
||||
domain = [('create_date', '>=', fields.Datetime.to_string(fields.datetime.now() - timedelta(days=30)))]
|
||||
for project in self:
|
||||
activity = project.tasks.rating_get_grades(domain)
|
||||
project.percentage_satisfaction_project = activity['great'] * 100 / sum(activity.values()) if sum(activity.values()) else -1
|
||||
project.percentage_satisfaction_project = activity['great'] * 100 / sum(pycompat.values(activity)) if sum(pycompat.values(activity)) else -1
|
||||
|
||||
@api.one
|
||||
@api.depends('tasks.rating_ids.rating')
|
||||
def _compute_percentage_satisfaction_task(self):
|
||||
activity = self.tasks.rating_get_grades()
|
||||
self.percentage_satisfaction_task = activity['great'] * 100 / sum(activity.values()) if sum(activity.values()) else -1
|
||||
self.percentage_satisfaction_task = activity['great'] * 100 / sum(pycompat.values(activity)) if sum(pycompat.values(activity)) else -1
|
||||
|
||||
percentage_satisfaction_task = fields.Integer(
|
||||
compute='_compute_percentage_satisfaction_task', string="Happy % on Task", store=True, default=-1)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
@@ -56,11 +57,11 @@ class Project(models.Model):
|
||||
if project.use_tasks:
|
||||
activity_task = project.tasks.rating_get_grades(domain)
|
||||
activity_great = activity_task['great']
|
||||
activity_sum = sum(activity_task.values())
|
||||
activity_sum = sum(pycompat.values(activity_task))
|
||||
if project.use_issues:
|
||||
activity_issue = self.env['project.issue'].search([('project_id', '=', project.id)]).rating_get_grades(domain)
|
||||
activity_great += activity_issue['great']
|
||||
activity_sum += sum(activity_issue.values())
|
||||
activity_sum += sum(pycompat.values(activity_issue))
|
||||
project.percentage_satisfaction_project = activity_great * 100 / activity_sum if activity_sum else -1
|
||||
|
||||
@api.one
|
||||
@@ -68,7 +69,7 @@ class Project(models.Model):
|
||||
def _compute_percentage_satisfaction_issue(self):
|
||||
project_issue = self.env['project.issue'].search([('project_id', '=', self.id)])
|
||||
activity = project_issue.rating_get_grades()
|
||||
self.percentage_satisfaction_issue = activity['great'] * 100 / sum(activity.values()) if sum(activity.values()) else -1
|
||||
self.percentage_satisfaction_issue = activity['great'] * 100 / sum(pycompat.values(activity)) if sum(pycompat.values(activity)) else -1
|
||||
|
||||
percentage_satisfaction_issue = fields.Integer(compute='_compute_percentage_satisfaction_issue', string="Happy % on Issue", store=True, default=-1)
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ class ResourceCalendar(models.Model):
|
||||
""" Return the list of weekdays that contain at least one working
|
||||
interval. """
|
||||
self.ensure_one()
|
||||
return list(set(map(int, (self.attendance_ids.mapped('dayofweek')))))
|
||||
return list({int(d) for d in self.attendance_ids.mapped('dayofweek')})
|
||||
|
||||
@api.multi
|
||||
def _get_next_work_day(self, day_date):
|
||||
|
||||
@@ -92,4 +92,4 @@ class ResourceMixin(models.AbstractModel):
|
||||
attendances = calendar._get_day_attendances(day_date, False, False)
|
||||
if not attendances:
|
||||
return 0
|
||||
return sum(map(lambda i: float(i.hour_to) - float(i.hour_from), attendances))
|
||||
return sum(float(i.hour_to) - float(i.hour_from) for i in attendances)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from itertools import groupby
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
@@ -57,7 +58,7 @@ class AccountInvoice(models.Model):
|
||||
result = super(AccountInvoice, self)._refund_cleanup_lines(lines)
|
||||
if self.env.context.get('mode') == 'modify':
|
||||
for i, line in enumerate(lines):
|
||||
for name, field in line._fields.iteritems():
|
||||
for name, field in pycompat.items(line._fields):
|
||||
if name == 'sale_line_ids':
|
||||
result[i][2][name] = [(6, 0, line[name].ids)]
|
||||
line[name] = False
|
||||
|
||||
@@ -21,7 +21,7 @@ class ResPartner(models.Model):
|
||||
mapped_data = dict([(m['partner_id'][0], m['partner_id_count']) for m in sale_data])
|
||||
for partner in self:
|
||||
# let's obtain the partner id and all its child ids from the read up there
|
||||
partner_ids = filter(lambda r: r['id'] == partner.id, partner_child_ids)[0]
|
||||
partner_ids = [partner_ids.get('id')] + partner_ids.get('child_ids')
|
||||
item = next(p for p in partner_child_ids if p['id'] == partner.id)
|
||||
partner_ids = [partner.id] + item.get('child_ids')
|
||||
# then we can sum for all the partner's child
|
||||
partner.sale_order_count = sum(mapped_data.get(child, 0) for child in partner_ids)
|
||||
|
||||
@@ -6,7 +6,7 @@ from datetime import datetime, timedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT, pycompat
|
||||
from odoo.tools.misc import formatLang
|
||||
|
||||
import odoo.addons.decimal_precision as dp
|
||||
@@ -362,7 +362,7 @@ class SaleOrder(models.Model):
|
||||
if not invoices:
|
||||
raise UserError(_('There is no invoicable line.'))
|
||||
|
||||
for invoice in invoices.values():
|
||||
for invoice in pycompat.values(invoices):
|
||||
if not invoice.invoice_line_ids:
|
||||
raise UserError(_('There is no invoicable line.'))
|
||||
# If invoice is negative, do a refund invoice instead
|
||||
@@ -379,7 +379,7 @@ class SaleOrder(models.Model):
|
||||
invoice.message_post_with_view('mail.message_origin_link',
|
||||
values={'self': invoice, 'origin': references[invoice]},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
return [inv.id for inv in invoices.values()]
|
||||
return [inv.id for inv in pycompat.values(invoices)]
|
||||
|
||||
@api.multi
|
||||
def action_draft(self):
|
||||
@@ -517,8 +517,8 @@ class SaleOrder(models.Model):
|
||||
if tax.include_base_amount:
|
||||
base_tax += tax.compute_all(line.price_reduce + base_tax, quantity=1, product=line.product_id,
|
||||
partner=self.partner_shipping_id)['taxes'][0]['amount']
|
||||
res = sorted(res.items(), key=lambda l: l[0].sequence)
|
||||
res = map(lambda l: (l[0].name, l[1]), res)
|
||||
res = sorted(pycompat.items(res), key=lambda l: l[0].sequence)
|
||||
res = [(l[0].name, l[1]) for l in res]
|
||||
return res
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import pycompat
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
@@ -46,7 +48,7 @@ class SaleOrderLine(models.Model):
|
||||
qty = d['unit_amount']
|
||||
lines[line] += qty
|
||||
|
||||
for line, qty in lines.items():
|
||||
for line, qty in pycompat.items(lines):
|
||||
line.qty_delivered = qty
|
||||
return True
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.exceptions import UserError, AccessError
|
||||
from odoo.tools import pycompat
|
||||
|
||||
from .test_sale_common import TestSale
|
||||
|
||||
@@ -17,10 +18,10 @@ class TestSaleOrder(TestSale):
|
||||
'partner_id': self.partner.id,
|
||||
'partner_invoice_id': self.partner.id,
|
||||
'partner_shipping_id': self.partner.id,
|
||||
'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in self.products.iteritems()],
|
||||
'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in pycompat.items(self.products)],
|
||||
'pricelist_id': self.env.ref('product.list0').id,
|
||||
})
|
||||
self.assertEqual(so.amount_total, sum([2 * p.list_price for (k, p) in self.products.iteritems()]), 'Sale: total amount is wrong')
|
||||
self.assertEqual(so.amount_total, sum([2 * p.list_price for (k, p) in pycompat.items(self.products)]), 'Sale: total amount is wrong')
|
||||
|
||||
# send quotation
|
||||
so.force_quotation_send()
|
||||
@@ -35,7 +36,7 @@ class TestSaleOrder(TestSale):
|
||||
inv_id = so.action_invoice_create()
|
||||
inv = inv_obj.browse(inv_id)
|
||||
self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: invoice is missing lines')
|
||||
self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'order' else 0 for (k, p) in self.products.iteritems()]), 'Sale: invoice total amount is wrong')
|
||||
self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'order' else 0 for (k, p) in pycompat.items(self.products)]), 'Sale: invoice total amount is wrong')
|
||||
self.assertTrue(so.invoice_status == 'no', 'Sale: SO status after invoicing should be "nothing to invoice"')
|
||||
self.assertTrue(len(so.invoice_ids) == 1, 'Sale: invoice is missing')
|
||||
|
||||
@@ -46,7 +47,7 @@ class TestSaleOrder(TestSale):
|
||||
inv_id = so.action_invoice_create()
|
||||
inv = inv_obj.browse(inv_id)
|
||||
self.assertEqual(len(inv.invoice_line_ids), 2, 'Sale: second invoice is missing lines')
|
||||
self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'delivery' else 0 for (k, p) in self.products.iteritems()]), 'Sale: second invoice total amount is wrong')
|
||||
self.assertEqual(inv.amount_total, sum([2 * p.list_price if p.invoice_policy == 'delivery' else 0 for (k, p) in pycompat.items(self.products)]), 'Sale: second invoice total amount is wrong')
|
||||
self.assertTrue(so.invoice_status == 'invoiced', 'Sale: SO status after invoicing everything should be "invoiced"')
|
||||
self.assertTrue(len(so.invoice_ids) == 2, 'Sale: invoice is missing')
|
||||
# go over the sold quantity
|
||||
@@ -71,7 +72,7 @@ class TestSaleOrder(TestSale):
|
||||
'partner_id': self.partner.id,
|
||||
'partner_invoice_id': self.partner.id,
|
||||
'partner_shipping_id': self.partner.id,
|
||||
'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in self.products.iteritems()],
|
||||
'order_line': [(0, 0, {'name': p.name, 'product_id': p.id, 'product_uom_qty': 2, 'product_uom': p.uom_id.id, 'price_unit': p.list_price}) for (_, p) in pycompat.items(self.products)],
|
||||
'pricelist_id': self.env.ref('product.list0').id,
|
||||
})
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class TestSaleExpense(TestSale):
|
||||
# Create Expense Entries
|
||||
sheet.action_sheet_move_create()
|
||||
# expense should now be in sales order
|
||||
self.assertTrue(prod_exp_1 in map(lambda so: so.product_id, so.order_line), 'Sale Expense: expense product should be in so')
|
||||
self.assertIn(prod_exp_1, so.mapped('order_line.product_id'), 'Sale Expense: expense product should be in so')
|
||||
sol = so.order_line.filtered(lambda sol: sol.product_id.id == prod_exp_1.id)
|
||||
self.assertEqual((sol.price_unit, sol.qty_delivered), (621.54, 1.0), 'Sale Expense: error when invoicing an expense at cost')
|
||||
self.assertEqual(so.amount_total, init_price, 'Sale Expense: price of so not updated after adding expense')
|
||||
@@ -76,7 +76,7 @@ class TestSaleExpense(TestSale):
|
||||
# Create Expense Entries
|
||||
sheet.action_sheet_move_create()
|
||||
# expense should now be in sales order
|
||||
self.assertTrue(prod_exp_2 in map(lambda so: so.product_id, so.order_line), 'Sale Expense: expense product should be in so')
|
||||
self.assertIn(prod_exp_2, so.mapped('order_line.product_id'), 'Sale Expense: expense product should be in so')
|
||||
sol = so.order_line.filtered(lambda sol: sol.product_id.id == prod_exp_2.id)
|
||||
self.assertEqual((sol.price_unit, sol.qty_delivered), (prod_exp_2.list_price, 100.0), 'Sale Expense: error when invoicing an expense at cost')
|
||||
self.assertEqual(so.amount_total, init_price, 'Sale Expense: price of so not updated after adding expense')
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user