From b03e5f9034430dde015ad3511f6e4acff4b61936 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 22 Apr 2011 10:54:46 +0200 Subject: [PATCH 001/106] [IMP] Add a cron for the analytic accounts, this cron will send an email to the responsible of the pending accounts bzr revid: stephane@openerp.com-20110422085446-r744ot300i6qwksv --- addons/account_analytic_analysis/__init__.py | 1 + .../account_analytic_analysis/__openerp__.py | 1 + .../account_analytic_analysis.py | 20 ++++- .../account_analytic_analysis_cron.xml | 15 ++++ .../account_analytic_analysis_menu.xml | 58 ++++++++++++++ .../cron_account_analytic_account.py | 79 +++++++++++++++++++ 6 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 addons/account_analytic_analysis/account_analytic_analysis_cron.xml create mode 100644 addons/account_analytic_analysis/cron_account_analytic_account.py diff --git a/addons/account_analytic_analysis/__init__.py b/addons/account_analytic_analysis/__init__.py index a7bdb11ad67..429aa011f31 100644 --- a/addons/account_analytic_analysis/__init__.py +++ b/addons/account_analytic_analysis/__init__.py @@ -20,6 +20,7 @@ ############################################################################## import account_analytic_analysis +import cron_account_analytic_account # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account_analytic_analysis/__openerp__.py b/addons/account_analytic_analysis/__openerp__.py index 588055b3741..f5dea6e77e0 100644 --- a/addons/account_analytic_analysis/__openerp__.py +++ b/addons/account_analytic_analysis/__openerp__.py @@ -41,6 +41,7 @@ user-wise as well as month wise. "security/ir.model.access.csv", "account_analytic_analysis_view.xml", "account_analytic_analysis_menu.xml", + "account_analytic_analysis_cron.xml", ], 'demo_xml' : [], 'installable': True, diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 11f92614b73..03dea210396 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -374,7 +374,25 @@ class account_analytic_account(osv.osv): res[id] = round(res.get(id, 0.0),2) return res - _columns ={ + def _is_overdue_quantity(self, cr, uid, ids, fieldnames, args, context=None): + result = dict.fromkeys(ids, 0) + + for record in self.browse(cr, uid, ids, context=context): + result[record.id] = int(record.quantity >= record.quantity_max) + + return result + + def _get_analytic_account(self, cr, uid, ids, context=None): + result = set() + for line in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context): + result.add(line.account_id.id) + return list(result) + + _columns = { + 'is_overdue_quantity' : fields.function(_is_overdue_quantity, method=True, type='boolean', string='Overdue Quantity', + store={ + 'account.analytic.line' : (_get_analytic_account, None, 20), + }), 'ca_invoiced': fields.function(_ca_invoiced_calc, method=True, type='float', string='Invoiced Amount', help="Total customer invoiced amount for this account.", digits_compute=dp.get_precision('Account')), diff --git a/addons/account_analytic_analysis/account_analytic_analysis_cron.xml b/addons/account_analytic_analysis/account_analytic_analysis_cron.xml new file mode 100644 index 00000000000..7134d59414b --- /dev/null +++ b/addons/account_analytic_analysis/account_analytic_analysis_cron.xml @@ -0,0 +1,15 @@ + + + + + Analytic Account Report for Sales + 1 + weeks + -1 + + + + + + + diff --git a/addons/account_analytic_analysis/account_analytic_analysis_menu.xml b/addons/account_analytic_analysis/account_analytic_analysis_menu.xml index badd7e1816a..a564db17dfc 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_menu.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_menu.xml @@ -21,5 +21,63 @@ [('date','<=',time.strftime('%Y-%m-%d')),('state','=','open')] + + + account.analytic.account.search + account.analytic.account + search + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Overdue Accounts + account.analytic.account + form + tree,form,graph + {'search_default_has_partner' : 1, 'search_default_current' : 1} + + + + + diff --git a/addons/account_analytic_analysis/cron_account_analytic_account.py b/addons/account_analytic_analysis/cron_account_analytic_account.py new file mode 100644 index 00000000000..2bc8e0f98b1 --- /dev/null +++ b/addons/account_analytic_analysis/cron_account_analytic_account.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +from osv import osv +from mako.template import Template +import time +try: + import cStringIO as StringIO +except ImportError: + import StringIO + +import tools + +MAKO_TEMPLATE = u"""Hello ${user.name}, + +Here is a list of contracts that have to be renewed for two +possible reasons: + - the end of contract date is passed + - the customer consumed more support hours than expected + +Can you contact the customer in order to sell a new or renew its contract. +the contract has been set with a pending state, can you update the status +of the analytic account following this rule: + - Set Done: if the customer does not want to renew + - Set Open: if the customer purchased an extra contract + +Here is the list of contracts to renew: +% for partner, accounts in partners.iteritems(): + * ${partner.name} + % for account in accounts: + - Name: ${account.name} + % if account.quantity_max != 0.0: + - Quantity: ${account.quantity}/${account.quantity_max} hours + % endif + - Dates: ${account.date_start} to ${account.date and account.date or '???'} + - Contacts: + % for address in account.partner_id.address: + . ${address.name}, ${address.phone}, ${address.email} + % endfor + + % endfor +% endfor + +You can use the report in the menu: Sales > Invoicing > Overdue Accounts + +Regards, + +-- +OpenERP +""" + +class analytic_account(osv.osv): + _inherit = 'account.analytic.account' + + def cron_account_analytic_account(self, cr, uid, context=None): + domain = [ + ('name', 'not ilike', 'maintenance'), + ('partner_id', '!=', False), + ('user_id', '!=', False), + ('user_id.user_email', '!=', False), + ('state', 'in', ('draft', 'open')), + '|', ('date', '<', time.strftime('%Y-%m-%d')), ('date', '=', False), + ] + + account_ids = self.search(cr, uid, domain, context=context, order='name asc') + accounts = self.browse(cr, uid, account_ids, context=context) + + users = dict() + for account in accounts: + users.setdefault(account.user_id, dict()).setdefault(account.partner_id, []).append(account) + + account.write({'state' : 'pending'}, context=context) + + for user, data in users.iteritems(): + subject = '[OPENERP] Reporting: Analytic Accounts' + body = Template(MAKO_TEMPLATE).render_unicode(user=user, partners=data) + tools.email_send('noreply@openerp.com', [user.user_email, ], subject, body) + + return True + +analytic_account() From 8fee569a7b4ea90ee4946975fdb6f3ad3d130c8f Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 22 Apr 2011 13:37:06 +0200 Subject: [PATCH 002/106] [REF] Clean up bzr revid: stephane@openerp.com-20110422113706-kyeoyfb79uvc5899 --- addons/base_action_rule/base_action_rule.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 2a4e2c0e4e1..869c8ea1f03 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -325,7 +325,7 @@ the rule to mark CC(mail to any other person defined in actions)."), @param context: A standard dictionary for contextual values """ if context is None: context = {} - ok = True + ok = True if action.filter_id: if action.model_id.model == action.filter_id.model_id: context.update(eval(action.filter_id.context)) @@ -445,19 +445,13 @@ the rule to mark CC(mail to any other person defined in actions)."), context = {} context.update({'action': True}) - if not scrit: - scrit = [] for action in self.browse(cr, uid, ids, context=context): - model_obj = self.pool.get(action.model_id.model) for obj in objects: - ok = self.do_check(cr, uid, action, obj, context=context) - if not ok: - continue + if self.do_check(cr, uid, action, obj, context=context): + model_obj = self.pool.get(action.model_id.model) + self.do_action(cr, uid, action, model_obj, obj, context=context) - if ok: - self.do_action(cr, uid, action, model_obj, obj, context) - break context.update({'action': False}) return True @@ -480,7 +474,7 @@ the rule to mark CC(mail to any other person defined in actions)."), return True _constraints = [ - (_check_mail, 'Error: The mail is not well formated', ['act_mail_body']), + (_check_mail, 'Error: The mail is not well formatted', ['act_mail_body']), ] base_action_rule() From 63573d48d887a2906a54d9a08828984030acf8d0 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 22 Apr 2011 13:41:11 +0200 Subject: [PATCH 003/106] [FIX] Use the last description where the name is not a state of a CRM case bzr revid: stephane@openerp.com-20110422114111-pb1boou0lf3q9rlh --- addons/crm/crm.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index f25e4c1a0bf..227b19db73e 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -466,6 +466,11 @@ class crm_case(object): src = case_email dest = case.user_id body = case.description or "" + for message in case.message_ids: + if message.name not in AVAILABLE_STATES.values(): + body = message.description + break + if case.message_ids: body = case.message_ids[0].description or "" if not destination: @@ -486,7 +491,7 @@ class crm_case(object): attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send) # Send an email - subject = "Reminder: [%s] %s" % (str(case.id), case.name,) + subject = "[%s] %s" % (str(case.id), case.name,) tools.email_send( src, [dest], From 65f1b3bcfb68db284e5fb530ba6a8e32f23a4386 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 22 Apr 2011 13:41:43 +0200 Subject: [PATCH 004/106] [FIX] Apply the change and call the do_action callback bzr revid: stephane@openerp.com-20110422114143-vum7ebti5q3lutip --- addons/crm/crm_action_rule.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index 477f9132fab..62948a1d650 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -107,9 +107,7 @@ this if you want the rule to send an email to the partner."), @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @param context: A standard dictionary for contextual values """ - res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context) write = {} - if hasattr(action, 'act_section_id') and action.act_section_id: obj.section_id = action.act_section_id write['section_id'] = action.act_section_id.id @@ -127,6 +125,7 @@ this if you want the rule to send an email to the partner."), model_obj._history(cr, uid, [obj], _(action.act_state)) model_obj.write(cr, uid, [obj.id], write, context) + super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context) emails = [] if hasattr(obj, 'email_from') and action.act_mail_to_partner: From 1704c9f8822f77066ca78136f485bec995484716 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 22 Apr 2011 13:43:07 +0200 Subject: [PATCH 005/106] [REF] Use the set_reply_to function if this one exists in the object bzr revid: stephane@openerp.com-20110422114307-dyymgrgz0j4e1ace --- addons/crm/wizard/crm_send_email.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/addons/crm/wizard/crm_send_email.py b/addons/crm/wizard/crm_send_email.py index 21902d9ece4..0282ddd346b 100644 --- a/addons/crm/wizard/crm_send_email.py +++ b/addons/crm/wizard/crm_send_email.py @@ -212,9 +212,13 @@ class crm_send_new_email(osv.osv_memory): res.update({'email_to': ''}) if 'email_from' in fields: res.update({'email_from': user_mail_from and tools.ustr(user_mail_from) or ''}) + if 'reply_to' in fields: - if hasattr(case, 'section_id'): - res.update({'reply_to': case.section_id and case.section_id.reply_to or False}) + if hasattr(case, 'set_reply_to'): + case.set_reply_to(res) + elif hasattr(case, 'section_id'): + res.update({'reply_to' : case.section_id and case.section_id.reply_to or False}) + if 'subject' in fields: res.update({'subject': tools.ustr(context.get('subject', case.name) or '')}) if context.get('mass_mail'): @@ -276,9 +280,10 @@ class crm_send_new_email(osv.osv_memory): if 'email_cc' in fields: email_cc = (case.email_cc and tools.ustr(case.email_cc) + ', ' or '') + (hist.email_cc or '') res.update({'email_cc': email_cc}) - if 'reply_to' in fields: - if hasattr(case, 'section_id'): - res.update({'reply_to': case.section_id.reply_to or ''}) + if hasattr(case, 'set_reply_to'): + case.set_reply_to(res) + elif hasattr(case, 'section_id'): + res.update({'reply_to': case.section_id.reply_to or ''}) if 'state' in fields: res['state'] = u'pending' return res From 6063f74810a9e2e510ec87f83709542c6a0c3f34 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 22 Apr 2011 13:43:35 +0200 Subject: [PATCH 006/106] [FIX] Catch the Exception and not the all bzr revid: stephane@openerp.com-20110422114335-jxm4jrbkw1hw24t3 --- .../mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py b/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py index 204cb2d5d03..e30741c882e 100644 --- a/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py +++ b/addons/mail_gateway/scripts/openerp_mailgate/openerp_mailgate.py @@ -86,7 +86,7 @@ if __name__ == '__main__': custom_values = {} try: custom_values = dict(eval(options.custom_values)) - except: + except Exception: pass parser.parse(msg_txt, custom_values) From dcf034bd52a861454a5dd7f10b0af5409468ba13 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 22 Apr 2011 13:44:23 +0200 Subject: [PATCH 007/106] [FIX] Use the Responsible (user_id) of the case as the default value in the sheet line. [FIX] Remove the on_change event on the analytic account in the worklogs bzr revid: stephane@openerp.com-20110422114423-fhqluv4tp1uho6x7 --- addons/project_issue_sheet/project_issue_sheet_view.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/project_issue_sheet/project_issue_sheet_view.xml b/addons/project_issue_sheet/project_issue_sheet_view.xml index 058fd67a946..4de1caeddd3 100644 --- a/addons/project_issue_sheet/project_issue_sheet_view.xml +++ b/addons/project_issue_sheet/project_issue_sheet_view.xml @@ -18,11 +18,11 @@ - - + + - + From c56df0436089193581ccdf961f51442b96b38152 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 22 Apr 2011 13:46:05 +0200 Subject: [PATCH 008/106] [FIX] Set the user_id (Responsible) field when the user clicks up on the "Open" button. [FIX] There was a bug in the computation function for the working_hours, delay_open, ... fields. [FIX] The user_id (Responsible) is replaced by a many2one instead of a related field. [IMP] Escalation: The user_id (Responsible) is the project manager of the project escalation, if there is no project manager, let's empty. bzr revid: stephane@openerp.com-20110422114605-nlle7h9mpqq9yj00 --- addons/project_issue/project_issue.py | 43 ++++++++++++------- addons/project_issue/project_issue_view.xml | 21 +++++---- .../report/project_issue_report_view.xml | 8 ++-- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index a8a32847243..e6ebf64e453 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -56,7 +56,7 @@ class project_issue(crm.crm_case, osv.osv): """ res = super(project_issue, self).case_open(cr, uid, ids, *args) - self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'), 'assigned_to' : uid}) + self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id' : uid}) for (id, name) in self.name_get(cr, uid, ids): message = _("Issue '%s' has been opened.") % name self.log(cr, uid, id, message) @@ -90,32 +90,32 @@ class project_issue(crm.crm_case, osv.osv): res = {} for issue in self.browse(cr, uid, ids, context=context): + res[issue.id] = {} for field in fields: - res[issue.id] = {} duration = 0 ans = False hours = 0 + date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") if field in ['working_hours_open','day_open']: if issue.date_open: - date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S") ans = date_open - date_create date_until = issue.date_open #Calculating no. of working hours to open the issue hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id, - datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'), - datetime.strptime(issue.date_open, '%Y-%m-%d %H:%M:%S')) + date_create, + date_open) elif field in ['working_hours_close','day_close']: if issue.date_closed: - date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S") date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S") date_until = issue.date_closed ans = date_close - date_create #Calculating no. of working hours to close the issue hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id, - datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'), - datetime.strptime(issue.date_closed, '%Y-%m-%d %H:%M:%S')) + date_create, + date_close) + if ans: resource_id = False if issue.user_id: @@ -125,7 +125,11 @@ class project_issue(crm.crm_case, osv.osv): duration = float(ans.days) if issue.project_id and issue.project_id.resource_calendar_id: duration = float(ans.days) * 24 - new_dates = cal_obj.interval_min_get(cr, uid, issue.project_id.resource_calendar_id.id, datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'), duration, resource=resource_id) + + new_dates = cal_obj.interval_min_get(cr, uid, + issue.project_id.resource_calendar_id.id, + date_create, + duration, resource=resource_id) no_days = [] date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S') for in_time, out_time in new_dates: @@ -134,10 +138,12 @@ class project_issue(crm.crm_case, osv.osv): if out_time > date_until: break duration = len(no_days) + if field in ['working_hours_open','working_hours_close']: res[issue.id][field] = hours else: res[issue.id][field] = abs(float(duration)) + return res def _get_issue_task(self, cr, uid, ids, context=None): @@ -175,7 +181,7 @@ class project_issue(crm.crm_case, osv.osv): 'section_id': fields.many2one('crm.case.section', 'Sales Team', \ select=True, help='Sales team to which Case belongs to.\ Define Responsible user and Email account for mail gateway.'), - 'user_id': fields.related('project_id', 'user_id', type='many2one', relation='res.users', store=True, select=1, string='Responsible'), + 'user_id' : fields.many2one('res.users', select=1, string='Responsible'), 'partner_id': fields.many2one('res.partner', 'Partner'), 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \ domain="[('partner_id','=',partner_id)]"), @@ -282,7 +288,7 @@ class project_issue(crm.crm_case, osv.osv): 'date': bug.date, 'project_id': bug.project_id.id, 'priority': bug.priority, - 'user_id': bug.assigned_to.id, + 'user_id': bug.user_id.id, 'planned_hours': 0.0, }) @@ -362,13 +368,10 @@ class project_issue(crm.crm_case, osv.osv): """ cases = self.browse(cr, uid, ids) for case in cases: - data = {} + data = {'state' : 'draft'} if case.project_id.project_escalation_id: data['project_id'] = case.project_id.project_escalation_id.id - if case.project_id.project_escalation_id.user_id: - data['user_id'] = case.project_id.project_escalation_id.user_id.id - if case.task_id: - self.pool.get('project.task').write(cr, uid, [case.task_id.id], {'project_id': data['project_id'], 'user_id': False}) + data['user_id'] = case.project_id.project_escalation_id.user_id and case.project_id.project_escalation_id.user_id.id or False else: raise osv.except_osv(_('Warning !'), _('You cannot escalate this issue.\nThe relevant Project has not configured the Escalation Project!')) self.write(cr, uid, [case.id], data) @@ -487,6 +490,14 @@ class project_issue(crm.crm_case, osv.osv): return super(project_issue, self).copy(cr, uid, id, default=default, context=context) + #def set_reply_to(self, cr, uid, oid, values, context=None): + # default_value = False + + # if oid: + # this = self.browse(cr, uid, oid, context=context)[0] + # if this.section_id: + # values['reply_to'] = this.section_id.email or default_value + project_issue() class project(osv.osv): diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 2224527e9d2..34d429b4352 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -55,8 +55,7 @@ - - + @@ -75,7 +74,7 @@ - + From d07f77f62b51b60ae92520513800be6dc93a83f3 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Wed, 9 Nov 2011 11:43:12 +0100 Subject: [PATCH 076/106] [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 077/106] [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 078/106] [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 079/106] [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 080/106] [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 081/106] [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 082/106] [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 083/106] [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 084/106] [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
        +
        +
        +
        + + + + +