[MERGE] forward port of branch 7.0 up to d24fcd1
This commit is contained in:
commit
dd8cbf49ac
|
@ -40,18 +40,24 @@ class hr_timesheet_sheet(osv.osv):
|
|||
""" Compute the attendances, analytic lines timesheets and differences between them
|
||||
for all the days of a timesheet and the current day
|
||||
"""
|
||||
res = dict.fromkeys(ids, {
|
||||
'total_attendance': 0.0,
|
||||
'total_timesheet': 0.0,
|
||||
'total_difference': 0.0,
|
||||
})
|
||||
|
||||
cr.execute("""
|
||||
SELECT sheet_id as id,
|
||||
sum(total_attendance) as total_attendance,
|
||||
sum(total_timesheet) as total_timesheet,
|
||||
sum(total_difference) as total_difference
|
||||
FROM hr_timesheet_sheet_sheet_day
|
||||
WHERE sheet_id IN %s
|
||||
GROUP BY sheet_id
|
||||
""", (tuple(ids),))
|
||||
|
||||
res.update(dict((x.pop('id'), x) for x in cr.dictfetchall()))
|
||||
|
||||
res = {}
|
||||
for sheet in self.browse(cr, uid, ids, context=context or {}):
|
||||
res.setdefault(sheet.id, {
|
||||
'total_attendance': 0.0,
|
||||
'total_timesheet': 0.0,
|
||||
'total_difference': 0.0,
|
||||
})
|
||||
for period in sheet.period_ids:
|
||||
res[sheet.id]['total_attendance'] += period.total_attendance
|
||||
res[sheet.id]['total_timesheet'] += period.total_timesheet
|
||||
res[sheet.id]['total_difference'] += period.total_attendance - period.total_timesheet
|
||||
return res
|
||||
|
||||
def check_employee_attendance_state(self, cr, uid, sheet_id, context=None):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import unittest2
|
||||
|
||||
import openerp.osv.expression as expression
|
||||
from openerp.osv.expression import get_unaccent_wrapper
|
||||
from openerp.osv.orm import BaseModel
|
||||
import openerp.tests.common as common
|
||||
|
@ -459,6 +460,25 @@ class test_expression(common.TransactionCase):
|
|||
partner_parent_id_col._auto_join = False
|
||||
state_country_id_col._auto_join = False
|
||||
|
||||
def test_40_negating_long_expression(self):
|
||||
source = ['!','&',('user_id','=',4),('partner_id','in',[1,2])]
|
||||
expect = ['|',('user_id','!=',4),('partner_id','not in',[1,2])]
|
||||
self.assertEqual(expression.distribute_not(source), expect,
|
||||
"distribute_not on expression applied wrongly")
|
||||
|
||||
pos_leaves = [[('a', 'in', [])], [('d', '!=', 3)]]
|
||||
neg_leaves = [[('a', 'not in', [])], [('d', '=', 3)]]
|
||||
|
||||
source = expression.OR([expression.AND(pos_leaves)] * 1000)
|
||||
expect = source
|
||||
self.assertEqual(expression.distribute_not(source), expect,
|
||||
"distribute_not on long expression without negation operator should not alter it")
|
||||
|
||||
source = ['!'] + source
|
||||
expect = expression.AND([expression.OR(neg_leaves)] * 1000)
|
||||
self.assertEqual(expression.distribute_not(source), expect,
|
||||
"distribute_not on long expression applied wrongly")
|
||||
|
||||
def test_translate_search(self):
|
||||
Country = self.registry('res.country')
|
||||
be = self.ref('base.be')
|
||||
|
|
|
@ -164,6 +164,26 @@ TERM_OPERATORS = ('=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike',
|
|||
# legal in the processed term.
|
||||
NEGATIVE_TERM_OPERATORS = ('!=', 'not like', 'not ilike', 'not in')
|
||||
|
||||
# Negation of domain expressions
|
||||
DOMAIN_OPERATORS_NEGATION = {
|
||||
AND_OPERATOR: OR_OPERATOR,
|
||||
OR_OPERATOR: AND_OPERATOR,
|
||||
}
|
||||
TERM_OPERATORS_NEGATION = {
|
||||
'<': '>=',
|
||||
'>': '<=',
|
||||
'<=': '>',
|
||||
'>=': '<',
|
||||
'=': '!=',
|
||||
'!=': '=',
|
||||
'in': 'not in',
|
||||
'like': 'not like',
|
||||
'ilike': 'not ilike',
|
||||
'not in': 'in',
|
||||
'not like': 'like',
|
||||
'not ilike': 'ilike',
|
||||
}
|
||||
|
||||
TRUE_LEAF = (1, '=', 1)
|
||||
FALSE_LEAF = (0, '=', 1)
|
||||
|
||||
|
@ -260,51 +280,36 @@ def distribute_not(domain):
|
|||
['|',('user_id','!=',4),('partner_id','not in',[1,2])]
|
||||
|
||||
"""
|
||||
def negate(leaf):
|
||||
"""Negates and returns a single domain leaf term,
|
||||
using the opposite operator if possible"""
|
||||
left, operator, right = leaf
|
||||
mapping = {
|
||||
'<': '>=',
|
||||
'>': '<=',
|
||||
'<=': '>',
|
||||
'>=': '<',
|
||||
'=': '!=',
|
||||
'!=': '=',
|
||||
}
|
||||
if operator in ('in', 'like', 'ilike'):
|
||||
operator = 'not ' + operator
|
||||
return [(left, operator, right)]
|
||||
if operator in ('not in', 'not like', 'not ilike'):
|
||||
operator = operator[4:]
|
||||
return [(left, operator, right)]
|
||||
if operator in mapping:
|
||||
operator = mapping[operator]
|
||||
return [(left, operator, right)]
|
||||
return [NOT_OPERATOR, (left, operator, right)]
|
||||
|
||||
def distribute_negate(domain):
|
||||
"""Negate the domain ``subtree`` rooted at domain[0],
|
||||
leaving the rest of the domain intact, and return
|
||||
(negated_subtree, untouched_domain_rest)
|
||||
"""
|
||||
if is_leaf(domain[0]):
|
||||
return negate(domain[0]), domain[1:]
|
||||
if domain[0] == AND_OPERATOR:
|
||||
done1, todo1 = distribute_negate(domain[1:])
|
||||
done2, todo2 = distribute_negate(todo1)
|
||||
return [OR_OPERATOR] + done1 + done2, todo2
|
||||
if domain[0] == OR_OPERATOR:
|
||||
done1, todo1 = distribute_negate(domain[1:])
|
||||
done2, todo2 = distribute_negate(todo1)
|
||||
return [AND_OPERATOR] + done1 + done2, todo2
|
||||
if not domain:
|
||||
return []
|
||||
if domain[0] != NOT_OPERATOR:
|
||||
return [domain[0]] + distribute_not(domain[1:])
|
||||
if domain[0] == NOT_OPERATOR:
|
||||
done, todo = distribute_negate(domain[1:])
|
||||
return done + distribute_not(todo)
|
||||
# This is an iterative version of a recursive function that split domain
|
||||
# into subdomains, processes them and combine the results. The "stack" below
|
||||
# represents the recursive calls to be done.
|
||||
result = []
|
||||
stack = [False]
|
||||
|
||||
for token in domain:
|
||||
negate = stack.pop()
|
||||
# negate tells whether the subdomain starting with token must be negated
|
||||
if is_leaf(token):
|
||||
if negate:
|
||||
left, operator, right = token
|
||||
if operator in TERM_OPERATORS_NEGATION:
|
||||
result.append((left, TERM_OPERATORS_NEGATION[operator], right))
|
||||
else:
|
||||
result.append(NOT_OPERATOR)
|
||||
result.append(token)
|
||||
else:
|
||||
result.append(token)
|
||||
elif token == NOT_OPERATOR:
|
||||
stack.append(not negate)
|
||||
elif token in DOMAIN_OPERATORS_NEGATION:
|
||||
result.append(DOMAIN_OPERATORS_NEGATION[token] if negate else token)
|
||||
stack.append(negate)
|
||||
stack.append(negate)
|
||||
else:
|
||||
result.append(token)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue