[MERGE] project_issue: days since creation date added
authorQuentin (OpenERP) <qdp-launchpad@openerp.com>
Fri, 12 Aug 2011 15:52:53 +0000 (17:52 +0200)
committerQuentin (OpenERP) <qdp-launchpad@openerp.com>
Fri, 12 Aug 2011 15:52:53 +0000 (17:52 +0200)
bzr revid: qdp-launchpad@openerp.com-20110812155253-21jegfewstaoowu4

16 files changed:
addons/account_invoice_l10nbe/__init__.py [new file with mode: 0644]
addons/account_invoice_l10nbe/__openerp__.py [new file with mode: 0644]
addons/account_invoice_l10nbe/account_invoice_view.xml [new file with mode: 0644]
addons/account_invoice_l10nbe/i18n/fr.po [new file with mode: 0644]
addons/account_invoice_l10nbe/i18n/nl.po [new file with mode: 0644]
addons/account_invoice_l10nbe/invoice.py [new file with mode: 0644]
addons/account_invoice_l10nbe/partner.py [new file with mode: 0644]
addons/account_invoice_l10nbe/partner_view.xml [new file with mode: 0644]
addons/base/controllers/main.py
addons/base/static/src/js/core.js
addons/base/static/src/js/formats.js
addons/base/static/src/js/list.js
addons/base/static/src/js/search.js
addons/base_gantt/static/src/js/gantt.js
doc/source/addons.rst
doc/source/development.rst

