[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
|
""" Compute the attendances, analytic lines timesheets and differences between them
|
||||||
for all the days of a timesheet and the current day
|
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
|
return res
|
||||||
|
|
||||||
def check_employee_attendance_state(self, cr, uid, sheet_id, context=None):
|
def check_employee_attendance_state(self, cr, uid, sheet_id, context=None):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import unittest2
|
import unittest2
|
||||||
|
|
||||||
|
import openerp.osv.expression as expression
|
||||||
from openerp.osv.expression import get_unaccent_wrapper
|
from openerp.osv.expression import get_unaccent_wrapper
|
||||||
from openerp.osv.orm import BaseModel
|
from openerp.osv.orm import BaseModel
|
||||||
import openerp.tests.common as common
|
import openerp.tests.common as common
|
||||||
|
@ -459,6 +460,25 @@ class test_expression(common.TransactionCase):
|
||||||
partner_parent_id_col._auto_join = False
|
partner_parent_id_col._auto_join = False
|
||||||
state_country_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):
|
def test_translate_search(self):
|
||||||
Country = self.registry('res.country')
|
Country = self.registry('res.country')
|
||||||
be = self.ref('base.be')
|
be = self.ref('base.be')
|
||||||
|
|
|
@ -164,6 +164,26 @@ TERM_OPERATORS = ('=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike',
|
||||||
# legal in the processed term.
|
# legal in the processed term.
|
||||||
NEGATIVE_TERM_OPERATORS = ('!=', 'not like', 'not ilike', 'not in')
|
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)
|
TRUE_LEAF = (1, '=', 1)
|
||||||
FALSE_LEAF = (0, '=', 1)
|
FALSE_LEAF = (0, '=', 1)
|
||||||
|
|
||||||
|
@ -260,51 +280,36 @@ def distribute_not(domain):
|
||||||
['|',('user_id','!=',4),('partner_id','not in',[1,2])]
|
['|',('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):
|
# This is an iterative version of a recursive function that split domain
|
||||||
"""Negate the domain ``subtree`` rooted at domain[0],
|
# into subdomains, processes them and combine the results. The "stack" below
|
||||||
leaving the rest of the domain intact, and return
|
# represents the recursive calls to be done.
|
||||||
(negated_subtree, untouched_domain_rest)
|
result = []
|
||||||
"""
|
stack = [False]
|
||||||
if is_leaf(domain[0]):
|
|
||||||
return negate(domain[0]), domain[1:]
|
for token in domain:
|
||||||
if domain[0] == AND_OPERATOR:
|
negate = stack.pop()
|
||||||
done1, todo1 = distribute_negate(domain[1:])
|
# negate tells whether the subdomain starting with token must be negated
|
||||||
done2, todo2 = distribute_negate(todo1)
|
if is_leaf(token):
|
||||||
return [OR_OPERATOR] + done1 + done2, todo2
|
if negate:
|
||||||
if domain[0] == OR_OPERATOR:
|
left, operator, right = token
|
||||||
done1, todo1 = distribute_negate(domain[1:])
|
if operator in TERM_OPERATORS_NEGATION:
|
||||||
done2, todo2 = distribute_negate(todo1)
|
result.append((left, TERM_OPERATORS_NEGATION[operator], right))
|
||||||
return [AND_OPERATOR] + done1 + done2, todo2
|
else:
|
||||||
if not domain:
|
result.append(NOT_OPERATOR)
|
||||||
return []
|
result.append(token)
|
||||||
if domain[0] != NOT_OPERATOR:
|
else:
|
||||||
return [domain[0]] + distribute_not(domain[1:])
|
result.append(token)
|
||||||
if domain[0] == NOT_OPERATOR:
|
elif token == NOT_OPERATOR:
|
||||||
done, todo = distribute_negate(domain[1:])
|
stack.append(not negate)
|
||||||
return done + distribute_not(todo)
|
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