From d0ddde6ea76e68f02eb48b40431fd4f08ae21d5c Mon Sep 17 00:00:00 2001 From: Guewen Baconnier <> Date: Thu, 22 Sep 2011 16:34:40 +0530 Subject: [PATCH 01/33] hr_timesheet_sheet: Timesheets big performance issues lp bug: https://launchpad.net/bugs/798732 fixed bzr revid: bde@tinyerp.com-20110922110440-0c4jhq2gmh627s63 --- .../hr_timesheet_sheet/hr_timesheet_sheet.py | 405 +++++++++--------- .../hr_timesheet_sheet_view.xml | 45 +- 2 files changed, 248 insertions(+), 202 deletions(-) diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index 8f5c9638a53..ee419054d05 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -20,7 +20,7 @@ ############################################################################## import time -from datetime import datetime +from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta from osv import fields, osv @@ -35,16 +35,9 @@ class one2many_mod2(fields.one2many): if values is None: values = {} - # dict: - # {idn: (date_current, user_id), ... - # 1: ('2010-08-15', 1)} - res6 = dict([(rec['id'], (rec['date_current'], rec['user_id'][0])) - for rec - in obj.read(cr, user, ids, ['date_current', 'user_id'], context=context)]) + res6 = dict([(rec['id'], rec['date_current']) + for rec in obj.read(cr, user, ids, ['date_current'], context=context)]) - # eg: ['|', '|', - # '&', '&', ('name', '>=', '2011-03-01'), ('name', '<=', '2011-03-01'), ('employee_id.user_id', '=', 1), - # '&', '&', ('name', '>=', '2011-02-01'), ('name', '<=', '2011-02-01'), ('employee_id.user_id', '=', 1)] dom = [] for c, id in enumerate(ids): if id in res6: @@ -52,9 +45,9 @@ class one2many_mod2(fields.one2many): dom.insert(0 ,'|') dom.append('&') dom.append('&') - dom.append(('name', '>=', res6[id][0])) - dom.append(('name', '<=', res6[id][0])) - dom.append(('employee_id.user_id', '=', res6[id][1])) + dom.append(('name', '>=', res6[id])) + dom.append(('name', '<=', res6[id])) + dom.append(('sheet_id', '=', id)) ids2 = obj.pool.get(self._obj).search(cr, user, dom, limit=self._limit) @@ -62,10 +55,9 @@ class one2many_mod2(fields.one2many): for i in ids: res[i] = [] - for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): + for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_read'): if r[self._fields_id]: res[r[self._fields_id][0]].append(r['id']) - return res def set(self, cr, obj, id, field, values, user=None, context=None): @@ -85,62 +77,161 @@ class one2many_mod(fields.one2many): if values is None: values = {} - - res5 = obj.read(cr, user, ids, ['date_current', 'user_id'], context=context) + res5 = obj.read(cr, user, ids, ['date_current'], context=context) res6 = {} for r in res5: - res6[r['id']] = (r['date_current'], r['user_id'][0]) + res6[r['id']] = r['date_current'] ids2 = [] for id in ids: dom = [] if id in res6: - dom = [('date', '=', res6[id][0]), ('user_id', '=', res6[id][1])] + dom = [('date', '=', res6[id]), ('sheet_id', '=', id)] ids2.extend(obj.pool.get(self._obj).search(cr, user, dom, limit=self._limit)) res = {} for i in ids: res[i] = [] for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, - [self._fields_id], context=context, load='_classic_write'): + [self._fields_id], context=context, load='_classic_read'): if r[self._fields_id]: res[r[self._fields_id][0]].append(r['id']) return res + class hr_timesheet_sheet(osv.osv): _name = "hr_timesheet_sheet.sheet" _table = 'hr_timesheet_sheet_sheet' _order = "id desc" _description="Timesheet" - def _total_day(self, cr, uid, ids, name, args, context=None): + def _total_attendances(self, cr, uid, ids, name, args, context=None): + """ + Get the total attendance for the timesheets + Returns a dict like : + {id: {'date_current': '2011-06-17', + 'totals_per_day': { + day: timedelta, + day: timedelta} + } + } + """ + context = context or {} + attendance_obj = self.pool.get('hr.attendance') res = {} - cr.execute('SELECT sheet.id, day.total_attendance, day.total_timesheet, day.total_difference\ - FROM hr_timesheet_sheet_sheet AS sheet \ - LEFT JOIN hr_timesheet_sheet_sheet_day AS day \ - ON (sheet.id = day.sheet_id \ - AND day.name = sheet.date_current) \ - WHERE sheet.id IN %s',(tuple(ids),)) - for record in cr.fetchall(): - res[record[0]] = {} - res[record[0]]['total_attendance_day'] = record[1] - res[record[0]]['total_timesheet_day'] = record[2] - res[record[0]]['total_difference_day'] = record[3] + for sheet_id in ids: + sheet = self.browse(cr, uid, sheet_id, context) + date_current = sheet.date_current + # field attendances_ids of hr_timesheet_sheet.sheet only + # returns attendances of timesheet's current date + attendance_ids = attendance_obj.search(cr, uid, [('sheet_id', '=', sheet_id)], context=context) + attendances = attendance_obj.browse(cr, uid, attendance_ids, context=context) + total_attendance = {} + for attendance in [att for att in attendances + if att.action in ('sign_in', 'sign_out')]: + day = attendance.name[:10] + if not total_attendance.get(day, False): + total_attendance[day] = timedelta(seconds=0) + + attendance_in_time = datetime.strptime(attendance.name, '%Y-%m-%d %H:%M:%S') + attendance_interval = timedelta(hours=attendance_in_time.hour, + minutes=attendance_in_time.minute, + seconds=attendance_in_time.second) + if attendance.action == 'sign_in': + total_attendance[day] -= attendance_interval + else: + total_attendance[day] += attendance_interval + + # if the delta is negative, it means that a sign out is missing + # in a such case, we want to have the time to the end of the day + # for a past date, and the time to now for the current date + if total_attendance[day] < timedelta(0): + if day == date_current: + now = datetime.now() + total_attendance[day] += timedelta(hours=now.hour, + minutes=now.minute, + seconds=now.second) + else: + total_attendance[day] += timedelta(days=1) + + res[sheet_id] = {'date_current': date_current, + 'totals_per_day': total_attendance} + return res + + def _total_timesheet(self, cr, uid, ids, name, args, context=None): + """ + Get the total of analytic lines for the timesheets + Returns a dict like : + {id: {day: timedelta, + day: timedelta,}} + """ + context = context or {} + sheet_line_obj = self.pool.get('hr.analytic.timesheet') + + res = {} + for sheet_id in ids: + # field timesheet_ids of hr_timesheet_sheet.sheet only + # returns lines of timesheet's current date + sheet_lines_ids = sheet_line_obj.search(cr, uid, [('sheet_id', '=', sheet_id)], context=context) + sheet_lines = sheet_line_obj.browse(cr, uid, sheet_lines_ids, context=context) + total_timesheet = {} + for line in sheet_lines: + day = line.date + if not total_timesheet.get(day, False): + total_timesheet[day] = timedelta(seconds=0) + total_timesheet[day] += timedelta(hours=line.unit_amount) + res[sheet_id] = total_timesheet return res def _total(self, cr, uid, ids, name, args, context=None): + """ + Compute the attendances, analytic lines timesheets and differences between them + for all the days of a timesheet and the current day + """ + def sum_all_days(sheet_amounts): + if not sheet_amounts: + return timedelta(seconds=0) + total = reduce(lambda memo, value: memo + value, sheet_amounts.values()) + return total + + def timedelta_to_hours(delta): + hours = 0.0 + seconds = float(delta.seconds) + if delta.microseconds: + seconds += float(delta.microseconds) / 100000 + hours += delta.days * 24 + if seconds: + hours += seconds / 3600 + return hours + res = {} - cr.execute('SELECT s.id, COALESCE(SUM(d.total_attendance),0), COALESCE(SUM(d.total_timesheet),0), COALESCE(SUM(d.total_difference),0) \ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN hr_timesheet_sheet_sheet_day d \ - ON (s.id = d.sheet_id) \ - WHERE s.id IN %s GROUP BY s.id',(tuple(ids),)) - for record in cr.fetchall(): - res[record[0]] = {} - res[record[0]]['total_attendance'] = record[1] - res[record[0]]['total_timesheet'] = record[2] - res[record[0]]['total_difference'] = record[3] + all_timesheet_attendances = self._total_attendances(cr, uid, ids, name, args, context) + all_timesheet_lines = self._total_timesheet(cr, uid, ids, name, args, context) + for id in ids: + res[id] = {} + + all_attendances_sheet = all_timesheet_attendances[id] + + date_current = all_attendances_sheet['date_current'] + total_attendances_sheet = all_attendances_sheet['totals_per_day'] + total_attendances_all_days = sum_all_days(total_attendances_sheet) + total_attendances_day = total_attendances_sheet.get(date_current, timedelta(seconds=0)) + + total_timesheets_sheet = all_timesheet_lines[id] + total_timesheets_all_days = sum_all_days(total_timesheets_sheet) + total_timesheets_day = total_timesheets_sheet.get(date_current, timedelta(seconds=0)) + + total_difference_all_days = total_attendances_all_days - total_timesheets_all_days + total_difference_day = total_attendances_day - total_timesheets_day + + res[id]['total_attendance'] = timedelta_to_hours(total_attendances_all_days) + res[id]['total_timesheet'] = timedelta_to_hours(total_timesheets_all_days) + res[id]['total_difference'] = timedelta_to_hours(total_difference_all_days) + + res[id]['total_attendance_day'] = timedelta_to_hours(total_attendances_day) + res[id]['total_timesheet_day'] = timedelta_to_hours(total_timesheets_day) + res[id]['total_difference_day'] = timedelta_to_hours(total_difference_day) return res def _state_attendance(self, cr, uid, ids, name, args, context=None): @@ -173,6 +264,7 @@ class hr_timesheet_sheet(osv.osv): def copy(self, cr, uid, ids, *args, **argv): raise osv.except_osv(_('Error !'), _('You cannot duplicate a timesheet!')) + def create(self, cr, uid, vals, *args, **argv): if 'employee_id' in vals: if not self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).user_id: @@ -287,12 +379,12 @@ class hr_timesheet_sheet(osv.osv): \n* The \'Confirmed\' state is used for to confirm the timesheet by user. \ \n* The \'Done\' state is used when users timesheet is accepted by his/her senior.'), 'state_attendance' : fields.function(_state_attendance, type='selection', selection=[('absent', 'Absent'), ('present', 'Present'),('none','No employee defined')], string='Current Status'), - 'total_attendance_day': fields.function(_total_day, string='Total Attendance', multi="_total_day"), - 'total_timesheet_day': fields.function(_total_day, string='Total Timesheet', multi="_total_day"), - 'total_difference_day': fields.function(_total_day, string='Difference', multi="_total_day"), - 'total_attendance': fields.function(_total, string='Total Attendance', multi="_total_sheet"), - 'total_timesheet': fields.function(_total, string='Total Timesheet', multi="_total_sheet"), - 'total_difference': fields.function(_total, string='Difference', multi="_total_sheet"), + 'total_attendance_day': fields.function(_total, method=True, string='Total Attendance', multi="_total"), + 'total_timesheet_day': fields.function(_total, method=True, string='Total Timesheet', multi="_total"), + 'total_difference_day': fields.function(_total, method=True, string='Difference', multi="_total"), + 'total_attendance': fields.function(_total, method=True, string='Total Attendance', multi="_total"), + 'total_timesheet': fields.function(_total, method=True, string='Total Timesheet', multi="_total"), + 'total_difference': fields.function(_total, method=True, string='Difference', multi="_total"), 'period_ids': fields.one2many('hr_timesheet_sheet.sheet.day', 'sheet_id', 'Period', readonly=True), 'account_ids': fields.one2many('hr_timesheet_sheet.sheet.account', 'sheet_id', 'Analytic accounts', readonly=True), 'company_id': fields.many2one('res.company', 'Company'), @@ -397,85 +489,47 @@ class hr_timesheet_line(osv.osv): def _sheet(self, cursor, user, ids, name, args, context=None): sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - cursor.execute('SELECT l.id, COALESCE(MAX(s.id), 0) \ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN (hr_analytic_timesheet l \ - LEFT JOIN account_analytic_line al \ - ON (l.line_id = al.id)) \ - ON (s.date_to >= al.date \ - AND s.date_from <= al.date \ - AND s.user_id = al.user_id) \ - WHERE l.id IN %s GROUP BY l.id',(tuple(ids),)) - res = dict(cursor.fetchall()) - sheet_names = {} - for sheet_id, name in sheet_obj.name_get(cursor, user, res.values(), - context=context): - sheet_names[sheet_id] = name - - for line_id in {}.fromkeys(ids): - sheet_id = res.get(line_id, False) - if sheet_id: - res[line_id] = (sheet_id, sheet_names[sheet_id]) - else: - res[line_id] = False + res = {}.fromkeys(ids, False) + for ts_line in self.browse(cursor, user, ids, context=context): + sheet_ids = sheet_obj.search(cursor, user, + [('date_to', '>=', ts_line.date), + ('date_from', '<=', ts_line.date), + ('employee_id.user_id', '=', ts_line.user_id.id)], context=context) + if sheet_ids: + # [0] because only one sheet possible for an employee between 2 dates + res[ts_line.id] = sheet_obj.name_get(cursor, user, sheet_ids, context=context)[0] return res - def _sheet_search(self, cursor, user, obj, name, args, context=None): - if not len(args): - return [] - sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') + def _get_hr_timesheet_sheet(self, cr, uid, ids, context=None): + ts_line_ids = [] + for ts in self.browse(cr, uid, ids, context=context): + cr.execute(""" + SELECT l.id + FROM hr_analytic_timesheet l + INNER JOIN account_analytic_line al + ON (l.line_id = al.id) + WHERE %(date_to)s >= al.date + AND %(date_from)s <= al.date + AND %(user_id)s = al.user_id + GROUP BY l.id""", {'date_from': ts.date_from, + 'date_to': ts.date_to, + 'user_id': ts.employee_id.user_id.id,}) + ts_line_ids.extend([row[0] for row in cr.fetchall()]) + return ts_line_ids - i = 0 - while i < len(args): - fargs = args[i][0].split('.', 1) - if len(fargs) > 1: - args[i] = (fargs[0], 'in', sheet_obj.search(cursor, user, - [(fargs[1], args[i][1], args[i][2])], context=context)) - i += 1 - continue - if isinstance(args[i][2], basestring): - res_ids = sheet_obj.name_search(cursor, user, args[i][2], [], - args[i][1]) - args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) - i += 1 - qu1, qu2 = [], [] - for x in args: - if x[1] != 'in': - if (x[2] is False) and (x[1] == '='): - qu1.append('(s.id IS NULL)') - elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): - qu1.append('(s.id IS NOT NULL)') - else: - qu1.append('(s.id %s %s)' % (x[1], '%s')) - qu2.append(x[2]) - elif x[1] == 'in': - if len(x[2]) > 0: - qu1.append('(s.id in (%s))' % (','.join(['%d'] * len(x[2])))) - qu2 += x[2] - else: - qu1.append('(False)') - if len(qu1): - qu1 = ' WHERE ' + ' AND '.join(qu1) - else: - qu1 = '' - cursor.execute('SELECT l.id \ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN (hr_analytic_timesheet l \ - LEFT JOIN account_analytic_line al \ - ON (l.line_id = al.id)) \ - ON (s.date_to >= al.date \ - AND s.date_from <= al.date \ - AND s.user_id = al.user_id)' + \ - qu1, qu2) - res = cursor.fetchall() - if not len(res): - return [('id', '=', '0')] - return [('id', 'in', [x[0] for x in res])] + def _get_account_analytic_line(self, cr, uid, ids, context=None): + ts_line_ids = self.pool.get('hr.analytic.timesheet').search(cr, uid, [('line_id', 'in', ids)]) + return ts_line_ids _columns = { 'sheet_id': fields.function(_sheet, string='Sheet', type='many2one', relation='hr_timesheet_sheet.sheet', - fnct_search=_sheet_search), + store={ + 'hr_timesheet_sheet.sheet': (_get_hr_timesheet_sheet, ['employee_id', 'date_from', 'date_to'], 10), + 'account.analytic.line': (_get_account_analytic_line, ['user_id', 'date'], 10), + 'hr.analytic.timesheet': (lambda self,cr,uid,ids,c={}: ids, ['line_id'], 10), + }, + ), } _defaults = { 'date': _get_default_date, @@ -517,90 +571,47 @@ class hr_attendance(osv.osv): return context['name'] + time.strftime(' %H:%M:%S') return time.strftime('%Y-%m-%d %H:%M:%S') + def _get_hr_timesheet_sheet(self, cr, uid, ids, context=None): + attendance_ids = [] + for ts in self.browse(cr, uid, ids, context=context): + cr.execute(""" + SELECT a.id + FROM hr_attendance a + INNER JOIN hr_employee e + INNER JOIN resource_resource r + ON (e.resource_id = r.id) + ON (a.employee_id = e.id) + WHERE %(date_to)s >= date_trunc('day', a.name) + AND %(date_from)s <= a.name + AND %(user_id)s = r.user_id + GROUP BY a.id""", {'date_from': ts.date_from, + 'date_to': ts.date_to, + 'user_id': ts.employee_id.user_id.id,}) + attendance_ids.extend([row[0] for row in cr.fetchall()]) + return attendance_ids + def _sheet(self, cursor, user, ids, name, args, context=None): sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - cursor.execute("SELECT a.id, COALESCE(MAX(s.id), 0) \ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN (hr_attendance a \ - LEFT JOIN hr_employee e \ - LEFT JOIN resource_resource r \ - ON (e.resource_id = r.id) \ - ON (a.employee_id = e.id)) \ - ON (s.date_to >= date_trunc('day',a.name) \ - AND s.date_from <= a.name \ - AND s.user_id = r.user_id) \ - WHERE a.id IN %s GROUP BY a.id",(tuple(ids),)) - res = dict(cursor.fetchall()) - sheet_names = {} - for sheet_id, name in sheet_obj.name_get(cursor, user, res.values(), - context=context): - sheet_names[sheet_id] = name - for line_id in {}.fromkeys(ids): - sheet_id = res.get(line_id, False) - if sheet_id: - res[line_id] = (sheet_id, sheet_names[sheet_id]) - else: - res[line_id] = False + res = {}.fromkeys(ids, False) + for attendance in self.browse(cursor, user, ids, context=context): + date_to = datetime.strftime(datetime.strptime(attendance.name[0:10], '%Y-%m-%d'), '%Y-%m-%d %H:%M:%S') + sheet_ids = sheet_obj.search(cursor, user, + [('date_to', '>=', date_to), + ('date_from', '<=', attendance.name), + ('employee_id', '=', attendance.employee_id.id)], context=context) + if sheet_ids: + # [0] because only one sheet possible for an employee between 2 dates + res[attendance.id] = sheet_obj.name_get(cursor, user, sheet_ids, context=context)[0] return res - def _sheet_search(self, cursor, user, obj, name, args, context=None): - if not len(args): - return [] - - sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - i = 0 - while i < len(args): - fargs = args[i][0].split('.', 1) - if len(fargs) > 1: - args[i] = (fargs[0], 'in', sheet_obj.search(cursor, user, - [(fargs[1], args[i][1], args[i][2])], context=context)) - i += 1 - continue - if isinstance(args[i][2], basestring): - res_ids = sheet_obj.name_search(cursor, user, args[i][2], [], - args[i][1]) - args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) - i += 1 - qu1, qu2 = [], [] - for x in args: - if x[1] != 'in': - if (x[2] is False) and (x[1] == '='): - qu1.append('(s.id IS NULL)') - elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): - qu1.append('(s.id IS NOT NULL)') - else: - qu1.append('(s.id %s %s)' % (x[1], '%s')) - qu2.append(x[2]) - elif x[1] == 'in': - if len(x[2]) > 0: - qu1.append('(s.id in (%s))' % (','.join(['%d'] * len(x[2])))) - qu2 += x[2] - else: - qu1.append('(False)') - if len(qu1): - qu1 = ' WHERE ' + ' AND '.join(qu1) - else: - qu1 = '' - cursor.execute('SELECT a.id\ - FROM hr_timesheet_sheet_sheet s \ - LEFT JOIN (hr_attendance a \ - LEFT JOIN hr_employee e \ - ON (a.employee_id = e.id)) \ - LEFT JOIN resource_resource r \ - ON (e.resource_id = r.id) \ - ON (s.date_to >= date_trunc(\'day\',a.name) \ - AND s.date_from <= a.name \ - AND s.user_id = r.user_id) ' + \ - qu1, qu2) - res = cursor.fetchall() - if not len(res): - return [('id', '=', '0')] - return [('id', 'in', [x[0] for x in res])] - _columns = { 'sheet_id': fields.function(_sheet, string='Sheet', type='many2one', relation='hr_timesheet_sheet.sheet', - fnct_search=_sheet_search), + store={ + 'hr_timesheet_sheet.sheet': (_get_hr_timesheet_sheet, ['employee_id', 'date_from', 'date_to'], 10), + 'hr.attendance': (lambda self,cr,uid,ids,c={}: ids, ['employee_id', 'name', 'day'], 10), + }, + ) } _defaults = { 'name': _get_default_date, diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index 87f212112c5..d76839ff8b5 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -1,7 +1,7 @@ - + hr.timesheet.sheet.graph hr_timesheet_sheet.sheet @@ -228,6 +228,19 @@ + + + hr.analytic.timesheet.search + hr.analytic.timesheet + form + + + + + + + + @@ -279,14 +292,21 @@ + + + + + hr.timesheet.day.tree + hr_timesheet_sheet.sheet.day + tree + + + + + + + + + + Date: Thu, 13 Oct 2011 14:27:04 +0530 Subject: [PATCH 02/33] [Fix] Account :In menu Accounting/Configuration/Analytic Accounting/Analytic Accounts change the icone of state and Parent button and Parent to Parent Account lp bug: https://launchpad.net/bugs/873184 fixed bzr revid: aag@tinyerp.com-20111013085704-xa6dj0v9c39too2r --- addons/account/project/project_view.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml index ec9ded68a12..b185c8ab10a 100644 --- a/addons/account/project/project_view.xml +++ b/addons/account/project/project_view.xml @@ -44,8 +44,8 @@ - - + + From c27bc7faf1c9f8aec26eeb6901a4438d0e302bd7 Mon Sep 17 00:00:00 2001 From: "Bharat (OpenERP)" Date: Fri, 14 Oct 2011 11:10:47 +0530 Subject: [PATCH 03/33] [FIX] hr : Added category_id field in tree viewof hr.holidays in hr_holidays/hr_holidays_view.xml lp bug: https://launchpad.net/bugs/872245 fixed bzr revid: bde@tinyerp.com-20111014054047-gp46a6fqbpadu0es --- addons/hr_holidays/hr_holidays_view.xml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index b6d6a64ed26..0b3d5ab58c7 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -203,6 +203,7 @@ + @@ -427,9 +428,9 @@ - + - + hr.employee.leave.tree hr.employee @@ -440,7 +441,7 @@ - + Allocate Leaves for Employees @@ -458,7 +459,7 @@ - + hr.employee.leave.form.inherit hr.employee From 5d53d94b4220f14387a2769bae9054f93b9a867f Mon Sep 17 00:00:00 2001 From: "Bharat (OpenERP)" Date: Fri, 14 Oct 2011 14:45:19 +0530 Subject: [PATCH 04/33] [FIX] point_sale : In point of sale, put money in operation is not working lp bug: https://launchpad.net/bugs/871684 fixed bzr revid: bde@tinyerp.com-20111014091519-3i2g7pwzy6j0yx3a --- addons/point_of_sale/wizard/pos_box_entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/point_of_sale/wizard/pos_box_entries.py b/addons/point_of_sale/wizard/pos_box_entries.py index 0b417d8b1cf..0c48aff736f 100644 --- a/addons/point_of_sale/wizard/pos_box_entries.py +++ b/addons/point_of_sale/wizard/pos_box_entries.py @@ -96,11 +96,11 @@ class pos_box_entries(osv.osv_memory): for data in self.read(cr, uid, ids, context=context): vals = {} curr_company = res_obj.browse(cr, uid, uid, context=context).company_id.id - statement_id = statement_obj.search(cr, uid, [('journal_id', '=', data['journal_id']), ('company_id', '=', curr_company), ('user_id', '=', uid), ('state', '=', 'open')], context=context) + statement_id = statement_obj.search(cr, uid, [('journal_id', '=', int(data['journal_id'])), ('company_id', '=', curr_company), ('user_id', '=', uid), ('state', '=', 'open')], context=context) if not statement_id: raise osv.except_osv(_('Error !'), _('You have to open at least one cashbox')) - acc_id = product_obj.browse(cr, uid, data['product_id']).property_account_income + acc_id = product_obj.browse(cr, uid, int(data['product_id'])).property_account_income if not acc_id: raise osv.except_osv(_('Error !'), _('Please check that income account is set to %s')%(product_obj.browse(cr, uid, data['product_id']).name)) if statement_id: From 69a0c93e0ab9a17ac862c196c5ddf2cbc6cdc54d Mon Sep 17 00:00:00 2001 From: Serpent Consulting Services Date: Thu, 27 Oct 2011 04:58:39 +0530 Subject: [PATCH 05/33] [FIX] Survey : Fixed a bunch of errors altogether lp bug: https://launchpad.net/bugs/882288 fixed bzr revid: support@serpentcs.com-20111026232839-3h5ux90w0qa1s1e3 --- addons/survey/test/survey00.yml | 2 +- addons/survey/wizard/survey_answer.py | 31 ++++++++++-------------- addons/survey/wizard/survey_selection.py | 2 +- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/addons/survey/test/survey00.yml b/addons/survey/test/survey00.yml index fa56e9fac39..ab34f62f65e 100644 --- a/addons/survey/test/survey00.yml +++ b/addons/survey/test/survey00.yml @@ -147,7 +147,7 @@ id = self.create(cr, uid, {'survey_id': ref("survey_partner_0")}) self.action_next(cr, uid, [id], context) - - Give answer of the first and second page in "Partner Feedback" suvey. + Give answer of the first and second page in "Partner Feedback" survey. - !python {model: survey.question.wiz}: | ids = self.create(cr, uid, {str(ref("survey_p_question_0")) +"_single" :'Tiny' , str(ref("survey_p_question_1")) + "_selection" :int(ref("survey_p_1_1"))}, context) diff --git a/addons/survey/wizard/survey_answer.py b/addons/survey/wizard/survey_answer.py index 332b74cc660..f913f922c55 100644 --- a/addons/survey/wizard/survey_answer.py +++ b/addons/survey/wizard/survey_answer.py @@ -42,7 +42,8 @@ class survey_question_wiz(osv.osv_memory): """ Fields View Get method :- generate the new view and display the survey pages of selected survey. """ - + if context is None: + context = {} result = super(survey_question_wiz, self).fields_view_get(cr, uid, view_id, \ view_type, context, toolbar,submenu) @@ -55,8 +56,7 @@ class survey_question_wiz(osv.osv_memory): que_col_head = self.pool.get('survey.question.column.heading') user_obj = self.pool.get('res.users') mail_message = self.pool.get('mail.message') - if context is None: - context = {} + if view_type in ['form']: wiz_id = 0 sur_name_rec = None @@ -407,21 +407,13 @@ class survey_question_wiz(osv.osv_memory): attachments[survey_data.title + ".pdf"] = file_data file.close() os.remove(addons.get_module_resource('survey', 'report') + survey_data.title + ".pdf") - user_email = False - resp_email = False + + user_email = user_obj.browse(cr, uid, uid, context).user_email + resp_email = survey_data.responsible_id and survey_data.responsible_id.user_email or False - address_id = user_obj.browse(cr, uid, uid).address_id.id - if address_id: - cr.execute("select email from res_partner_address where id =%s", (address_id,)) - user_email = cr.fetchone()[0] - resp_id = survey_data.responsible_id.address_id - - if resp_id: - cr.execute("select email from res_partner_address where id =%s", (resp_id.id,)) - resp_email = cr.fetchone()[0] if user_email and resp_email: user_name = user_obj.browse(cr, uid, uid, context=context).name - mail = "Hello " + survey_data.responsible_id.name + ",\n\n " + str(user_name) + " Give Response Of " + survey_data.title + " Survey.\n\n Thanks," + mail = "Hello " + survey_data.responsible_id.name + ",\n\n " + str(user_name) + " has given the Response Of " + survey_data.title + " Survey.\nThe Response has been attached herewith.\n\n Thanks." mail_message.schedule_with_attach(cr, uid, user_email, [resp_email], "Survey Answer Of " + str(user_name) , mail, attachments=attachments, context=context) xml_form = etree.Element('form', {'string': _('Complete Survey Answer')}) @@ -520,9 +512,10 @@ class survey_question_wiz(osv.osv_memory): return value if context.has_key('active') and context.get('active',False): return value - + sur_name_read = surv_name_wiz.read(cr, uid, context.get('sur_name_id',False)) ans_list = [] + for key,val in safe_eval(sur_name_read.get('store_ans',"{}")).items(): for field in fields_list: if field in list(val): @@ -535,8 +528,10 @@ class survey_question_wiz(osv.osv_memory): Create the Answer of survey and store in survey.response object, and if set validation of question then check the value of question if value is wrong then raise the exception. """ if context is None: context = {} + + survey_question_wiz_id = super(survey_question_wiz,self).create(cr, uid, vals, context=context) if context.has_key('active') and context.get('active',False): - return True + return survey_question_wiz_id for key,val in vals.items(): if key.split('_')[0] == "progress": @@ -982,7 +977,7 @@ class survey_question_wiz(osv.osv_memory): if que_rec['type'] in ['multiple_choice_only_one_ans','single_textbox','comment'] and que_rec['is_require_answer'] and select_count <= 0: raise osv.except_osv(_('Warning !'), "'" + que_rec['question'] + "' " + tools.ustr(que_rec['req_error_msg'])) - return True + return survey_question_wiz_id def action_new_question(self,cr, uid, ids, context=None): """ diff --git a/addons/survey/wizard/survey_selection.py b/addons/survey/wizard/survey_selection.py index a803459899c..82c9fd385ed 100644 --- a/addons/survey/wizard/survey_selection.py +++ b/addons/survey/wizard/survey_selection.py @@ -41,6 +41,7 @@ class survey_name_wiz(osv.osv_memory): 'transfer': 1, 'response': 0, 'survey_id': lambda self,cr,uid,context:context.get('survey_id',False), + 'store_ans': '{}' #Setting the default pattern as '{}' as the field is of type text. The field always gets the value in dict format } def action_next(self, cr, uid, ids, context=None): @@ -68,7 +69,6 @@ class survey_name_wiz(osv.osv_memory): raise osv.except_osv(_('Warning !'),_("You can not give more response. Please contact the author of this survey for further assistance.")) search_id = search_obj.search(cr,uid,[('model','=','survey.question.wiz'),('name','=','Survey Search')]) - return { 'view_type': 'form', "view_mode": 'form', From 66863453bc9b6f22437c236a2e51dec09b3ed474 Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Tue, 1 Nov 2011 14:05:34 +0530 Subject: [PATCH 06/33] [FIX] membership : Cannot subscribe to membership lp bug: https://launchpad.net/bugs/883052 fixed bzr revid: mdi@tinyerp.com-20111101083534-2jjy05atfaqe5n6c --- addons/membership/wizard/membership_invoice_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/membership/wizard/membership_invoice_view.xml b/addons/membership/wizard/membership_invoice_view.xml index 500885d35f1..dedecf1e9dc 100644 --- a/addons/membership/wizard/membership_invoice_view.xml +++ b/addons/membership/wizard/membership_invoice_view.xml @@ -10,7 +10,7 @@
- + From d07f77f62b51b60ae92520513800be6dc93a83f3 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Wed, 9 Nov 2011 11:43:12 +0100 Subject: [PATCH 16/33] [imp] small improvement to ease widgetification of the pos bzr revid: nicolas.vanhoren@openerp.com-20111109104312-tn9vyykt27hyjpej --- addons/web/static/src/js/core.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 7b402cae7fd..b7edc472b88 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -792,6 +792,11 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W * @type string */ identifier_prefix: 'generic-identifier-', + /** + * Tag name when creating a default $element. + * @type string + */ + tag_name: 'div', /** * Construct the widget and set its parent if a parent is given. * @@ -814,7 +819,7 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W this.element_id = element_id; this.element_id = this.element_id || _.uniqueId(this.identifier_prefix); var tmp = document.getElementById(this.element_id); - this.$element = tmp ? $(tmp) : undefined; + this.$element = tmp ? $(tmp) : $(document.createElement(this.tag_name)); this.widget_parent = parent; this.widget_children = []; From 13e361920f0ef7eebafdfb4a973fab03a0f54ad6 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Wed, 9 Nov 2011 12:02:16 +0100 Subject: [PATCH 17/33] [imp] some more architecture improvements bzr revid: nicolas.vanhoren@openerp.com-20111109110216-p7e2xnckhbs8ojqk --- addons/web/static/src/js/core.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index b7edc472b88..96e45c7cc92 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -874,8 +874,7 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W }, target); }, _render_and_insert: function(insertion, target) { - var rendered = this.render(); - this.$element = $(rendered); + this.render_element(); if (target instanceof openerp.web.Widget) target = target.$element; insertion(target); @@ -883,6 +882,14 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W return this.start(); }, on_inserted: function(element, widget) {}, + /** + * Renders the element and insert the result of the render() method in this.$element. + */ + render_element: function() { + var rendered = this.render(); + this.$element = $(rendered); + return this; + }, /** * Renders the widget using QWeb, `this.template` must be defined. * The context given to QWeb contains the "widget" key that references `this`. @@ -890,7 +897,9 @@ openerp.web.Widget = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.W * @param {Object} additional Additional context arguments to pass to the template. */ render: function (additional) { - return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {})); + if (this.template) + return openerp.web.qweb.render(this.template, _.extend({widget: this}, additional || {})); + return $("
").append(document.createElement(this.tagName)).html(); }, /** * Method called after rendering. Mostly used to bind actions, perform asynchronous From 8b6a201278affbad9616449f3bdd605c26540694 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 9 Nov 2011 12:04:57 +0100 Subject: [PATCH 18/33] [IMP] display logs in reverse chronological order bzr revid: xmo@openerp.com-20111109110457-n6ijg7agc6mdoq28 --- addons/web/static/src/js/views.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index b23c00f2c6d..b6ff65bf8f3 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -519,7 +519,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner do_display_log: function (log_records) { var self = this, $logs = this.$element.find('ul.oe-view-manager-logs:first').empty(); - _(log_records).each(function (record) { + _(log_records.reverse()).each(function (record) { $(_.sprintf('
  • %s
  • ', record.name)) .appendTo($logs) .delegate('a', 'click', function (e) { From 3542733e18f982b9c913047a3a7e3354b32f18d0 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Wed, 9 Nov 2011 12:07:04 +0100 Subject: [PATCH 19/33] [FIX] account: fields_view_get of account.invoice should not filter the journals if nothing is passed in context lp bug: https://launchpad.net/bugs/871960 fixed bzr revid: qdp-launchpad@openerp.com-20111109110704-yla19u2o2r2ctico --- addons/account/account_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 8f873065014..cce7d22d673 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -306,9 +306,9 @@ class account_invoice(osv.osv): view_id = view_id[0] res = super(account_invoice,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) - type = context.get('journal_type', 'sale') + type = context.get('journal_type', False) for field in res['fields']: - if field == 'journal_id': + if field == 'journal_id' and type: journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', type)], context=context, limit=None, name_get_uid=1) res['fields'][field]['selection'] = journal_select From 1104277a61f528cd2af133b821d274076ed69016 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 9 Nov 2011 12:31:31 +0100 Subject: [PATCH 20/33] [FIX] membership: wrong field name in onchange method bzr revid: rco@openerp.com-20111109113131-pf78lcbphr5igd7l --- addons/membership/wizard/membership_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/membership/wizard/membership_invoice.py b/addons/membership/wizard/membership_invoice.py index b97926e564c..d0be223158f 100644 --- a/addons/membership/wizard/membership_invoice.py +++ b/addons/membership/wizard/membership_invoice.py @@ -35,7 +35,7 @@ class membership_invoice(osv.osv_memory): """This function returns value of product's member price based on product id. """ if not product_id: - return {'value': {'unit_price': False}} + return {'value': {'member_price': False}} return {'value': {'member_price': self.pool.get('product.product').price_get(cr, uid, [product_id])[product_id]}} def membership_invoice(self, cr, uid, ids, context=None): @@ -67,4 +67,4 @@ class membership_invoice(osv.osv_memory): membership_invoice() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 493f313c674e9fbc5f4f98e8544174193c3796e7 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 9 Nov 2011 12:35:35 +0100 Subject: [PATCH 21/33] [IMP] only display last 3 log items, add link to display everything bzr revid: xmo@openerp.com-20111109113535-mwht7h1xkw4jdqfp --- addons/web/static/src/css/base.css | 21 ++++++++++++++++++--- addons/web/static/src/js/views.js | 14 ++++++++++++-- addons/web/static/src/xml/base.xml | 5 ++++- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 94e36d60419..5a629acbb74 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1080,11 +1080,14 @@ label.error { } .openerp .oe-view-manager-logs { clear: both; - font-size: 85%; - margin: 0.25em 0; background: #fff; - padding: 0 10px; + margin: 0.25em 0; + font-size: 85%; color: #4C4C4C; +} +.openerp .oe-view-manager-logs ul { + margin: 0; + padding: 0 10px; list-style: none; } .openerp .oe-view-manager-logs li:before { @@ -1094,6 +1097,18 @@ label.error { text-decoration: none; color: inherit; } +/* only display first three log items of a folded logs list */ +.openerp .oe-view-manager-logs.oe-folded li:nth-child(n+4) { + display: none; +} +/* display link to more logs if there are more logs to view and the logview is + currently folded */ +.openerp .oe-view-manager-logs a.oe-more-logs { + display: none; +} +.openerp .oe-view-manager-logs.oe-folded.oe-has-more a.oe-more-logs { + display: block; +} .openerp .view-manager-main-sidebar { width: 180px; diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index b6ff65bf8f3..de411838af8 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -441,6 +441,12 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } } + var log_view = this.$element.find('.oe-view-manager-logs:first'); + log_view.delegate('a.oe-more-logs', 'click', function () { + log_view.removeClass('oe-folded'); + return false; + }); + return manager_ready; }, on_mode_switch: function (view_type) { @@ -518,10 +524,14 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner */ do_display_log: function (log_records) { var self = this, - $logs = this.$element.find('ul.oe-view-manager-logs:first').empty(); + cutoff = 3, + $logs = this.$element.find('.oe-view-manager-logs:first') + .addClass('oe-folded'), + $logs_list = $logs.find('ul').empty(); + $logs.toggleClass('oe-has-more', log_records.length > cutoff); _(log_records.reverse()).each(function (record) { $(_.sprintf('
  • %s
  • ', record.name)) - .appendTo($logs) + .appendTo($logs_list) .delegate('a', 'click', function (e) { self.do_action({ type: 'ir.actions.act_window', diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 1fc98ba34ee..507db6d829b 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -457,7 +457,10 @@ -
      +
      +
        + More… +
        From de0582a26b8b1a3b7c23a16319c8759f07305b9b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 9 Nov 2011 12:50:09 +0100 Subject: [PATCH 22/33] [ADD] ability to close res_log listing bzr revid: xmo@openerp.com-20111109115009-bce3wb65tawhbn9z --- addons/web/static/src/css/base.css | 8 ++++++++ addons/web/static/src/js/views.js | 10 +++++++--- addons/web/static/src/xml/base.xml | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 5a629acbb74..aa4669afd77 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1084,6 +1084,8 @@ label.error { margin: 0.25em 0; font-size: 85%; color: #4C4C4C; + position: relative; + overflow: hidden; } .openerp .oe-view-manager-logs ul { margin: 0; @@ -1109,6 +1111,12 @@ label.error { .openerp .oe-view-manager-logs.oe-folded.oe-has-more a.oe-more-logs { display: block; } +.openerp .oe-view-manager-logs a.oe-remove-everything { + position: absolute; + top: 0; + right: 0; + cursor: pointer; +} .openerp .view-manager-main-sidebar { width: 180px; diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index de411838af8..9cfbf84b9ec 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -441,9 +441,13 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } } - var log_view = this.$element.find('.oe-view-manager-logs:first'); - log_view.delegate('a.oe-more-logs', 'click', function () { - log_view.removeClass('oe-folded'); + var $res_logs = this.$element.find('.oe-view-manager-logs:first'); + $res_logs.delegate('a.oe-more-logs', 'click', function () { + $res_logs.removeClass('oe-folded'); + return false; + }).delegate('a.oe-remove-everything', 'click', function () { + $res_logs.removeClass('oe-has-more') + .find('ul').empty(); return false; }); diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 507db6d829b..0501f784644 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -460,6 +460,7 @@ From f975258f0f88eec867827c318bb27ed5ec50e8e8 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 9 Nov 2011 12:57:44 +0100 Subject: [PATCH 23/33] [ADD] Added jquery.scrollTo bzr revid: fme@openerp.com-20111109115744-m20vq3szt3hak3lf --- addons/web/__openerp__.py | 1 + .../static/lib/jquery.scrollTo/changes.txt | 91 ++++++++ .../jquery.scrollTo/jquery.scrollTo-min.js | 11 + .../lib/jquery.scrollTo/jquery.scrollTo.js | 215 ++++++++++++++++++ 4 files changed, 318 insertions(+) create mode 100644 addons/web/static/lib/jquery.scrollTo/changes.txt create mode 100644 addons/web/static/lib/jquery.scrollTo/jquery.scrollTo-min.js create mode 100644 addons/web/static/lib/jquery.scrollTo/jquery.scrollTo.js diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index a66bd4687a7..6921b36f91d 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -23,6 +23,7 @@ "static/lib/jquery.ui/js/jquery-ui-timepicker-addon.js", "static/lib/jquery.ui.notify/js/jquery.notify.js", "static/lib/jquery.deferred-queue/jquery.deferred-queue.js", + "static/lib/jquery.scrollTo/jquery.scrollTo-min.js", "static/lib/json/json2.js", "static/lib/qweb/qweb2.js", "static/lib/underscore/underscore.js", diff --git a/addons/web/static/lib/jquery.scrollTo/changes.txt b/addons/web/static/lib/jquery.scrollTo/changes.txt new file mode 100644 index 00000000000..4b2933f6667 --- /dev/null +++ b/addons/web/static/lib/jquery.scrollTo/changes.txt @@ -0,0 +1,91 @@ +1.4.2 +[Feature] +- The plugin support percentages as target ('50%' or {top:'50%', left:'45%'}) +- Exposed the max() calculation as $.scrollTo.max +[Enhancement] +- Renamed $.fn.scrollable to $.fn._scrollable to avoid conflicts with other plugins +[Fix] +- Fixing max calculations for regular DOM elements + +1.4.1 +[Feature] +- The target can be 'max' to scroll to the end while keeping it elegant. +[Enhancement] +- Default duration is 0 for jquery +1.3. Means sync animation +- The plugin works on all major browsers, on compat & quirks modes, including iframes. +- In addition to window/document, if html or body are received, the plugin will choose the right one. +[Fix] +- The plugin accepts floating numbers, Thanks Ramin +- Using jQuery.nodeName where neccessary so that this works on xml+xhtml +- The max() internal function wasn't completely accurrate, now it is 98% (except for IE on quirks mode and it's not too noticeable). + +1.4 +[Fix] +- Fixed the problem when scrolling the window to absolute positioned elements on Safari. +- Fixed the problem on Opera 9.5 when scrolling the window. That it always scrolls to 0. +[Feature] +- Added the settings object as 2nd argument to the onAfter callback. +- The 3rd argument of scrollTo can be just a function and it's used as the onAfter. +- Added full support for iframes (even max scroll calculation). +- Instead of $.scrollTo, $(window).scrollTo() and $(document).scrollTo() can be used. +- Added $().scrollable() that returns the real element to scroll, f.e: $(window).scrollable() == [body|html], works for iframes. +[Enhancement] +- Cleaned the code a bit, specially the comments + +1.3.3 +[Change] +- Changed the licensing from GPL to GPL+MIT. + +1.3.2 +[Enhancement] +- Small improvements to make the code shorter. +[Change] +- Removed the last argument received by onAfter as it was the same as the 'this' but jqueryfied. + +1.3.1 +[Feature] +- Exposed $.scrollTo.window() to get the element that needs to be animated, to scroll the window. +- Added option 'over'. +[Enhancement] +- Made the code as short as possible. +[Change] +- Changed the arguments received by onAfter + +1.3 +[Enhancement] +- Added semicolon to the start, for safe file concatenation +- Added a limit check, values below 0 or over the maximum are fixed. +- Now it should work faster, only one of html or body go through all the processing, instead of both for all browsers. +[Fix] +- Fixed the behavior for Opera, which seemed to react to both changes on and . +- The border is also reduced, when 'margin' is set to true. +[Change] +- The option speed has been renamed to duration. +[Feature] +- The duration can be specified with a number as 2nd argument, and the rest of the settings as the third ( like $().animate ) +- Remade the demo + +1.2.4 +[Enhancement] +- The target can be in the form of { top:x, left:y } allowing different position for each axis. +[Feature] +- The option 'offset' has been added, to scroll behind or past the target. Can be a number(both axes) or { top:x, left:y }. + +1.2.3 +[Feature] +- Exposed the defaults. +[Enhancement] +- Made the callback functions receive more parameters. + +1.2.2 +[Fix] +- Fixed a bug, I didn't have to add the scrolled amount if it was body or html. + +1.2 +[Change] +- The option 'onafter' is now called 'onAfter'. +[Feature] +- Two axes can be scrolled together, this is set with the option 'axis'. +- In case 2 axes are chosen, the scrolling can be queued: one scrolls, and then the other. +- There's an intermediary event, 'onAfterFirst' called in case the axes are queued, after the first ends. +- If the option 'margin' is set to true, the plugin will take in account, the margin of the target(no use if target is a value). \ No newline at end of file diff --git a/addons/web/static/lib/jquery.scrollTo/jquery.scrollTo-min.js b/addons/web/static/lib/jquery.scrollTo/jquery.scrollTo-min.js new file mode 100644 index 00000000000..73a334184e4 --- /dev/null +++ b/addons/web/static/lib/jquery.scrollTo/jquery.scrollTo-min.js @@ -0,0 +1,11 @@ +/** + * jQuery.ScrollTo - Easy element scrolling using jQuery. + * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 5/25/2009 + * @author Ariel Flesler + * @version 1.4.2 + * + * http://flesler.blogspot.com/2007/10/jqueryscrollto.html + */ +;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery); \ No newline at end of file diff --git a/addons/web/static/lib/jquery.scrollTo/jquery.scrollTo.js b/addons/web/static/lib/jquery.scrollTo/jquery.scrollTo.js new file mode 100644 index 00000000000..eec31e191cb --- /dev/null +++ b/addons/web/static/lib/jquery.scrollTo/jquery.scrollTo.js @@ -0,0 +1,215 @@ +/** + * jQuery.ScrollTo + * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 5/25/2009 + * + * @projectDescription Easy element scrolling using jQuery. + * http://flesler.blogspot.com/2007/10/jqueryscrollto.html + * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP. + * + * @author Ariel Flesler + * @version 1.4.2 + * + * @id jQuery.scrollTo + * @id jQuery.fn.scrollTo + * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements. + * The different options for target are: + * - A number position (will be applied to all axes). + * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes + * - A jQuery/DOM element ( logically, child of the element to scroll ) + * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc ) + * - A hash { top:x, left:y }, x and y can be any kind of number/string like above. +* - A percentage of the container's dimension/s, for example: 50% to go to the middle. + * - The string 'max' for go-to-end. + * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead. + * @param {Object,Function} settings Optional set of settings or the onAfter callback. + * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'. + * @option {Number} duration The OVERALL length of the animation. + * @option {String} easing The easing method for the animation. + * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position. + * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }. + * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes. + * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends. + * @option {Function} onAfter Function to be called after the scrolling ends. + * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends. + * @return {jQuery} Returns the same jQuery object, for chaining. + * + * @desc Scroll to a fixed position + * @example $('div').scrollTo( 340 ); + * + * @desc Scroll relatively to the actual position + * @example $('div').scrollTo( '+=340px', { axis:'y' } ); + * + * @dec Scroll using a selector (relative to the scrolled element) + * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } ); + * + * @ Scroll to a DOM element (same for jQuery object) + * @example var second_child = document.getElementById('container').firstChild.nextSibling; + * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){ + * alert('scrolled!!'); + * }}); + * + * @desc Scroll on both axes, to different values + * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } ); + */ +;(function( $ ){ + + var $scrollTo = $.scrollTo = function( target, duration, settings ){ + $(window).scrollTo( target, duration, settings ); + }; + + $scrollTo.defaults = { + axis:'xy', + duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1 + }; + + // Returns the element that needs to be animated to scroll the window. + // Kept for backwards compatibility (specially for localScroll & serialScroll) + $scrollTo.window = function( scope ){ + return $(window)._scrollable(); + }; + + // Hack, hack, hack :) + // Returns the real elements to scroll (supports window/iframes, documents and regular nodes) + $.fn._scrollable = function(){ + return this.map(function(){ + var elem = this, + isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1; + + if( !isWin ) + return elem; + + var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem; + + return $.browser.safari || doc.compatMode == 'BackCompat' ? + doc.body : + doc.documentElement; + }); + }; + + $.fn.scrollTo = function( target, duration, settings ){ + if( typeof duration == 'object' ){ + settings = duration; + duration = 0; + } + if( typeof settings == 'function' ) + settings = { onAfter:settings }; + + if( target == 'max' ) + target = 9e9; + + settings = $.extend( {}, $scrollTo.defaults, settings ); + // Speed is still recognized for backwards compatibility + duration = duration || settings.speed || settings.duration; + // Make sure the settings are given right + settings.queue = settings.queue && settings.axis.length > 1; + + if( settings.queue ) + // Let's keep the overall duration + duration /= 2; + settings.offset = both( settings.offset ); + settings.over = both( settings.over ); + + return this._scrollable().each(function(){ + var elem = this, + $elem = $(elem), + targ = target, toff, attr = {}, + win = $elem.is('html,body'); + + switch( typeof targ ){ + // A number will pass the regex + case 'number': + case 'string': + if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){ + targ = both( targ ); + // We are done + break; + } + // Relative selector, no break! + targ = $(targ,this); + case 'object': + // DOMElement / jQuery + if( targ.is || targ.style ) + // Get the real position of the target + toff = (targ = $(targ)).offset(); + } + $.each( settings.axis.split(''), function( i, axis ){ + var Pos = axis == 'x' ? 'Left' : 'Top', + pos = Pos.toLowerCase(), + key = 'scroll' + Pos, + old = elem[key], + max = $scrollTo.max(elem, axis); + + if( toff ){// jQuery / DOMElement + attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] ); + + // If it's a dom element, reduce the margin + if( settings.margin ){ + attr[key] -= parseInt(targ.css('margin'+Pos)) || 0; + attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0; + } + + attr[key] += settings.offset[pos] || 0; + + if( settings.over[pos] ) + // Scroll to a fraction of its width/height + attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos]; + }else{ + var val = targ[pos]; + // Handle percentage values + attr[key] = val.slice && val.slice(-1) == '%' ? + parseFloat(val) / 100 * max + : val; + } + + // Number or 'number' + if( /^\d+$/.test(attr[key]) ) + // Check the limits + attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max ); + + // Queueing axes + if( !i && settings.queue ){ + // Don't waste time animating, if there's no need. + if( old != attr[key] ) + // Intermediate animation + animate( settings.onAfterFirst ); + // Don't animate this axis again in the next iteration. + delete attr[key]; + } + }); + + animate( settings.onAfter ); + + function animate( callback ){ + $elem.animate( attr, duration, settings.easing, callback && function(){ + callback.call(this, target, settings); + }); + }; + + }).end(); + }; + + // Max scrolling position, works on quirks mode + // It only fails (not too badly) on IE, quirks mode. + $scrollTo.max = function( elem, axis ){ + var Dim = axis == 'x' ? 'Width' : 'Height', + scroll = 'scroll'+Dim; + + if( !$(elem).is('html,body') ) + return elem[scroll] - $(elem)[Dim.toLowerCase()](); + + var size = 'client' + Dim, + html = elem.ownerDocument.documentElement, + body = elem.ownerDocument.body; + + return Math.max( html[scroll], body[scroll] ) + - Math.min( html[size] , body[size] ); + + }; + + function both( val ){ + return typeof val == 'object' ? val : { top:val, left:val }; + }; + +})( jQuery ); \ No newline at end of file From 200e143ef387e8e8ad104c3af80f69fefd25fcd1 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 9 Nov 2011 12:58:32 +0100 Subject: [PATCH 24/33] [REM] Removed TranslateDialog tabs. Focus field correctly and scroll overflow to the focused field bzr revid: fme@openerp.com-20111109115832-w711t2duob958j7v --- addons/web/static/src/js/views.js | 24 ++------------ addons/web/static/src/xml/base.xml | 51 +++++++++++------------------- 2 files changed, 22 insertions(+), 53 deletions(-) diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 4fc10aa23eb..faaa886648d 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -742,11 +742,6 @@ session.web.TranslateDialog = session.web.Dialog.extend({ this._super(); $.when(this.languages_loaded).then(function() { self.$element.html(session.web.qweb.render('TranslateDialog', { widget: self })); - self.$element.tabs(); - if (!(self.view.translatable_fields && self.view.translatable_fields.length)) { - self.hide_tabs('fields'); - self.select_tab('view'); - } self.$fields_form = self.$element.find('.oe_translation_form'); self.$fields_form.find('.oe_trad_field').change(function() { $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value'))); @@ -789,21 +784,6 @@ session.web.TranslateDialog = session.web.Dialog.extend({ }); $.when.apply(null, deffered).then(callback); }, - show_tabs: function() { - for (var i = 0; i < arguments.length; i++) { - this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().show(); - } - }, - hide_tabs: function() { - for (var i = 0; i < arguments.length; i++) { - this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().hide(); - } - }, - select_tab: function(name) { - this.show_tabs(name); - var index = this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().index() - 1; - this.$element.tabs('select', index); - }, open: function(field) { var self = this, sup = this._super; @@ -812,7 +792,9 @@ session.web.TranslateDialog = session.web.Dialog.extend({ self.do_load_fields_values(function() { sup.call(self); if (field) { - // TODO: focus and scroll to field + var $field_input = self.$element.find('tr[data-field="' + field.name + '"] td:nth-child(2) *:first-child'); + self.$element.scrollTo($field_input); + $field_input.focus(); } }); } else { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index e30e91a81d7..cffed128e33 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -482,38 +482,25 @@ - -
        - - - - - - - - - -
        -
        Field
        -
        -
        -
        - - - - -
        -
        -
        - Translate view -
        -
        - Translate sidebar -
        + + + + + + + + + +
        +
        Field
        +
        +
        +
        + + + + +