diff --git a/addons/account_invoice_l10nbe/__init__.py b/addons/account_invoice_l10nbe/__init__.py
new file mode 100644 (file)
index 0000000..176d1b9
--- /dev/null
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    
+#    Copyright (c) 2011 Noviat nv/sa (www.noviat.be). All rights reserved.
+# 
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import partner
+import invoice
diff --git a/addons/account_invoice_l10nbe/__openerp__.py b/addons/account_invoice_l10nbe/__openerp__.py
new file mode 100644 (file)
index 0000000..c8d08d0
--- /dev/null
@@ -0,0 +1,55 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    
+#    Copyright (c) 2011 Noviat nv/sa (www.noviat.be). All rights reserved.
+# 
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+{
+    'name': 'Add support for Belgian structured communication to Invoices',
+    'version': '1.2',
+    'license': 'AGPL-3',
+    'author': 'Noviat',
+    'category' : 'Localization/Accounting',
+    'description': """
+    
+Belgian localisation for in- and outgoing invoices (prereq to account_coda):
+    - Rename 'reference' field labels to 'Communication'
+    - Add support for Belgian Structured Communication
+
+A Structured Communication can be generated automatically on outgoing invoices according to the following algorithms:
+    1) Random : +++RRR/RRRR/RRRDD+++
+        R..R = Random Digits, DD = Check Digits
+    2) Date : +++DOY/YEAR/SSSDD+++
+        DOY = Day of the Year, SSS = Sequence Number, DD = Check Digits)
+    3) Customer Reference +++RRR/RRRR/SSSDDD+++
+        R..R = Customer Reference without non-numeric characters, SSS = Sequence Number, DD = Check Digits)  
+        
+The preferred type of Structured Communication and associated Algorithm can be specified on the Partner records. 
+A 'random' Structured Communication will generated if no algorithm is specified on the Partner record. 
+
+    """,
+    'depends': ['account', 'account_cancel'],
+    'demo_xml': [],
+    'init_xml': [],
+    'update_xml' : [
+        'partner_view.xml',
+        'account_invoice_view.xml',        
+    ],
+    'active': False,
+    'installable': True,}
diff --git a/addons/account_invoice_l10nbe/account_invoice_view.xml b/addons/account_invoice_l10nbe/account_invoice_view.xml
new file mode 100644 (file)
index 0000000..8cabe03
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+
+    <!-- Adapt Customer Invoices to support bba structured communication -->   
+    <record id="customer_invoice_bbacomm_form" model="ir.ui.view">
+      <field name="name">account.invoice.form.inherit</field>
+      <field name="model">account.invoice</field>
+      <field name="type">form</field>
+      <field name="inherit_id" ref="account.invoice_form"/>
+      <field name="arch" type="xml">
+        <field name="payment_term" position="after">
+          <group col="4" colspan="2">
+              <field name="reference_type" nolabel="1" select="2" size="0" attrs="{'readonly':[('state','!=','draft')]}"
+                 on_change="generate_bbacomm(type,reference_type,algorithm,partner_id,reference)" colspan="1"/>
+              <field name="reference" nolabel="1" select="1" colspan="3" attrs="{'readonly':[('state','!=','draft')]}"/>
+          </group>
+        </field>
+      </field>
+    </record>
+    
+  </data>
+</openerp>
diff --git a/addons/account_invoice_l10nbe/i18n/fr.po b/addons/account_invoice_l10nbe/i18n/fr.po
new file mode 100644 (file)
index 0000000..f6f88f0
--- /dev/null
@@ -0,0 +1,26 @@
+# French translation of openobject-addons.
+# This file contains the translation of the following modules:
+#      * l10n_be_extra
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: support@noviat.be\n"
+"POT-Creation-Date: 2011-01-16 17:06:14.002000\n"
+"PO-Revision-Date: 2011-01-16 17:06:14.002000\n"
+"Last-Translator: Luc De Meyer (Noviat nv/sa)\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. module: l10n_be_extra
+#: field:account.invoice,reference:0
+msgid "Communication"
+msgstr "Communication"
+
+#. module: l10n_be_extra
+#: field:account.invoice,reference_type:0
+msgid "Communication Type"
+msgstr "Type de communication"
+
diff --git a/addons/account_invoice_l10nbe/i18n/nl.po b/addons/account_invoice_l10nbe/i18n/nl.po
new file mode 100644 (file)
index 0000000..4222121
--- /dev/null
@@ -0,0 +1,26 @@
+# Dutch translation of openobject-addons.
+# This file contains the translation of the following modules:
+#      * l10n_be_extra
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: openobject-addons\n"
+"Report-Msgid-Bugs-To: support@noviat.be\n"
+"POT-Creation-Date: 2011-01-16 17:05:57.465000\n"
+"PO-Revision-Date: 2011-01-16 17:05:57.465000\n"
+"Last-Translator: Luc De Meyer (Noviat nv/sa)\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. module: l10n_be_extra
+#: field:account.invoice,reference:0
+msgid "Communication"
+msgstr "Mededeling"
+
+#. module: l10n_be_extra
+#: field:account.invoice,reference_type:0
+msgid "Communication Type"
+msgstr "Type mededeling"
+
diff --git a/addons/account_invoice_l10nbe/invoice.py b/addons/account_invoice_l10nbe/invoice.py
new file mode 100644 (file)
index 0000000..6aea223
--- /dev/null
@@ -0,0 +1,215 @@
+# -*- encoding: utf-8 -*-\r
+##############################################################################\r
+#\r
+#    OpenERP, Open Source Management Solution\r
+#    \r
+#    Copyright (c) 2011 Noviat nv/sa (www.noviat.be). All rights reserved.\r
+# \r
+#    This program is free software: you can redistribute it and/or modify\r
+#    it under the terms of the GNU Affero General Public License as\r
+#    published by the Free Software Foundation, either version 3 of the\r
+#    License, or (at your option) any later version.\r
+#\r
+#    This program is distributed in the hope that it will be useful,\r
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+#    GNU Affero General Public License for more details.\r
+#\r
+#    You should have received a copy of the GNU Affero General Public License\r
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
+#\r
+##############################################################################\r
+\r
+import re, time, random\r
+from osv import fields, osv\r
+from tools.translate import _\r
+import netsvc\r
+logger=netsvc.Logger()\r
+\r
+"""\r
+account.invoice object:\r
+    - Add support for Belgian structured communication\r
+    - Rename 'reference' field labels to 'Communication'\r
+"""\r
+\r
+class account_invoice(osv.osv):\r
+    _inherit = 'account.invoice'\r
+\r
+    def _get_reference_type(self, cursor, user, context=None):\r
+        """Add BBA Structured Communication Type and change labels from 'reference' into 'communication' """  \r
+        res = super(account_invoice, self)._get_reference_type(cursor, user,\r
+                context=context)\r
+        res[[i for i,x in enumerate(res) if x[0] == 'none'][0]] = ('none', 'Free Communication')\r
+        res.append(('bba', 'BBA Structured Communication'))\r
+        #logger.notifyChannel('addons.'+self._name, netsvc.LOG_WARNING, 'reference_type =  %s' %res ) \r
+        return res\r
+\r
+    def check_bbacomm(self, val):\r
+        supported_chars = '0-9+*/ '\r
+        pattern = re.compile('[^' + supported_chars + ']')\r
+        if pattern.findall(val or ''):\r
+            return False                \r
+        bbacomm = re.sub('\D', '', val or '')\r
+        if len(bbacomm) == 12:\r
+            base = int(bbacomm[:10])\r
+            mod = base % 97 or 97      \r
+            if mod == int(bbacomm[-2:]):\r
+                return True\r
+        return False\r
+\r
+    def _check_communication(self, cr, uid, ids):\r
+        for inv in self.browse(cr, uid, ids):\r
+            if inv.reference_type == 'bba':\r
+                return self.check_bbacomm(inv.reference)\r
+        return True\r
+\r
+    def onchange_partner_id(self, cr, uid, ids, type, partner_id,\r
+            date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False):       \r
+        result = super(account_invoice, self).onchange_partner_id(cr, uid, ids, type, partner_id,\r
+            date_invoice, payment_term, partner_bank_id, company_id)\r
+#        reference_type = self.default_get(cr, uid, ['reference_type'])['reference_type']\r
+#        logger.notifyChannel('addons.'+self._name, netsvc.LOG_WARNING, 'partner_id %s' % partner_id)\r
+        reference = False\r
+        reference_type = 'none'\r
+        if partner_id:        \r
+            if (type == 'out_invoice'):\r
+                reference_type = self.pool.get('res.partner').browse(cr, uid, partner_id).out_inv_comm_type\r
+                if reference_type:\r
+                    algorithm = self.pool.get('res.partner').browse(cr, uid, partner_id).out_inv_comm_algorithm\r
+                    if not algorithm:\r
+                        algorithm = 'random' \r
+                    reference = self.generate_bbacomm(cr, uid, ids, type, reference_type, algorithm, partner_id, '')['value']['reference']\r
+        res_update = {       \r
+            'reference_type': reference_type,\r
+            'reference': reference,\r
+        }\r
+        result['value'].update(res_update)\r
+        return result                    \r
+\r
+    def generate_bbacomm(self, cr, uid, ids, type, reference_type, algorithm, partner_id, reference):\r
+        partner_obj =  self.pool.get('res.partner')\r
+        reference = reference or ''  \r
+        if (type == 'out_invoice'):\r
+            if reference_type == 'bba':\r
+                if not algorithm:\r
+                    if partner_id:\r
+                        algorithm = partner_obj.browse(cr, uid, partner_id).out_inv_comm_algorithm\r
+                    if not algorithm:\r
+                        if not algorithm:   \r
+                            algorithm = 'random'\r
+                if algorithm == 'date':\r
+                    if not self.check_bbacomm(reference):\r
+                        doy = time.strftime('%j')\r
+                        year = time.strftime('%Y')\r
+                        seq = '001'\r
+                        seq_ids = self.search(cr, uid, \r
+                            [('type', '=', 'out_invoice'), ('reference_type', '=', 'bba'),\r
+                             ('reference', 'like', '+++%s/%s/%%' % (doy, year))], order='reference')\r
+                        if seq_ids:\r
+                            prev_seq = int(self.browse(cr, uid, seq_ids[-1]).reference[12:15])\r
+                            if prev_seq < 999:\r
+                                seq = '%03d' % (prev_seq + 1)\r
+                            else:\r
+                                raise osv.except_osv(_('Warning!'),\r
+                                    _('The daily maximum of outgoing invoices with an automatically generated BBA Structured Communications has been exceeded!' \\r
+                                      '\nPlease create manually a unique BBA Structured Communication.'))\r
+                        bbacomm = doy + year + seq\r
+                        base = int(bbacomm)\r
+                        mod = base % 97 or 97   \r
+                        reference = '+++%s/%s/%s%02d+++' % (doy, year, seq, mod)\r
+                elif algorithm == 'partner_ref':\r
+                    if not self.check_bbacomm(reference):\r
+                        partner_ref = self.pool.get('res.partner').browse(cr, uid, partner_id).ref\r
+                        partner_ref_nr = re.sub('\D', '', partner_ref or '')\r
+                        if (len(partner_ref_nr) < 3) or (len(partner_ref_nr) > 7):\r
+                            raise osv.except_osv(_('Warning!'),\r
+                                _('The Partner should have a 3-7 digit Reference Number for the generation of BBA Structured Communications!' \\r
+                                  '\nPlease correct the Partner record.'))                            \r
+                        else:\r
+                            partner_ref_nr = partner_ref_nr.ljust(7, '0')\r
+                            seq = '001'\r
+                            seq_ids = self.search(cr, uid, \r
+                                [('type', '=', 'out_invoice'), ('reference_type', '=', 'bba'),\r
+                                 ('reference', 'like', '+++%s/%s/%%' % (partner_ref_nr[:3], partner_ref_nr[3:]))], order='reference')                            \r
+                            if seq_ids:\r
+                                prev_seq = int(self.browse(cr, uid, seq_ids[-1]).reference[12:15])\r
+                                if prev_seq < 999:\r
+                                    seq = '%03d' % (prev_seq + 1)\r
+                                else:\r
+                                    raise osv.except_osv(_('Warning!'),\r
+                                        _('The daily maximum of outgoing invoices with an automatically generated BBA Structured Communications has been exceeded!' \\r
+                                          '\nPlease create manually a unique BBA Structured Communication.'))                            \r
+                        bbacomm = partner_ref_nr + seq\r
+                        base = int(bbacomm)\r
+                        mod = base % 97 or 97                           \r
+                        reference = '+++%s/%s/%s%02d+++' % (partner_ref_nr[:3], partner_ref_nr[3:], seq, mod)\r
+                elif algorithm == 'random':\r
+                    if not self.check_bbacomm(reference):\r
+                        base = random.randint(1, 9999999999)\r
+                        bbacomm = str(base).rjust(7, '0')\r
+                        base = int(bbacomm)\r
+                        mod = base % 97 or 97\r
+                        mod = str(mod).rjust(2, '0')               \r
+                        reference = '+++%s/%s/%s%s+++' % (bbacomm[:3], bbacomm[3:7], bbacomm[7:], mod)\r
+                else:\r
+                    raise osv.except_osv(_('Error!'),\r
+                        _("Unsupported Structured Communication Type Algorithm '%s' !" \\r
+                          "\nPlease contact your OpenERP support channel.") % algorithm)   \r
+        return {'value': {'reference': reference}}    \r
+    \r
+    def create(self, cr, uid, vals, context=None):\r
+        if vals.has_key('reference_type'):\r
+            reference_type = vals['reference_type']\r
+            if reference_type == 'bba':               \r
+                if vals.has_key('reference'):\r
+                    bbacomm = vals['reference']\r
+                else:\r
+                    raise osv.except_osv(_('Warning!'),\r
+                        _('Empty BBA Structured Communication!' \\r
+                          '\nPlease fill in a unique BBA Structured Communication.'))       \r
+                if self.check_bbacomm(bbacomm):\r
+                    reference = re.sub('\D', '', bbacomm)\r
+                    vals['reference'] = '+++' + reference[0:3] + '/' + reference[3:7] + '/' + reference[7:] + '+++'     \r
+                    same_ids = self.search(cr, uid, \r
+                        [('type', '=', 'out_invoice'), ('reference_type', '=', 'bba'),\r
+                         ('reference', '=', vals['reference'])])\r
+                    if same_ids:\r
+                        raise osv.except_osv(_('Warning!'),\r
+                            _('The BBA Structured Communication has already been used!' \\r
+                              '\nPlease create manually a unique BBA Structured Communication.'))                 \r
+        return super(account_invoice, self).create(cr, uid, vals, context=context)     \r
+\r
+    def write(self, cr, uid, ids, vals, context={}):\r
+        for inv in self.browse(cr, uid, ids, context):    \r
+            if vals.has_key('reference_type'):\r
+                reference_type = vals['reference_type']\r
+            else:    \r
+                reference_type = inv.reference_type or ''\r
+            if reference_type == 'bba':               \r
+                if vals.has_key('reference'):\r
+                    bbacomm = vals['reference']\r
+                else:\r
+                    bbacomm = inv.reference or ''\r
+                if self.check_bbacomm(bbacomm):\r
+                    reference = re.sub('\D', '', bbacomm)\r
+                    vals['reference'] = '+++' + reference[0:3] + '/' + reference[3:7] + '/' + reference[7:] + '+++'     \r
+                    same_ids = self.search(cr, uid, \r
+                        [('id', '!=', inv.id), ('type', '=', 'out_invoice'), \r
+                         ('reference_type', '=', 'bba'), ('reference', '=', vals['reference'])])\r
+                    if same_ids:\r
+                        raise osv.except_osv(_('Warning!'),\r
+                            _('The BBA Structured Communication has already been used!' \\r
+                              '\nPlease create manually a unique BBA Structured Communication.'))                 \r
+        return super(account_invoice, self).write(cr, uid, ids, vals, context)    \r
+\r
+    _columns = {\r
+        'reference': fields.char('Communication', size=64, help="The partner reference of this invoice."),\r
+        'reference_type': fields.selection(_get_reference_type, 'Communication Type',\r
+            required=True),\r
+    }\r
+    \r
+    _constraints = [\r
+        (_check_communication, 'Invalid BBA Structured Communication !', ['Communication']),\r
+        ]\r
+\r
+account_invoice()\r
diff --git a/addons/account_invoice_l10nbe/partner.py b/addons/account_invoice_l10nbe/partner.py
new file mode 100644 (file)
index 0000000..2794b81
--- /dev/null
@@ -0,0 +1,48 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    
+#    Created by Luc De Meyer
+#    Copyright (c) 2010 Noviat nv/sa (www.noviat.be). All rights reserved.
+# 
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from osv import fields, osv
+import time
+from tools.translate import _
+import netsvc
+logger=netsvc.Logger()
+
+class res_partner(osv.osv):  
+    """ add field to indicate default 'Communication Type' on customer invoices """
+    _inherit = 'res.partner'
+    
+    def _get_comm_type(self, cr, uid, context=None):
+        res = self.pool.get('account.invoice')._get_reference_type(cr, uid,context=context)
+        return res
+    
+    _columns = {
+        'out_inv_comm_type': fields.selection(_get_comm_type, 'Communication Type', change_default=True,
+            help='Select Default Communication Type for Outgoing Invoices.' ),
+        'out_inv_comm_algorithm': fields.selection([
+            ('random','Random'),
+            ('date','Date'),
+            ('partner_ref','Customer Reference'),
+            ], 'Communication Algorithm',
+            help='Select Algorithm to generate the Structured Communication on Outgoing Invoices.' ),
+    }
+res_partner()    
diff --git a/addons/account_invoice_l10nbe/partner_view.xml b/addons/account_invoice_l10nbe/partner_view.xml
new file mode 100644 (file)
index 0000000..7489322
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+  <data>
+
+    <!-- Extension to Partner Form to support outgoing invoices with automatic generation of structured communication -->
+    <record id="view_partner_inv_comm_type_form" model="ir.ui.view">
+      <field name="name">res.partner.inv_comm_type.form.inherit</field>
+      <field name="model">res.partner</field>
+      <field name="type">form</field>
+      <field name="inherit_id" ref="base.view_partner_form"/>
+      <field name="arch" type="xml">
+        <field name="property_payment_term" position="after">
+          <field name="out_inv_comm_type" groups="base.group_extended"/>
+          <field name="out_inv_comm_algorithm" groups="base.group_extended" attrs="{'invisible':[('out_inv_comm_type','!=','bba')]}"/>
+        </field>
+      </field>
+    </record>    
+
+  </data>
+</openerp>
index 4f6b3ea..36db03d 100644 (file)
@@ -772,8 +772,8 @@ class View(openerpweb.Controller):
         """ Parses an arbitrary string containing a domain, transforms it
         to either a literal domain or a :class:`openerpweb.nonliterals.Domain`
 
-        :param domain: the domain to parse, if the domain is not a string it is assumed to
-        be a literal domain and is returned as-is
+        :param domain: the domain to parse, if the domain is not a string it
+                       is assumed to be a literal domain and is returned as-is
         :param session: Current OpenERP session
         :type session: openerpweb.openerpweb.OpenERPSession
         """
@@ -789,8 +789,8 @@ class View(openerpweb.Controller):
         """ Parses an arbitrary string containing a context, transforms it
         to either a literal context or a :class:`openerpweb.nonliterals.Context`
 
-        :param context: the context to parse, if the context is not a string it is assumed to
-        be a literal domain and is returned as-is
+        :param context: the context to parse, if the context is not a string it
+               is assumed to be a literal domain and is returned as-is
         :param session: Current OpenERP session
         :type session: openerpweb.openerpweb.OpenERPSession
         """
index 859c966..da9b5ab 100644 (file)
@@ -373,59 +373,6 @@ openerp.base.SessionAware = openerp.base.Class.extend({
     }
 });
 
-/**
- * Base class for all visual components. Provides a lot of functionalities helpful
- * for the management of a part of the DOM.
- *
- * Widget handles:
- * - Rendering with QWeb.
- * - Life-cycle management and parenting (when a parent is destroyed, all its children are
- *     destroyed too).
- * - Insertion in DOM.
- *
- * Widget also extends SessionAware for ease of use.
- *
- * Guide to create implementations of the Widget class:
- * ==============================================
- *
- * Here is a sample child class:
- *
- * MyWidget = openerp.base.Widget.extend({
- *     // the name of the QWeb template to use for rendering
- *     template: "MyQWebTemplate",
- *     // identifier prefix, it is useful to put an obvious one for debugging
- *     identifier_prefix: 'my-id-prefix-',
- *
- *     init: function(parent) {
- *         this._super(parent);
- *         // stuff that you want to init before the rendering
- *     },
- *     start: function() {
- *         this._super();
- *         // stuff you want to make after the rendering, `this.$element` holds a correct value
- *         this.$element.find(".my_button").click(/* an example of event binding * /);
- *
- *         // if you have some asynchronous operations, it's a good idea to return
- *         // a promise in start()
- *         var promise = this.rpc(...);
- *         return promise;
- *     }
- * });
- *
- * Now this class can simply be used with the following syntax:
- *
- * var my_widget = new MyWidget(this);
- * my_widget.appendTo($(".some-div"));
- *
- * With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
- * DOM inside the ".some-div" div and its events were binded.
- *
- * And of course, when you don't need that widget anymore, just do:
- *
- * my_widget.stop();
- *
- * That will kill the widget in a clean way and erase its content from the dom.
- */
 openerp.base.Widget = openerp.base.SessionAware.extend({
     /**
      * The name of the QWeb template that will be used for rendering. Must be
index fb1ada5..074cabe 100644 (file)
@@ -83,7 +83,7 @@ openerp.base.parse_time = function(str) {
  */
 var zpad = function(str, size) {
     str = "" + str;
-    return new Array(size - str.length).join('0') + str;
+    return new Array(size - str.length + 1).join('0') + str;
 };
 
 /**
index 3032e70..a044022 100644 (file)
@@ -772,7 +772,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
             var old_index = this.dataset.index;
             this.dataset.index = record_index;
             read_p = this.dataset.read_index(
-                _.filter(_.pluck(this.columns, 'name'), _.identity),
+                _.pluck(_(this.columns).filter(function (r) {return r.tag === 'field';}), 'name'),
                 function (record) {
                     var form_record = self.transform_record(record);
                     self.rows.splice(record_index, 1, form_record);
@@ -1035,7 +1035,7 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
             page = this.datagroup.openable ? this.page : view.page;
 
         dataset.read_slice({
-                fields: _.filter(_.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name'), _.identity),
+                fields: _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name'),
                 offset: page * limit,
                 limit: limit
             }, function (records) {
index 4b74683..63ccec0 100644 (file)
@@ -875,11 +875,10 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
         this._super();
         var _this = this;
         this.add_prop();
-        this.$element.find('.searchview_extended_add_proposition').click(function (e) {
+        this.$element.find('.searchview_extended_add_proposition').click(function () {
             _this.add_prop();
         });
-        var delete_btn = this.$element.find('.searchview_extended_delete_group');
-        delete_btn.click(function (e) {
+        this.$element.find('.searchview_extended_delete_group').click(function () {
             _this.stop();
         });
     },
@@ -889,7 +888,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
         }).compact().value();
         var choice = this.$element.find(".searchview_extended_group_choice").val();
         var op = choice == "all" ? "&" : "|";
-        return [].concat(choice == "none" ? ['!'] : [],
+        return choice == "none" ? ['!'] : [].concat(
             _.map(_.range(_.max([0,props.length - 1])), function() { return op; }),
             props);
     },
@@ -901,10 +900,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
         parent.check_last_element();
     },
     set_last_group: function(is_last) {
-        if(is_last)
-            this.$element.addClass("last_group");
-        else
-            this.$element.removeClass("last_group");
+        this.$element.toggleClass('last_group', is_last);
     }
 });
 
@@ -927,8 +923,7 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.OldWidget.extend({
         this.$element.find(".searchview_extended_prop_field").change(function() {
             _this.changed();
         });
-        var delete_btn = this.$element.find('.searchview_extended_delete_prop');
-        delete_btn.click(function (e) {
+        this.$element.find('.searchview_extended_delete_prop').click(function () {
             _this.stop();
         });
     },
index 4a38747..307dc94 100644 (file)
@@ -1,16 +1,14 @@
 /*---------------------------------------------------------
  * OpenERP base_gantt
  *---------------------------------------------------------*/
-
 openerp.base_gantt = function (openerp) {
 QWeb.add_template('/base_gantt/static/src/xml/base_gantt.xml');
 openerp.base.views.add('gantt', 'openerp.base_gantt.GanttView');
-openerp.base_gantt.GanttView = openerp.base.Widget.extend({
-
-init: function(view_manager, session, element_id, dataset, view_id) {
+openerp.base_gantt.GanttView = openerp.base.View.extend({
 
-        this._super(session, element_id);
-        this.view_manager = view_manager;
+init: function(parent, element_id, dataset, view_id) {
+        this._super(parent, element_id);
+        this.view_manager = parent || new openerp.base.NullViewManager();
         this.dataset = dataset;
         this.model = dataset.model;
         this.view_id = view_id;
@@ -29,7 +27,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
         this.calendar_fields = {};
         this.info_fields = [];
         this.domain = this.dataset._domain ? this.dataset._domain: [];
-        this.context = {};
+        this.context = this.dataset.context || {};
     },
 
     start: function() {
@@ -52,8 +50,8 @@ init: function(view_manager, session, element_id, dataset, view_id) {
         this.color_field = this.fields_view.arch.attrs.color;
         this.day_length = this.fields_view.arch.attrs.day_length || 8;
         this.colors = this.fields_view.arch.attrs.colors;
-
-        this.text = this.fields_view.arch.children[0].children[0].attrs.name;
+        var arch_children = this.fields_view.arch.children[0];
+        this.text = arch_children.children[0] ? arch_children.children[0].attrs.name : arch_children.attrs.name;
         this.parent = this.fields_view.arch.children[0].attrs.link;
 
         this.format = "yyyy-MM-dd";
@@ -100,7 +98,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
 
         if (result.length != 0){
             var show_event = [];
-            for (i in result){
+            for (var i in result){
                 var res = result[i];
                 if (res[this.date_start] != false){
                     
@@ -132,7 +130,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
         var child_event = {};
         var temp_id = "";
         var final_events = [];
-        for (i in show_event) {
+        for (var i in show_event) {
 
             var res = show_event[i];
 
@@ -161,11 +159,8 @@ init: function(view_manager, session, element_id, dataset, view_id) {
             if (duration == false)
                 duration = 0
 
-            if (self.grp.length == 0){
-               self.grp.push({'group_by' : this.parent})
-            }
-            if (self.grp != undefined){
-                for (j in self.grp){
+            if (self.grp.length){
+                for (var j in self.grp){
                     var grp_key = res[self.grp[j]['group_by']];
                     if (typeof(grp_key) == "object"){
                         grp_key = res[self.grp[j]['group_by']][1];
@@ -175,7 +170,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
                     }
 
                     if (grp_key == false){
-                        grp_key = "False";
+                        grp_key = "Undefined";
                     }
 
                     if (j == 0){
@@ -207,9 +202,17 @@ init: function(view_manager, session, element_id, dataset, view_id) {
                 all_events[id] = {'parent': temp_id, 'evt':[id , text, start_date, duration, 100, "", color_box[color]]};
                 final_events.push(id);
             }
+            else {
+                if (i == 0) {
+                    var mod_id = "_" + i;
+                    all_events[mod_id] = {'parent': "", 'evt': [mod_id, this.name, start_date, start_date, 100, "", "white"]};
+                }
+                all_events[id] = {'parent': mod_id, 'evt':[id , text, start_date, duration, 100, "", color_box[color]]};
+                final_events.push(id);
+            }
         }
 
-        for (i in final_events){
+        for (var i in final_events){
             var evt_id = final_events[i];
             var evt_date = all_events[evt_id]['evt'][2];
             while (all_events[evt_id]['parent'] != "") {
@@ -225,7 +228,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
         var evt_duration = "";
         var evt_end_date = "";
 
-        for (i in final_events){
+        for (var i in final_events){
             evt_id = final_events[i];
             evt_date = all_events[evt_id]['evt'][2];
             evt_duration = all_events[evt_id]['evt'][3];
@@ -242,42 +245,27 @@ init: function(view_manager, session, element_id, dataset, view_id) {
             }
         }
 
-        for (j in self.grp){
-            for (i in all_events){
-                res = all_events[i];
-                if ((typeof(res['evt'][3])) == "object"){
-                    res['evt'][3] = self.hours_between(res['evt'][2],res['evt'][3]);
-                }
+        for (var j in self.grp) {
+            self.render_events(all_events, j);
+        }
 
-                k = res['evt'][0].toString().indexOf('_');
-                if (k != -1){
-                    if (res['evt'][0].substring(k) == "_"+j){
-                        if (j == 0){
-                            task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
-                            project.addTask(task);
-                        } else {
-                            task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
-                            prt = project.getTaskById(res['parent']);
-                            prt.addChildTask(task);
-                        }
-                    }
-                }
-            }
+        if (!self.grp.length) {
+            self.render_events(all_events, 0);
         }
-        for (i in final_events){
+
+        for (var i in final_events){
             evt_id = final_events[i];
             res = all_events[evt_id];
-
             task=new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
             prt = project.getTaskById(res['parent']);
             prt.addChildTask(task);
         }
 
-        oth_hgt = 264;
-        min_hgt = 150;
-        name_min_wdt = 150;
-        gantt_hgt = jQuery(window).height() - oth_hgt;
-        search_wdt = jQuery("#oe_app_search").width();
+        var oth_hgt = 264;
+        var min_hgt = 150;
+        var name_min_wdt = 150;
+        var gantt_hgt = jQuery(window).height() - oth_hgt;
+        var search_wdt = jQuery("#oe_app_search").width();
 
         if (gantt_hgt > min_hgt){
             jQuery('#GanttDiv').height(gantt_hgt).width(search_wdt);
@@ -291,13 +279,13 @@ init: function(view_manager, session, element_id, dataset, view_id) {
         ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_resize_drag_end(task, "drag");});
         ganttChartControl.attachEvent("onTaskDblClick", function(task) {self.open_popup(task);});
 
-        taskdiv = jQuery("div.taskPanel").parent();
+        var taskdiv = jQuery("div.taskPanel").parent();
         taskdiv.addClass('ganttTaskPanel');
         taskdiv.prev().addClass('ganttDayPanel');
-        $gantt_panel = jQuery(".ganttTaskPanel , .ganttDayPanel");
+        var $gantt_panel = jQuery(".ganttTaskPanel , .ganttDayPanel");
 
-        ganttrow = jQuery('.taskPanel').closest('tr');
-        gtd =  ganttrow.children(':first-child');
+        var ganttrow = jQuery('.taskPanel').closest('tr');
+        var gtd =  ganttrow.children(':first-child');
         gtd.children().addClass('task-name');
 
         jQuery(".toggle-sidebar").click(function(e) {
@@ -319,9 +307,8 @@ init: function(view_manager, session, element_id, dataset, view_id) {
         $gantt_panel.width(1);
         jQuery(".ganttTaskPanel").parent().width(1);
 
-        search_wdt = jQuery("#oe_app_search").width();
-        day_wdt = jQuery(".ganttDayPanel").children().children().width();
-        name_wdt = jQuery('.task-name').width();
+        var search_wdt = jQuery("#oe_app_search").width();
+        var day_wdt = jQuery(".ganttDayPanel").children().children().width();
         jQuery('#GanttDiv').css('width','100%');
 
         if (search_wdt - day_wdt <= name_min_wdt){
@@ -345,7 +332,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
 
          var self = this;
 
-         dat = this.convert_str_date(dat);
+         var dat = this.convert_str_date(dat);
 
          var day = Math.floor(duration/self.day_length);
          var hrs = duration % self.day_length;
@@ -356,47 +343,98 @@ init: function(view_manager, session, element_id, dataset, view_id) {
          return dat;
     },
 
-    hours_between: function(date1, date2) {
+    hours_between: function(date1, date2, parent_task) {
 
         var ONE_DAY = 1000 * 60 * 60 * 24;
         var date1_ms = date1.getTime();
         var date2_ms = date2.getTime();
         var difference_ms = Math.abs(date1_ms - date2_ms);
 
-        d = Math.floor(difference_ms / ONE_DAY);
-        h = (difference_ms % ONE_DAY)/(1000 * 60 * 60);
-        num = (d * this.day_length) + h;
+        var d = parent_task? Math.ceil(difference_ms / ONE_DAY) : Math.floor(difference_ms / ONE_DAY);
+        var h = (difference_ms % ONE_DAY)/(1000 * 60 * 60);
+        var num = (d * this.day_length) + h;
         return parseFloat(num.toFixed(2));
 
     },
 
+    render_events : function(all_events, j) {
+
+        var self = this;
+        for (var i in all_events){
+            var res = all_events[i];
+            if ((typeof(res['evt'][3])) == "object"){
+                res['evt'][3] = self.hours_between(res['evt'][2],res['evt'][3], true);
+            }
+
+            k = res['evt'][0].toString().indexOf('_');
+
+            if (k != -1) {
+                if (res['evt'][0].substring(k) == "_"+j){
+                    if (j == 0){
+                        task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
+                        project.addTask(task);
+                    } else {
+                        task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
+                        prt = project.getTaskById(res['parent']);
+                        prt.addChildTask(task);
+                    }
+                }
+            }
+        }
+    },
+
     open_popup : function(task) {
         var event_id = task.getId();
-        
         if(event_id.toString().search("_") != -1)
             return;
-        if (event_id) {
-            event_id = parseInt(event_id, 10);
-            var dataset_event_index = jQuery.inArray(event_id, this.ids);
-        } else  {
-            var dataset_event_index = null;
+        if(event_id) event_id = parseInt(event_id, 10);
+
+        var action = {
+            "res_model": this.dataset.model,
+            "res_id": event_id,
+            "views":[[false,"form"]],
+            "type":"ir.actions.act_window",
+            "view_type":"form",
+            "view_mode":"form"
         }
-        this.dataset.index = dataset_event_index;
+
+        action.flags = {
+            search_view: false,
+            sidebar : false,
+            views_switcher : false,
+            pager: false
+            }
         var element_id = _.uniqueId("act_window_dialog");
-        var dialog = jQuery('<div>', 
-                        {'id': element_id
-                    }).dialog({
-                        title: 'Gantt Chart',
-                        modal: true,
-                        minWidth: 800,
-                        position: 'top'
-                    });
-        var event_form = new openerp.base.FormView(this.view_manager, this.session, element_id, this.dataset, false);
-        event_form.start();
+        var dialog = jQuery('<div>', {
+            'id': element_id
+            }).dialog({
+                modal: true,
+                width: 'auto',
+                height: 'auto',
+                buttons: {
+                    Cancel: function() {
+                        $(this).dialog("destroy");
+                    },
+                    Save: function() {
+                        var view_manager = action_manager.viewmanager;
+                        var _dialog = this;
+                        view_manager.views[view_manager.active_view].controller.do_save(function(r) {
+                            $(_dialog).dialog("destroy");
+                            self.reload_gantt();
+                        })
+                    }
+                }
+        });
+        var action_manager = new openerp.base.ActionManager(this, element_id);
+        action_manager.start();
+        action_manager.do_action(action);
+
+        //Default_get
+        if(!event_id) action_manager.viewmanager.dataset.index = null;
     },
 
     on_drag_start : function(task){
-        st_date = task.getEST();
+        var st_date = task.getEST();
         if(st_date.getHours()){
             self.hh = st_date.getHours();
             self.mm = st_date.getMinutes();
index 4f37119..7261e57 100644 (file)
@@ -113,6 +113,98 @@ initializing the addon.
 Creating new standard roles
 ---------------------------
 
+Widget
+++++++
+
+This is the base class for all visual components. It provides a number of
+services for the management of a DOM subtree:
+
+* Rendering with QWeb
+
+* Parenting-child relations
+
+* Life-cycle management (including facilitating children destruction when a
+  parent object is removed)
+
+* DOM insertion, via jQuery-powered insertion methods. Insertion targets can
+  be anything the corresponding jQuery method accepts (generally selectors,
+  DOM nodes and jQuery objects):
+
+  :js:func:`~openerp.base.Widget.appendTo`
+    Renders the widget and inserts it as the last child of the target, uses
+    `.appendTo()`_
+
+  :js:func:`~openerp.base.Widget.prependTo`
+    Renders the widget and inserts it as the first child of the target, uses
+    `.prependTo()`_
+
+  :js:func:`~openerp.base.Widget.insertAfter`
+    Renders the widget and inserts it as the preceding sibling of the target,
+    uses `.insertAfter()`_
+
+  :js:func:`~openerp.base.Widget.insertBefore`
+    Renders the widget and inserts it as the following sibling of the target,
+    uses `.insertBefore()`_
+
+:js:class:`~openerp.base.Widget` inherits from
+:js:class:`~openerp.base.SessionAware`, so subclasses can easily access the
+RPC layers.
+
+Subclassing Widget
+~~~~~~~~~~~~~~~~~~
+
+:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the
+:js:func:`~openerp.base.Class.extend` method), and provides a number of
+abstract properties and concrete methods (which you may or may not want to
+override). Creating a subclass looks like this:
+
+.. code-block:: javascript
+
+    var MyWidget = openerp.base.Widget.extend({
+        // QWeb template to use when rendering the object
+        template: "MyQWebTemplate",
+        // autogenerated id prefix, specificity helps when debugging
+        identifier_prefix: 'my-id-prefix-',
+
+        init: function(parent) {
+            this._super(parent);
+            // insert code to execute before rendering, for object
+            // initialization
+        },
+        start: function() {
+            this._super();
+            // post-rendering initialization code, at this point
+            // ``this.$element`` has been initialized
+            this.$element.find(".my_button").click(/* an example of event binding * /);
+
+            // if ``start`` is asynchronous, return a promise object so callers
+            // know when the object is done initializing
+            return this.rpc(/* … */)
+        }
+    });
+
+The new class can then be used in the following manner:
+
+.. code-block:: javascript
+
+    // Create the instance
+    var my_widget = new MyWidget(this);
+    // Render and insert into DOM
+    my_widget.appendTo(".some-div");
+
+After these two lines have executed (and any promise returned by ``appendTo``
+has been resolved if needed), the widget is ready to be used.
+
+If the widget is not needed anymore (because it's transient), simply terminate
+it:
+
+.. code-block:: javascript
+
+    my_widget.stop();
+
+will unbind all DOM events, remove the widget's content from the DOM and
+destroy all widget data.
+
 Views
 +++++
 
@@ -236,193 +328,6 @@ replace ``addons`` by the directory in which your own addon lives.
      and run ``nosetests addons`` instead of the ``unit2`` command,
      the result should be exactly the same.
 
-APIs
-----
-
-Javascript
-++++++++++
-
-.. js:class:: openerp.base.Widget(view, node)
-
-    :param openerp.base.Controller view: The view to which the widget belongs
-    :param Object node: the ``fields_view_get`` descriptor for the widget
-
-    .. js:attribute:: $element
-
-        The widget's root element as jQuery object
-
-.. js:class:: openerp.base.DataSet(session, model)
-
-    :param openerp.base.Session session: the RPC session object
-    :param String model: the model managed by this dataset
-
-    The DataSet is the abstraction for a sequence of records stored in
-    database.
-
-    It provides interfaces for reading records based on search
-    criteria, and for selecting and fetching records based on
-    activated ids.
-
-    .. js:function:: fetch([offset][, limit])
-
-       :param Number offset: the index from which records should start
-                             being returned (section)
-       :param Number limit: the maximum number of records to return
-       :returns: the dataset instance it was called on
-
-       Asynchronously fetches the records selected by the DataSet's
-       domain and context, in the provided sort order if any.
-
-       Only fetches the fields selected by the DataSet.
-
-       On success, triggers :js:func:`on_fetch`
-
-    .. js:function:: on_fetch(records, event)
-
-        :param Array records: an array of
-                             :js:class:`openerp.base.DataRecord`
-                             matching the DataSet's selection
-        :param event: a data holder letting the event handler fetch
-                     meta-informations about the event.
-        :type event: OnFetchEvent
-
-        Fired after :js:func:`fetch` is done fetching the records
-        selected by the DataSet.
-
-    .. js:function:: active_ids
-
-        :returns: the dataset instance it was called on
-
-        Asynchronously fetches the active records for this DataSet.
-
-        On success, triggers :js:func:`on_active_ids`
-
-    .. js:function:: on_active_ids(records)
-
-        :param Array records: an array of
-                              :js:class:`openerp.base.DataRecord`
-                              matching the currently active ids
-
-        Fired after :js:func:`active_ids` fetched the records matching
-        the DataSet's active ids.
-
-    .. js:function:: active_id
-
-        :returns: the dataset instance in was called on
-
-        Asynchronously fetches the current active record.
-
-        On success, triggers :js:func:`on_active_id`
-
-    .. js:function:: on_active_id(record)
-
-        :param Object record: the record fetched by
-                              :js:func:`active_id`, or ``null``
-        :type record: openerp.base.DataRecord
-
-        Fired after :js:func:`active_id` fetched the record matching
-        the dataset's active id
-
-    .. js:function:: set(options)
-
-        :param Object options: the options to set on the dataset
-        :type options: DataSetOptions
-        :returns: the dataset instance it was called on
-
-        Configures the data set by setting various properties on it
-
-    .. js:function:: prev
-
-        :returns: the dataset instance it was called on
-
-        Activates the id preceding the current one in the active ids
-        sequence of the dataset.
-
-        If the current active id is at the start of the sequence,
-        wraps back to the last id of the sequence.
-
-    .. js:function:: next
-
-        :returns: the dataset instance it was called on
-
-        Activates the id following the current one in the active ids
-        sequence.
-
-        If the current active id is the last of the sequence, wraps
-        back to the beginning of the active ids sequence.
-
-    .. js:function:: select(ids)
-
-        :param Array ids: the identifiers to activate on the dataset
-        :returns: the dataset instance it was called on
-
-        Activates all the ids specified in the dataset, resets the
-        current active id to be the first id of the new sequence.
-
-        The internal order will be the same as the ids list provided.
-
-    .. js:function:: get_active_ids
-
-        :returns: the list of current active ids for the dataset
-
-    .. js:function:: activate(id)
-
-        :param Number id: the id to activate
-        :returns: the dataset instance it was called on
-
-        Activates the id provided in the dataset. If no ids are
-        selected, selects the id in the dataset.
-
-        If ids are already selected and the provided id is not in that
-        selection, raises an error.
-
-    .. js:function:: get_active_id
-
-        :returns: the dataset's current active id
-
-Ad-hoc objects and structural types
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-These objects are not associated with any specific class, they're
-generally literal objects created on the spot. Names are merely
-convenient ways to refer to them and their properties.
-
-.. js:class:: OnFetchEvent
-
-    .. js:attribute:: context
-
-        The context used for the :js:func:`fetch` call (domain set on
-        the :js:class:`openerp.base.DataSet` when ``fetch`` was
-        called)
-
-    .. js:attribute:: domain
-
-        The domain used for the :js:func:`fetch` call
-
-    .. js:attribute:: limit
-
-        The limit with which the original :js:func:`fetch` call was
-        performed
-
-    .. js:attribute:: offset
-
-        The offset with which the original :js:func:`fetch` call was
-        performed
-
-    .. js:attribute:: sort
-
-       The sorting criteria active on the
-       :js:class:`openerp.base.DataSet` when :js:func:`fetch` was
-       called
-
-.. js:class:: DataSetOptions
-
-    .. js:attribute:: context
-
-    .. js:attribute:: domain
-
-    .. js:attribute:: sort
-
 Python
 ++++++
 
@@ -499,3 +404,15 @@ Python
 
 .. _promise object:
     http://api.jquery.com/deferred.promise/
+
+.. _.appendTo():
+    http://api.jquery.com/appendTo/
+
+.. _.prependTo():
+    http://api.jquery.com/prependTo/
+
+.. _.insertAfter():
+    http://api.jquery.com/insertAfter/
+
+.. _.insertBefore():
+    http://api.jquery.com/insertBefore/
index bfb176c..85fbebc 100644 (file)
@@ -374,11 +374,13 @@ Deletion can be overridden by replacing the
 calls :js:func:`~openerp.base.DataSet.unlink` in order to remove the records
 entirely.
 
-.. note:: the list-wise deletion button (next to the record addition button)
-          simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
-          obtaining all selected record ids, but it is possible to override it
-          alone by replacing
-          :js:func:`~openerp.base.ListView.do_delete_selected`.
+.. note::
+
+ the list-wise deletion button (next to the record addition button)
+  simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
+  obtaining all selected record ids, but it is possible to override it
+  alone by replacing
+  :js:func:`~openerp.base.ListView.do_delete_selected`.
 
 Internal API Doc
 ----------------