[MERGE] forward port of branch 8.0 up to ed1c173
authorChristophe Simonis <chs@odoo.com>
Fri, 5 Sep 2014 13:28:22 +0000 (15:28 +0200)
committerChristophe Simonis <chs@odoo.com>
Fri, 5 Sep 2014 13:28:22 +0000 (15:28 +0200)
112 files changed:
1  2 
addons/account/__openerp__.py
addons/account/account.py
addons/account/account_invoice.py
addons/account/account_view.xml
addons/account/report/account_analytic_entries_report.py
addons/account/report/account_analytic_entries_report_view.xml
addons/account/res_config.py
addons/account/res_config_view.xml
addons/account_voucher/account_voucher.py
addons/analytic/__openerp__.py
addons/analytic/models/analytic.py
addons/calendar/calendar.py
addons/calendar/calendar_view.xml
addons/crm/__openerp__.py
addons/crm/crm_lead.py
addons/crm/crm_lead_view.xml
addons/event/__openerp__.py
addons/fleet/fleet_view.xml
addons/hr/hr.py
addons/hr_attendance/hr_attendance_view.xml
addons/hr_contract/hr_contract.py
addons/hr_holidays/hr_holidays.py
addons/hr_holidays/hr_holidays_view.xml
addons/hr_recruitment/hr_recruitment.py
addons/hw_scale/__openerp__.py
addons/l10n_be_coda/__openerp__.py
addons/l10n_be_coda/wizard/account_coda_import.py
addons/mail/__openerp__.py
addons/mail/mail_group.py
addons/mail/mail_mail.py
addons/mail/static/src/css/mail.css
addons/mail/tests/test_mail_features.py
addons/mass_mailing/views/website_mass_mailing.xml
addons/mrp/mrp.py
addons/mrp/mrp_view.xml
addons/point_of_sale/controllers/main.py
addons/point_of_sale/point_of_sale.py
addons/point_of_sale/point_of_sale_view.xml
addons/point_of_sale/report/pos_order_report.py
addons/point_of_sale/res_partner_view.xml
addons/point_of_sale/static/src/css/pos.css
addons/point_of_sale/static/src/js/models.js
addons/point_of_sale/static/src/js/screens.js
addons/point_of_sale/static/src/js/widget_base.js
addons/point_of_sale/static/src/js/widgets.js
addons/point_of_sale/static/src/xml/pos.xml
addons/portal_sale/portal_sale_data.xml
addons/product/product_view.xml
addons/project/project.py
addons/project_issue/project_issue.py
addons/purchase/edi/purchase_order_action_data.xml
addons/purchase/purchase.py
addons/purchase/purchase_view.xml
addons/restaurant/__openerp__.py
addons/sale/edi/sale_order_action_data.xml
addons/sale/res_partner_view.xml
addons/sale/sale.py
addons/sale/sale_view.xml
addons/stock/static/src/js/widgets.js
addons/stock/stock.py
addons/theme_bootswatch/views/theme.xml
addons/web/controllers/main.py
addons/web/static/src/css/base.css
addons/web/static/src/css/base.sass
addons/web/static/src/js/chrome.js
addons/web/static/src/js/core.js
addons/web/static/src/js/data_export.js
addons/web/static/src/js/tour.js
addons/web/static/src/js/view_form.js
addons/web/static/src/js/view_list.js
addons/web/static/src/js/views.js
addons/web/static/src/xml/base.xml
addons/web_kanban/static/src/js/kanban.js
addons/website/controllers/main.py
addons/website/models/ir_http.py
addons/website/models/ir_ui_view.py
addons/website/static/src/css/website.css
addons/website/static/src/css/website.sass
addons/website/static/src/js/website.editor.js
addons/website/static/src/js/website.snippets.editor.js
addons/website/views/themes.xml
addons/website/views/website_templates.xml
addons/website_blog/views/website_blog_templates.xml
addons/website_customer/views/website_customer.xml
addons/website_event/views/website_event.xml
addons/website_event_track/views/website_event.xml
addons/website_forum/models/forum.py
addons/website_forum/views/website_forum.xml
addons/website_hr_recruitment/views/templates.xml
addons/website_membership/views/website_membership.xml
addons/website_partner/models/res_partner.py
addons/website_quote/__openerp__.py
addons/website_quote/models/order.py
addons/website_quote/views/website_quotation.xml
addons/website_quote/views/website_quotation_backend.xml
addons/website_sale/__openerp__.py
addons/website_sale/data/demo.xml
addons/website_sale/views/templates.xml
openerp/addons/base/ir/ir_qweb.py
openerp/addons/base/ir/ir_ui_view.py
openerp/addons/base/res/res_partner.py
openerp/addons/base/res/res_partner_view.xml
openerp/addons/base/tests/test_mail_examples.py
openerp/addons/base/tests/test_views.py
openerp/models.py
openerp/modules/module.py
openerp/release.py
openerp/tools/config.py
openerp/tools/convert.py
openerp/tools/func.py
openerp/tools/mail.py
openerp/tools/misc.py

@@@ -47,7 -47,7 +47,7 @@@ Creates a dashboard for accountants tha
  Processes like maintaining general ledgers are done through the defined Financial Journals (entry move line or grouping is maintained through a journal) 
  for a particular financial year and for preparation of vouchers there is a module named account_voucher.
      """,
-     'website': 'http://www.openerp.com',
+     'website': 'https://www.odoo.com/page/billing',
      'images' : ['images/accounts.jpeg','images/bank_statement.jpeg','images/cash_register.jpeg','images/chart_of_accounts.jpeg','images/customer_invoice.jpeg','images/journal_entries.jpeg'],
      'depends' : ['base_setup', 'product', 'analytic', 'board', 'edi', 'report'],
      'data': [
          'wizard/account_automatic_reconcile_view.xml',
          'wizard/account_financial_report_view.xml',
          'wizard/pos_box.xml',
 -        'project/wizard/project_account_analytic_line_view.xml',
          'account_end_fy.xml',
          'account_invoice_view.xml',
          'data/account_data.xml',
          'data/data_account_type.xml',
          'data/configurable_account_chart.xml',
          'account_invoice_workflow.xml',
          'partner_view.xml',
          'product_view.xml',
          'account_assert_test.xml',
 +        'account_analytic_view.xml',
          'ir_sequence_view.xml',
          'company_view.xml',
          'edi/invoice_action_data.xml',
          'account_bank_view.xml',
          'res_config_view.xml',
 +        'account_tip_data.xml',
          'account_pre_install.yml',
          'views/report_vat.xml',
          'views/report_invoice.xml',
          'views/report_partnerledgerother.xml',
          'views/report_financial.xml',
          'views/report_generalledger.xml',
 -        'project/views/report_analyticbalance.xml',
 -        'project/views/report_analyticjournal.xml',
 -        'project/views/report_analyticcostledgerquantity.xml',
 -        'project/views/report_analyticcostledger.xml',
 -        'project/views/report_invertedanalyticbalance.xml',
          'views/account.xml',
      ],
      'qweb' : [
      ],
      'demo': [
          'demo/account_demo.xml',
 -        'project/project_demo.xml',
 -        'project/analytic_account_demo.xml',
          'demo/account_minimal.xml',
          'demo/account_invoice_demo.xml',
          'demo/account_bank_statement.xml',
@@@ -55,6 -55,19 +55,19 @@@ def check_cycle(self, cr, uid, ids, con
          level -= 1
      return True
  
+ class res_company(osv.osv):
+     _inherit = "res.company"
+     _columns = {
+         'income_currency_exchange_account_id': fields.many2one(
+             'account.account',
+             string="Gain Exchange Rate Account",
+             domain="[('type', '=', 'other')]",),
+         'expense_currency_exchange_account_id': fields.many2one(
+             'account.account',
+             string="Loss Exchange Rate Account",
+             domain="[('type', '=', 'other')]",),
+     }
  class account_payment_term(osv.osv):
      _name = "account.payment.term"
      _description = "Payment Term"
@@@ -738,7 -751,6 +751,7 @@@ class account_journal(osv.osv)
          'loss_account_id' : fields.many2one('account.account', 'Loss Account'),
          'internal_account_id' : fields.many2one('account.account', 'Internal Transfers Account', select=1),
          'cash_control' : fields.boolean('Cash Control', help='If you want the journal should be control at opening/closing, check this option'),
 +        'analytic_journal_id':fields.many2one('account.analytic.journal','Analytic Journal', help="Journal for analytic entries"),
      }
  
      _defaults = {
@@@ -1648,7 -1660,8 +1661,8 @@@ class account_move_reconcile(osv.osv)
          if not total:
              self.pool.get('account.move.line').write(cr, uid,
                  map(lambda x: x.id, rec.line_partial_ids),
-                 {'reconcile_id': rec.id }
+                 {'reconcile_id': rec.id },
+                 context=context
              )
          return True
  
@@@ -83,7 -83,7 +83,7 @@@ class account_invoice(models.Model)
          return journal.currency or journal.company_id.currency_id
  
      @api.model
-     @api.returns('account.analytic.journal')
+     @api.returns('account.analytic.journal', lambda r: r.id)
      def _get_journal_analytic(self, inv_type):
          """ Return the analytic journal corresponding to the given invoice type. """
          journal_type = TYPE2JOURNAL.get(inv_type, 'sale')
          if not journal:
              raise except_orm(_('No Analytic Journal!'),
                  _("You must define an analytic journal of type '%s'!") % (journal_type,))
-         return journal
+         return journal[0]
  
      @api.one
      @api.depends('account_id', 'move_id.line_id.account_id', 'move_id.line_id.reconcile_id')
      def _compute_reconciled(self):
          self.reconciled = self.test_paid()
-         if not self.reconciled and self.state == 'paid':
-             self.signal_workflow('open_test')
  
      @api.model
      def _get_reference_type(self):
      def _compute_residual(self):
          nb_inv_in_partial_rec = max_invoice_id = 0
          self.residual = 0.0
-         for line in self.move_id.line_id:
+         for line in self.sudo().move_id.line_id:
              if line.account_id.type in ('receivable', 'payable'):
                  if line.currency_id == self.currency_id:
                      self.residual += line.amount_residual_currency
      def _compute_payments(self):
          partial_lines = lines = self.env['account.move.line']
          for line in self.move_id.line_id:
+             if line.account_id != self.account_id:
+                 continue
              if line.reconcile_id:
                  lines |= line.reconcile_id.line_id
              elif line.reconcile_partial_id:
      @api.multi
      def onchange_payment_term_date_invoice(self, payment_term_id, date_invoice):
          if not date_invoice:
-             date_invoice = fields.Date.today()
+             date_invoice = fields.Date.context_today(self)
          if not payment_term_id:
              # To make sure the invoice due date should contain due date which is
              # entered by user when there is no payment term defined
              if 'journal_id' in journal_defaults:
                  values['journal_id'] = journal_defaults['journal_id']
              if not values.get('journal_id'):
-                 field_desc = journals.fields_get(['journal_id'])
-                 type_label = next(t for t, label in field_desc['journal_id']['selection'] if t == journal_type)
+                 field_desc = journals.fields_get(['type'])
+                 type_label = next(t for t, label in field_desc['type']['selection'] if t == journal_type)
                  action = self.env.ref('account.action_account_journal_form')
                  msg = _('Cannot find any account journal of type "%s" for this company, You should create one.\n Please go to Journal Configuration') % type_label
                  raise RedirectWarning(msg, action.id, _('Go to the configuration panel'))
                  invoice.check_total = invoice.amount_total
          return True
  
-     @staticmethod
-     def _convert_ref(ref):
-         return (ref or '').replace('/','')
      @api.multi
      def _get_analytic_lines(self):
          """ Return a list of dict for creating analytic lines for self[0] """
                  if self.type in ('in_invoice', 'in_refund'):
                      ref = self.reference
                  else:
-                     ref = self._convert_ref(self.number)
+                     ref = self.number
                  if not self.journal_id.analytic_journal_id:
                      raise except_orm(_('No Analytic Journal!'),
                          _("You have to define an analytic journal on the '%s' journal!") % (self.journal_id.name,))
              for tax in self.tax_line:
                  if tax.manual:
                      continue
 -                key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id, tax.account_analytic_id.id)
 +                key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id)
                  tax_key.append(key)
                  if key not in compute_taxes:
                      raise except_orm(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !'))
          total_currency = 0
          for line in invoice_move_lines:
              if self.currency_id != company_currency:
-                 currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.today())
+                 currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.context_today(self))
                  line['currency_id'] = currency.id
                  line['amount_currency'] = line['price']
                  line['price'] = currency.compute(line['price'], company_currency)
              inv.check_tax_lines(compute_taxes)
  
              # I disabled the check_total feature
-             group_check_total = self.env.ref('account.group_supplier_inv_check_total')
-             if self.env.user in group_check_total.users:
+             if self.env['res.users'].has_group('account.group_supplier_inv_check_total'):
                  if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding / 2.0):
                      raise except_orm(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.'))
  
              if inv.type in ('in_invoice', 'in_refund'):
                  ref = inv.reference
              else:
-                 ref = self._convert_ref(inv.number)
+                 ref = inv.number
  
              diff_currency = inv.currency_id != company_currency
              # create one move line for the total and possibly adjust the other lines amount
                  'ref': inv.reference or inv.name,
                  'line_id': line,
                  'journal_id': journal.id,
-                 'date': date,
+                 'date': inv.date_invoice,
                  'narration': inv.comment,
                  'company_id': inv.company_id.id,
              }
  
              if inv.type in ('in_invoice', 'in_refund'):
                  if not inv.reference:
-                     ref = self._convert_ref(inv.number)
+                     ref = inv.number
                  else:
                      ref = inv.reference
              else:
-                 ref = self._convert_ref(inv.number)
+                 ref = inv.number
  
              self._cr.execute(""" UPDATE account_move SET ref=%s
                             WHERE id=%s AND (ref IS NULL OR ref = '')""",
          values['journal_id'] = journal.id
  
          values['type'] = TYPE2REFUND[invoice['type']]
-         values['date_invoice'] = date or fields.Date.today()
+         values['date_invoice'] = date or fields.Date.context_today(invoice)
          values['state'] = 'draft'
          values['number'] = False
  
          SIGN = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1}
          direction = SIGN[self.type]
          # take the chosen date
-         date = self._context.get('date_p') or fields.Date.today()
+         date = self._context.get('date_p') or fields.Date.context_today(self)
  
          # Take the amount in currency and the currency of the payment
          if self._context.get('amount_currency') and self._context.get('currency_id'):
          if self.type in ('in_invoice', 'in_refund'):
              ref = self.reference
          else:
-             ref = self._convert_ref(self.number)
+             ref = self.number
          partner = self.partner_id._find_accounting_partner(self.partner_id)
          name = name or self.invoice_line.name or self.number
          # Pay attention to the sign for both debit/credit AND amount_currency
@@@ -1397,8 -1392,7 +1392,7 @@@ class account_invoice_line(models.Model
          res = []
          for line in inv.invoice_line:
              mres = self.move_line_get_item(line)
-             if not mres:
-                 continue
+             mres['invl_id'] = line.id
              res.append(mres)
              tax_code_found = False
              taxes = line.invoice_line_tax_id.compute_all(
@@@ -1507,7 -1501,7 +1501,7 @@@ class account_invoice_tax(models.Model)
          company = self.env['res.company'].browse(company_id)
          if currency_id and company.currency_id:
              currency = self.env['res.currency'].browse(currency_id)
-             currency = currency.with_context(date=date_invoice or fields.Date.today())
+             currency = currency.with_context(date=date_invoice or fields.Date.context_today(self))
              base = currency.compute(base * factor, company.currency_id, round=False)
          return {'value': {'base_amount': base}}
  
          company = self.env['res.company'].browse(company_id)
          if currency_id and company.currency_id:
              currency = self.env['res.currency'].browse(currency_id)
-             currency = currency.with_context(date=date_invoice or fields.Date.today())
+             currency = currency.with_context(date=date_invoice or fields.Date.context_today(self))
              amount = currency.compute(amount * factor, company.currency_id, round=False)
          return {'value': {'tax_amount': amount}}
  
      @api.v8
      def compute(self, invoice):
          tax_grouped = {}
-         currency = invoice.currency_id.with_context(date=invoice.date_invoice or fields.Date.today())
+         currency = invoice.currency_id.with_context(date=invoice.date_invoice or fields.Date.context_today(invoice))
          company_currency = invoice.company_id.currency_id
          for line in invoice.invoice_line:
              taxes = line.invoice_line_tax_id.compute_all(
                      val['account_id'] = tax['account_paid_id'] or line.account_id.id
                      val['account_analytic_id'] = tax['account_analytic_paid_id']
  
 -                key = (val['tax_code_id'], val['base_code_id'], val['account_id'], val['account_analytic_id'])
 +                # If the taxes generate moves on the same financial account as the invoice line
 +                # and no default analytic account is defined at the tax level, propagate the
 +                # analytic account from the invoice line to the tax line. This is necessary
 +                # in situations were (part of) the taxes cannot be reclaimed,
 +                # to ensure the tax move is allocated to the proper analytic account.
 +                if not val.get('account_analytic_id') and line.account_analytic_id and val['account_id'] == line.account_id.id:
 +                    val['account_analytic_id'] = line.account_analytic_id.id
 +
 +                key = (val['tax_code_id'], val['base_code_id'], val['account_id'])
                  if not key in tax_grouped:
                      tax_grouped[key] = val
                  else:
                          <button name="action_draft" states="done" string="Re-Open Period" type="object" groups="account.group_account_manager"/>
                          <field name="state" widget="statusbar" nolabel="1"/>
                      </header>
 -                    <sheet>
 +                    <group>
                          <group>
 -                            <group>
 -                                <field name="name"/>
 -                                <field name="fiscalyear_id" widget="selection"/>
 -                                <label for="date_start" string="Duration"/>
 -                                <div>
 -                                    <field name="date_start" class="oe_inline" nolabel="1"/> -
 -                                    <field name="date_stop" nolabel="1" class="oe_inline"/>
 -                                </div>
 -                            </group>
 -                            <group>
 -                                <field name="code"/>
 -                                <field name="special"/>
 -                                <field name="company_id" widget="selection" groups="base.group_multi_company"/>
 -                            </group>
 +                            <field name="name"/>
 +                            <field name="fiscalyear_id" widget="selection"/>
 +                            <label for="date_start" string="Duration"/>
 +                            <div>
 +                                <field name="date_start" class="oe_inline" nolabel="1"/> -
 +                                <field name="date_stop" nolabel="1" class="oe_inline"/>
 +                            </div>
                          </group>
 -                    </sheet>
 +                        <group>
 +                            <field name="code"/>
 +                            <field name="special"/>
 +                            <field name="company_id" widget="selection" groups="base.group_multi_company"/>
 +                        </group>
 +                    </group>
                  </form>
              </field>
          </record>
          <record id="action_bank_reconcile_bank_statements" model="ir.actions.client">
              <field name="name">Reconciliation on Bank Statements</field>
              <field name="tag">bank_statement_reconciliation_view</field>
-             <field name="context">{'statement_id': active_id}</field>
+             <field name="context">{'statement_ids': [active_id]}</field>
          </record>
          
          <record id="view_account_bank_statement_filter" model="ir.ui.view">
                  <form string="Payment Term">
                      <group>
                          <group string="Amount Computation">
 -                            <field name="value"/>
 +                            <field name="value" widget="radio" nolabel="1" colspan="4"/>
                              <label for="value_amount" string="Amount To Pay"  attrs="{'invisible':[('value','=','balance')]}"/>
                              <div attrs="{'invisible':[('value','=','balance')]}">
                                  <field name="value_amount" class="oe_inline"/>
@@@ -33,15 -33,15 +33,15 @@@ class analytic_entries_report(osv.osv)
          'partner_id': fields.many2one('res.partner', 'Partner'),
          'company_id': fields.many2one('res.company', 'Company', required=True),
          'currency_id': fields.many2one('res.currency', 'Currency', required=True),
 -        'account_id': fields.many2one('account.analytic.account', 'Account', required=False),
 -        'general_account_id': fields.many2one('account.account', 'General Account', required=True),
 +        'account_id': fields.many2one('account.analytic.account', 'Analytic Account', required=False),
 +        'general_account_id': fields.many2one('account.account', 'Financial Account', required=True),
          'journal_id': fields.many2one('account.analytic.journal', 'Journal', required=True),
          'move_id': fields.many2one('account.move.line', 'Move', required=True),
          'product_id': fields.many2one('product.product', 'Product', required=True),
          'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
          'amount': fields.float('Amount', readonly=True),
-         'unit_amount': fields.float('Quantity', readonly=True),
-         'nbr': fields.integer('#Entries', readonly=True),
+         'unit_amount': fields.integer('Unit Amount', readonly=True),
+         'nbr': fields.integer('# Entries', readonly=True),  # TDE FIXME master: rename into nbr_entries
      }
      def init(self, cr):
          tools.drop_view_if_exists(cr, 'analytic_entries_report')
@@@ -7,20 -7,18 +7,18 @@@
          <field name="arch" type="xml">
              <search string="Analytic Entries Analysis">
                  <field name="date"/>
-                 <filter string="My Entries" icon="terp-personal" domain="[('user_id','=',uid)]" help="My Entries"/>
+                 <filter string="My Entries" domain="[('user_id','=',uid)]" help="My Entries"/>
                  <field name="account_id" groups="analytic.group_analytic_accounting"/>
                  <field name="product_id" />
                  <field name="user_id"/>
                  <group expand="0" string="Group By">
-                     <filter string="User" name="User" icon="terp-personal" context="{'group_by':'user_id'}"/>
-                     <filter string="Partner" icon="terp-partner" context="{'group_by':'partner_id'}"/>
-                     <filter string="Analytic Account" name="Account" icon="terp-folder-green" context="{'group_by':'account_id'}" groups="analytic.group_analytic_accounting"/>
-                     <filter string="Financial Account" icon="terp-folder-orange" context="{'group_by':'general_account_id'}"/>
-                     <filter string="Journal" icon="terp-folder-orange" context="{'group_by':'journal_id'}"/>
-                     <filter string="Product" icon="terp-accessories-archiver" context="{'group_by':'product_id'}"/>
-                     <filter string="Product Unit of Measure" icon="terp-mrp" context="{'group_by':'product_uom_id'}"/>
-                     <filter string="Company" icon="terp-go-home" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
-                     <filter string="Date" name="Month" icon="terp-go-month" context="{'group_by':'date:month'}"/>
+                     <filter string="Partner" context="{'group_by':'partner_id'}"/>
 -                    <filter string="Account" name="Account" context="{'group_by':'account_id'}" groups="analytic.group_analytic_accounting"/>
 -                    <filter string="General Account" context="{'group_by':'general_account_id'}"/>
++                    <filter string="Analytic Account" name="Account" context="{'group_by':'account_id'}" groups="analytic.group_analytic_accounting"/>
++                    <filter string="Financial Account" context="{'group_by':'general_account_id'}"/>
+                     <filter string="Journal" context="{'group_by':'journal_id'}"/>
+                     <filter string="Company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
+                     <separator/>
+                     <filter string="Month" name="Month" context="{'group_by':'date:month'}"/>
                  </group>
              </search>
          </field>
@@@ -30,7 -28,8 +28,8 @@@
           <field name="model">analytic.entries.report</field>
           <field name="arch" type="xml">
               <graph string="Analytic Entries Analysis" type="pivot">
-                 <field name="user_id" type="row"/>
+                 <field name="account_id" type="row"/>
+                 <field name="date" interval="month" type="col"/>
                  <field name="nbr" type="measure"/>
                  <field name="unit_amount" type="measure"/>
                  <field name="amount" type="measure"/>
@@@ -42,8 -41,8 +41,8 @@@
          <field name="res_model">analytic.entries.report</field>
          <field name="view_type">form</field>
          <field name="view_mode">graph</field>
-         <field name="context">{'search_default_year':1,'search_default_month':1, 'group_by_no_leaf':1, 'search_default_Account':1, 'search_default_Month':1, 'group_by':[]}</field>
          <field name="search_view_id" ref="view_analytic_entries_report_search"/>
+         <field name="context">{'group_by_no_leaf':1, 'search_default_Account':1, 'group_by':[]}</field>
          <field name="help">From this view, have an analysis of your different analytic entries following the analytic account you defined matching your business need. Use the tool search to analyse information about analytic entries generated in the system.</field>
      </record>
  
@@@ -51,8 -51,8 +51,8 @@@ class account_config_settings(osv.osv_m
          'code_digits': fields.integer('# of Digits', help="No. of digits to use for account code"),
          'tax_calculation_rounding_method': fields.related('company_id',
              'tax_calculation_rounding_method', type='selection', selection=[
 -            ('round_per_line', 'Round per line'),
 -            ('round_globally', 'Round globally'),
 +            ('round_per_line', 'Round calculation of taxes per line'),
 +            ('round_globally', 'Round globally calculation of taxes '),
              ], string='Tax calculation rounding method',
              help="If you select 'Round per line' : for each tax, the tax amount will first be computed and rounded for each PO/SO/invoice line and then these rounded amounts will be summed, leading to the total amount for that tax. If you select 'Round globally': for each tax, the tax amount will be computed for each PO/SO/invoice line, then these amounts will be summed and eventually this total tax amount will be rounded. If you sell with tax included, you should choose 'Round per line' because you certainly want the sum of your tax-included line subtotals to be equal to the total amount with taxes."),
          'sale_tax': fields.many2one("account.tax.template", "Default sale tax"),
          'module_product_email_template': fields.boolean('Send products tools and information at the invoice confirmation',
              help='With this module, link your products to a template to send complete information and tools to your customer.\n'
                   'For instance when invoicing a training, the training agenda and materials will automatically be send to your customers.'),
 +        'module_account_bank_statement_import_ofx': fields.boolean('Import of Bank Statements in .OFX Format',
 +            help='Get your bank statements from you bank and import them in Odoo in .OFX format.\n'
 +                '-that installs the module account_bank_statement_import.'),
 +        'module_account_bank_statement_import_qif': fields.boolean('Import of Bank Statements in .QIF Format.',
 +            help='Get your bank statements from you bank and import them in Odoo in .QIF format.\n'
 +                '-that installs the module account_bank_statement_import_qif.'),
          'group_proforma_invoices': fields.boolean('Allow pro-forma invoices',
              implied_group='account.group_proforma_invoices',
              help="Allows you to put invoices in pro-forma state."),
              help="Allows you to use the analytic accounting."),
          'group_check_supplier_invoice_total': fields.boolean('Check the total of supplier invoices', 
              implied_group="account.group_supplier_inv_check_total"),
+         'income_currency_exchange_account_id': fields.related(
+             'company_id', 'income_currency_exchange_account_id',
+             type='many2one',
+             relation='account.account',
+             string="Gain Exchange Rate Account", 
+             domain="[('type', '=', 'other')]"),
+         'expense_currency_exchange_account_id': fields.related(
+             'company_id', 'expense_currency_exchange_account_id',
+             type="many2one",
+             relation='account.account',
+             string="Loss Exchange Rate Account",
+             domain="[('type', '=', 'other')]"),
      }
+     def onchange_company_id(self, cr, uid, ids, company_id, context=None):
+         res = super(account_config_settings, self).onchange_company_id(cr, uid, ids, company_id, context=context)
+         if company_id:
+             company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
+             res['value'].update({'income_currency_exchange_account_id': company.income_currency_exchange_account_id and company.income_currency_exchange_account_id.id or False, 
+                                  'expense_currency_exchange_account_id': company.expense_currency_exchange_account_id and company.expense_currency_exchange_account_id.id or False})
+         else: 
+             res['value'].update({'income_currency_exchange_account_id': False, 
+                                  'expense_currency_exchange_account_id': False})
+         return res
  
      def _default_company(self, cr, uid, context=None):
          user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
                                  <field name="decimal_precision" class="oe_inline"/>
                              </div>
                              <div>
 -                                <label for="tax_calculation_rounding_method"/>
 -                                <field name="tax_calculation_rounding_method" class="oe_inline"/>
 +                                <field name="tax_calculation_rounding_method" widget="radio" class="oe_inline" />
                              </div>
                          </div>
                          <label for="id" string="Features"/>
                              <div name="group_multi_currency">
                                  <field name="group_multi_currency" class="oe_inline" on_change="onchange_multi_currency(group_multi_currency)"/>
                                  <label for="group_multi_currency"/>
+                                 <group attrs="{'invisible': [('group_multi_currency', '&lt;&gt;', True)]}" col="2">
+                                     <group>
+                                         <field name="income_currency_exchange_account_id"/>
+                                         <field name="expense_currency_exchange_account_id"/>
+                                     </group>
+                                     <group>
+                                     </group>
+                                 </group>
                              </div>
                              <div>
                                  <field name="module_account_accountant" class="oe_inline"/>
                                  <field name="paypal_account" placeholder="e.g. sales@odoo.com" class="oe_inline"/>
                              </div>
                          </div>
 +                        <label for="id" string="Bank Statements"/>
 +                        <div>
 +                            <div>
 +                                <field name="module_account_bank_statement_import_ofx" class="oe_inline"/>
 +                                <label for="module_account_bank_statement_import_ofx"/>
 +                            </div>
 +                            <div>
 +                                <field name="module_account_bank_statement_import_qif" class="oe_inline"/>
 +                                <label for="module_account_bank_statement_import_qif"/>
 +                            </div>
 +                        </div>
                      </group>
                      <separator name="analytic_account" string="Analytic Accounting" invisible="1"/>
                      <group name="analytic_account_sale" invisible="1">
@@@ -41,47 -41,6 +41,6 @@@ class res_currency(osv.osv)
          return res
  
  
- class res_company(osv.osv):
-     _inherit = "res.company"
-     _columns = {
-         'income_currency_exchange_account_id': fields.many2one(
-             'account.account',
-             string="Gain Exchange Rate Account",
-             domain="[('type', '=', 'other')]",),
-         'expense_currency_exchange_account_id': fields.many2one(
-             'account.account',
-             string="Loss Exchange Rate Account",
-             domain="[('type', '=', 'other')]",),
-     }
- class account_config_settings(osv.osv_memory):
-     _inherit = 'account.config.settings'
-     _columns = {
-         'income_currency_exchange_account_id': fields.related(
-             'company_id', 'income_currency_exchange_account_id',
-             type='many2one',
-             relation='account.account',
-             string="Gain Exchange Rate Account", 
-             domain="[('type', '=', 'other')]"),
-         'expense_currency_exchange_account_id': fields.related(
-             'company_id', 'expense_currency_exchange_account_id',
-             type="many2one",
-             relation='account.account',
-             string="Loss Exchange Rate Account",
-             domain="[('type', '=', 'other')]"),
-     }
-     def onchange_company_id(self, cr, uid, ids, company_id, context=None):
-         res = super(account_config_settings, self).onchange_company_id(cr, uid, ids, company_id, context=context)
-         if company_id:
-             company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
-             res['value'].update({'income_currency_exchange_account_id': company.income_currency_exchange_account_id and company.income_currency_exchange_account_id.id or False, 
-                                  'expense_currency_exchange_account_id': company.expense_currency_exchange_account_id and company.expense_currency_exchange_account_id.id or False})
-         else: 
-             res['value'].update({'income_currency_exchange_account_id': False, 
-                                  'expense_currency_exchange_account_id': False})
-         return res
  class account_voucher(osv.osv):
      def _check_paid(self, cr, uid, ids, name, args, context=None):
          res = {}
                          \n* The \'Posted\' status is used when user create voucher,a voucher number is generated and voucher entries are created in account \
                          \n* The \'Cancelled\' status is used when user cancel voucher.'),
          'amount': fields.float('Total', digits_compute=dp.get_precision('Account'), required=True, readonly=True, states={'draft':[('readonly',False)]}),
-         'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}),
+         'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True),
          'reference': fields.char('Ref #', readonly=True, states={'draft':[('readonly',False)]},
                                   help="Transaction reference number.", copy=False),
          'number': fields.char('Number', readonly=True, copy=False),
          'pre_line':fields.boolean('Previous Payments ?', required=False),
          'date_due': fields.date('Due Date', readonly=True, select=True, states={'draft':[('readonly',False)]}),
          'payment_option':fields.selection([
 -                                           ('without_writeoff', 'Keep Open'),
 -                                           ('with_writeoff', 'Reconcile Payment Balance'),
 +                                           ('without_writeoff', 'Keep it open'),
 +                                           ('with_writeoff', 'Reconcile payment balance'),
                                             ], 'Payment Difference', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="This field helps you to choose what you want to do with the eventual difference between the paid amount and the sum of allocated amounts. You can either choose to keep open this difference on the partner's account, or reconcile it with the payment(s)"),
          'writeoff_acc_id': fields.many2one('account.account', 'Counterpart Account', readonly=True, states={'draft': [('readonly', False)]}),
          'comment': fields.char('Counterpart Comment', required=True, readonly=True, states={'draft': [('readonly', False)]}),
                  'period_id': voucher.period_id.id,
                  'partner_id': voucher.partner_id.id,
                  'currency_id': company_currency <> current_currency and  current_currency or False,
-                 'amount_currency': company_currency <> current_currency and sign * voucher.amount or 0.0,
+                 'amount_currency': (sign * abs(voucher.amount) # amount < 0 for refunds
+                     if company_currency != current_currency else 0.0),
                  'date': voucher.date,
                  'date_maturity': voucher.date_due
              }
              if line.amount == line.amount_unreconciled:
                  if not line.move_line_id:
                      raise osv.except_osv(_('Wrong voucher line'),_("The invoice you are willing to pay is not valid anymore."))
-                 sign = voucher.type in ('payment', 'purchase') and -1 or 1
+                 sign = line.type =='dr' and -1 or 1
                  currency_rate_difference = sign * (line.move_line_id.amount_residual - amount)
              else:
                  currency_rate_difference = 0.0
                          # otherwise we use the rates of the system
                          amount_currency = currency_obj.compute(cr, uid, company_currency, line.move_line_id.currency_id.id, move_line['debit']-move_line['credit'], context=ctx)
                  if line.amount == line.amount_unreconciled:
-                     sign = voucher.type in ('payment', 'purchase') and -1 or 1
-                     foreign_currency_diff = sign * line.move_line_id.amount_residual_currency + amount_currency
+                     foreign_currency_diff = line.move_line_id.amount_residual_currency - abs(amount_currency)
  
              move_line['amount_currency'] = amount_currency
              voucher_line = move_line_obj.create(cr, uid, move_line)
              if voucher.payment_option == 'with_writeoff':
                  account_id = voucher.writeoff_acc_id.id
                  write_off_name = voucher.comment
-             elif voucher.type in ('sale', 'receipt'):
-                 account_id = voucher.partner_id.property_account_receivable.id
+             elif voucher.partner_id:
+                 if voucher.type in ('sale', 'receipt'):
+                     account_id = voucher.partner_id.property_account_receivable.id
+                 else:
+                     account_id = voucher.partner_id.property_account_payable.id
              else:
-                 account_id = voucher.partner_id.property_account_payable.id
+                 # fallback on account of voucher
+                 account_id = voucher.account_id.id
              sign = voucher.type == 'payment' and -1 or 1
              move_line = {
                  'name': write_off_name or name,
@@@ -23,7 -23,7 +23,7 @@@
      'name' : 'Analytic Accounting',
      'version': '1.1',
      'author' : 'OpenERP SA',
-     'website' : 'http://www.openerp.com',
+     'website' : 'https://www.odoo.com/page/accounting',
      'category': 'Hidden/Dependency',
      'depends' : ['base', 'decimal_precision', 'mail'],
      'description': """
@@@ -37,26 -37,11 +37,26 @@@ that have no counterpart in the genera
      'data': [
          'security/analytic_security.xml',
          'security/ir.model.access.csv',
 -        'analytic_sequence.xml',
 -        'analytic_view.xml',
 -        'analytic_data.xml',
 +        'data/analytic_sequence.xml',
 +        'views/analytic_view.xml',
 +        'data/analytic_data.xml',
 +        'analytic_report.xml',
 +        'wizard/account_analytic_balance_report_view.xml',
 +        'wizard/account_analytic_cost_ledger_view.xml',
 +        'wizard/account_analytic_inverted_balance_report.xml',
 +        'wizard/account_analytic_journal_report_view.xml',
 +        'wizard/account_analytic_cost_ledger_for_journal_report_view.xml',
 +        'wizard/account_analytic_chart_view.xml',
 +        'views/report_analyticbalance.xml',
 +        'views/report_analyticjournal.xml',
 +        'views/report_analyticcostledgerquantity.xml',
 +        'views/report_analyticcostledger.xml',
 +        'views/report_invertedanalyticbalance.xml',
 +    ],
 +    'demo': [
 +        'data/analytic_demo.xml',
 +        'data/analytic_account_demo.xml',
      ],
 -    'demo': [],
      'installable': True,
      'auto_install': False,
  }
index 340a37f,0000000..4661a3f
mode 100644,000000..100644
--- /dev/null
@@@ -1,377 -1,0 +1,377 @@@
 +# -*- coding: utf-8 -*-
 +##############################################################################
 +#
 +#    OpenERP, Open Source Management Solution
 +#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
 +#
 +#    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 time
 +from datetime import datetime
 +
 +from openerp.osv import fields, osv
 +from openerp import tools
 +from openerp.tools.translate import _
 +import openerp.addons.decimal_precision as dp
 +
 +class account_analytic_account(osv.osv):
 +    _name = 'account.analytic.account'
 +    _inherit = ['mail.thread']
 +    _description = 'Analytic Account'
 +    _track = {
 +        'state': {
 +            'analytic.mt_account_pending': lambda self, cr, uid, obj, ctx=None: obj.state == 'pending',
 +            'analytic.mt_account_closed': lambda self, cr, uid, obj, ctx=None: obj.state == 'close',
 +            'analytic.mt_account_opened': lambda self, cr, uid, obj, ctx=None: obj.state == 'open',
 +        },
 +    }
 +
 +    def _compute_level_tree(self, cr, uid, ids, child_ids, res, field_names, context=None):
 +        currency_obj = self.pool.get('res.currency')
 +        recres = {}
 +        def recursive_computation(account):
 +            result2 = res[account.id].copy()
 +            for son in account.child_ids:
 +                result = recursive_computation(son)
 +                for field in field_names:
 +                    if (account.currency_id.id != son.currency_id.id) and (field!='quantity'):
 +                        result[field] = currency_obj.compute(cr, uid, son.currency_id.id, account.currency_id.id, result[field], context=context)
 +                    result2[field] += result[field]
 +            return result2
 +        for account in self.browse(cr, uid, ids, context=context):
 +            if account.id not in child_ids:
 +                continue
 +            recres[account.id] = recursive_computation(account)
 +        return recres
 +
 +    def _debit_credit_bal_qtty(self, cr, uid, ids, fields, arg, context=None):
 +        res = {}
 +        if context is None:
 +            context = {}
 +        child_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
 +        for i in child_ids:
 +            res[i] =  {}
 +            for n in fields:
 +                res[i][n] = 0.0
 +
 +        if not child_ids:
 +            return res
 +
 +        where_date = ''
 +        where_clause_args = [tuple(child_ids)]
 +        if context.get('from_date', False):
 +            where_date += " AND l.date >= %s"
 +            where_clause_args  += [context['from_date']]
 +        if context.get('to_date', False):
 +            where_date += " AND l.date <= %s"
 +            where_clause_args += [context['to_date']]
 +        cr.execute("""
 +              SELECT a.id,
 +                     sum(
 +                         CASE WHEN l.amount > 0
 +                         THEN l.amount
 +                         ELSE 0.0
 +                         END
 +                          ) as debit,
 +                     sum(
 +                         CASE WHEN l.amount < 0
 +                         THEN -l.amount
 +                         ELSE 0.0
 +                         END
 +                          ) as credit,
 +                     COALESCE(SUM(l.amount),0) AS balance,
 +                     COALESCE(SUM(l.unit_amount),0) AS quantity
 +              FROM account_analytic_account a
 +                  LEFT JOIN account_analytic_line l ON (a.id = l.account_id)
 +              WHERE a.id IN %s
 +              """ + where_date + """
 +              GROUP BY a.id""", where_clause_args)
 +        for row in cr.dictfetchall():
 +            res[row['id']] = {}
 +            for field in fields:
 +                res[row['id']][field] = row[field]
 +        return self._compute_level_tree(cr, uid, ids, child_ids, res, fields, context)
 +
 +    def name_get(self, cr, uid, ids, context=None):
 +        res = []
 +        if not ids:
 +            return res
 +        if isinstance(ids, (int, long)):
 +            ids = [ids]
 +        for id in ids:
 +            elmt = self.browse(cr, uid, id, context=context)
 +            res.append((id, self._get_one_full_name(elmt)))
 +        return res
 +
 +    def _get_full_name(self, cr, uid, ids, name=None, args=None, context=None):
 +        if context == None:
 +            context = {}
 +        res = {}
 +        for elmt in self.browse(cr, uid, ids, context=context):
 +            res[elmt.id] = self._get_one_full_name(elmt)
 +        return res
 +
 +    def _get_one_full_name(self, elmt, level=6):
 +        if level<=0:
 +            return '...'
 +        if elmt.parent_id and not elmt.type == 'template':
 +            parent_path = self._get_one_full_name(elmt.parent_id, level-1) + " / "
 +        else:
 +            parent_path = ''
 +        return parent_path + elmt.name
 +
 +    def _child_compute(self, cr, uid, ids, name, arg, context=None):
 +        result = {}
 +        if context is None:
 +            context = {}
 +
 +        for account in self.browse(cr, uid, ids, context=context):
 +            result[account.id] = map(lambda x: x.id, [child for child in account.child_ids if child.state != 'template'])
 +
 +        return result
 +
 +    def _get_analytic_account(self, cr, uid, ids, context=None):
 +        company_obj = self.pool.get('res.company')
 +        analytic_obj = self.pool.get('account.analytic.account')
 +        accounts = []
 +        for company in company_obj.browse(cr, uid, ids, context=context):
 +            accounts += analytic_obj.search(cr, uid, [('company_id', '=', company.id)])
 +        return accounts
 +
 +    def _set_company_currency(self, cr, uid, ids, name, value, arg, context=None):
 +        if isinstance(ids, (int, long)):
 +            ids=[ids]
 +        for account in self.browse(cr, uid, ids, context=context):
 +            if account.company_id:
 +                if account.company_id.currency_id.id != value:
 +                    raise osv.except_osv(_('Error!'), _("If you set a company, the currency selected has to be the same as it's currency. \nYou can remove the company belonging, and thus change the currency, only on analytic account of type 'view'. This can be really useful for consolidation purposes of several companies charts with different currencies, for example."))
 +        if value:
 +            cr.execute("""update account_analytic_account set currency_id=%s where id=%s""", (value, account.id))
 +            self.invalidate_cache(cr, uid, ['currency_id'], [account.id], context=context)
 +
 +    def _currency(self, cr, uid, ids, field_name, arg, context=None):
 +        result = {}
 +        for rec in self.browse(cr, uid, ids, context=context):
 +            if rec.company_id:
 +                result[rec.id] = rec.company_id.currency_id.id
 +            else:
 +                result[rec.id] = rec.currency_id.id
 +        return result
 +
 +    _columns = {
 +        'name': fields.char('Account/Contract Name', required=True, track_visibility='onchange'),
 +        'complete_name': fields.function(_get_full_name, type='char', string='Full Name'),
 +        'code': fields.char('Reference', select=True, track_visibility='onchange', copy=False),
 +        'type': fields.selection([('view','Analytic View'), ('normal','Analytic Account'),('contract','Contract or Project'),('template','Template of Contract')], 'Type of Account', required=True,
 +                                 help="If you select the View Type, it means you won\'t allow to create journal entries using that account.\n"\
 +                                  "The type 'Analytic account' stands for usual accounts that you only want to use in accounting.\n"\
 +                                  "If you select Contract or Project, it offers you the possibility to manage the validity and the invoicing options for this account.\n"\
 +                                  "The special type 'Template of Contract' allows you to define a template with default data that you can reuse easily."),
 +        'template_id': fields.many2one('account.analytic.account', 'Template of Contract'),
 +        'description': fields.text('Description'),
 +        'parent_id': fields.many2one('account.analytic.account', 'Parent Analytic Account', select=2),
 +        'child_ids': fields.one2many('account.analytic.account', 'parent_id', 'Child Accounts'),
 +        'child_complete_ids': fields.function(_child_compute, relation='account.analytic.account', string="Account Hierarchy", type='many2many'),
 +        'line_ids': fields.one2many('account.analytic.line', 'account_id', 'Analytic Entries'),
 +        'balance': fields.function(_debit_credit_bal_qtty, type='float', string='Balance', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')),
 +        'debit': fields.function(_debit_credit_bal_qtty, type='float', string='Debit', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')),
 +        'credit': fields.function(_debit_credit_bal_qtty, type='float', string='Credit', multi='debit_credit_bal_qtty', digits_compute=dp.get_precision('Account')),
 +        'quantity': fields.function(_debit_credit_bal_qtty, type='float', string='Quantity', multi='debit_credit_bal_qtty'),
 +        'quantity_max': fields.float('Prepaid Service Units', help='Sets the higher limit of time to work on the contract, based on the timesheet. (for instance, number of hours in a limited support contract.)'),
 +        'partner_id': fields.many2one('res.partner', 'Customer'),
 +        'user_id': fields.many2one('res.users', 'Project Manager', track_visibility='onchange'),
 +        'manager_id': fields.many2one('res.users', 'Account Manager', track_visibility='onchange'),
 +        'date_start': fields.date('Start Date'),
 +        'date': fields.date('Expiration Date', select=True, track_visibility='onchange'),
 +        'company_id': fields.many2one('res.company', 'Company', required=False), #not required because we want to allow different companies to use the same chart of account, except for leaf accounts.
 +        'state': fields.selection([('template', 'Template'),
 +                                   ('draft','New'),
 +                                   ('open','In Progress'),
 +                                   ('pending','To Renew'),
 +                                   ('close','Closed'),
 +                                   ('cancelled', 'Cancelled')],
 +                                  'Status', required=True,
 +                                  track_visibility='onchange', copy=False),
 +        'currency_id': fields.function(_currency, fnct_inv=_set_company_currency, #the currency_id field is readonly except if it's a view account and if there is no company
 +            store = {
 +                'res.company': (_get_analytic_account, ['currency_id'], 10),
 +            }, string='Currency', type='many2one', relation='res.currency'),
 +    }
 +
 +    def on_change_template(self, cr, uid, ids, template_id, date_start=False, context=None):
 +        if not template_id:
 +            return {}
 +        res = {'value':{}}
 +        template = self.browse(cr, uid, template_id, context=context)
 +        if template.date_start and template.date:
 +            from_dt = datetime.strptime(template.date_start, tools.DEFAULT_SERVER_DATE_FORMAT)
 +            to_dt = datetime.strptime(template.date, tools.DEFAULT_SERVER_DATE_FORMAT)
 +            timedelta = to_dt - from_dt
 +            res['value']['date'] = datetime.strftime(datetime.now() + timedelta, tools.DEFAULT_SERVER_DATE_FORMAT)
 +        if not date_start:
 +            res['value']['date_start'] = fields.date.today()
 +        res['value']['quantity_max'] = template.quantity_max
 +        res['value']['parent_id'] = template.parent_id and template.parent_id.id or False
 +        res['value']['description'] = template.description
 +        return res
 +
 +    def on_change_partner_id(self, cr, uid, ids,partner_id, name, context=None):
 +        res={}
 +        if partner_id:
 +            partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
 +            if partner.user_id:
 +                res['manager_id'] = partner.user_id.id
 +            if not name:
 +                res['name'] = _('Contract: ') + partner.name
 +        return {'value': res}
 +
 +    def _default_company(self, cr, uid, context=None):
 +        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
 +        if user.company_id:
 +            return user.company_id.id
 +        return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0]
 +
 +    def _get_default_currency(self, cr, uid, context=None):
 +        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
 +        return user.company_id.currency_id.id
 +
 +    _defaults = {
 +        'type': 'normal',
 +        'company_id': _default_company,
 +        'code' : lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'account.analytic.account'),
 +        'state': 'open',
 +        'user_id': lambda self, cr, uid, ctx: uid,
 +        'partner_id': lambda self, cr, uid, ctx: ctx.get('partner_id', False),
 +        'date_start': lambda *a: time.strftime('%Y-%m-%d'),
 +        'currency_id': _get_default_currency,
 +    }
 +
 +    def check_recursion(self, cr, uid, ids, context=None, parent=None):
 +        return super(account_analytic_account, self)._check_recursion(cr, uid, ids, context=context, parent=parent)
 +
 +    _order = 'code, name asc'
 +    _constraints = [
 +        (check_recursion, 'Error! You cannot create recursive analytic accounts.', ['parent_id']),
 +    ]
 +
 +    def name_create(self, cr, uid, name, context=None):
 +        raise osv.except_osv(_('Warning'), _("Quick account creation disallowed."))
 +
 +    def copy(self, cr, uid, id, default=None, context=None):
 +        if not default:
 +            default = {}
 +        analytic = self.browse(cr, uid, id, context=context)
 +        default['name'] = _("%s (copy)") % analytic['name']
 +        return super(account_analytic_account, self).copy(cr, uid, id, default, context=context)
 +
 +    def on_change_company(self, cr, uid, id, company_id):
 +        if not company_id:
 +            return {}
 +        currency = self.pool.get('res.company').read(cr, uid, [company_id], ['currency_id'])[0]['currency_id']
 +        return {'value': {'currency_id': currency}}
 +
 +    def on_change_parent(self, cr, uid, id, parent_id):
 +        if not parent_id:
 +            return {}
 +        parent = self.read(cr, uid, [parent_id], ['partner_id','code'])[0]
 +        if parent['partner_id']:
 +            partner = parent['partner_id'][0]
 +        else:
 +            partner = False
 +        res = {'value': {}}
 +        if partner:
 +            res['value']['partner_id'] = partner
 +        return res
 +
 +    def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
 +        if not args:
 +            args=[]
 +        if context is None:
 +            context={}
 +        if name:
 +            account_ids = self.search(cr, uid, [('code', '=', name)] + args, limit=limit, context=context)
 +            if not account_ids:
 +                dom = []
 +                for name2 in name.split('/'):
 +                    name = name2.strip()
-                     account_ids = self.search(cr, uid, dom + [('name', 'ilike', name)] + args, limit=limit, context=context)
++                    account_ids = self.search(cr, uid, dom + [('name', operator, name)] + args, limit=limit, context=context)
 +                    if not account_ids: break
 +                    dom = [('parent_id','in',account_ids)]
 +        else:
 +            account_ids = self.search(cr, uid, args, limit=limit, context=context)
 +        return self.name_get(cr, uid, account_ids, context=context)
 +
 +class account_analytic_line(osv.osv):
 +    _name = 'account.analytic.line'
 +    _description = 'Analytic Line'
 +
 +    _columns = {
 +        'name': fields.char('Description', required=True),
 +        'date': fields.date('Date', required=True, select=True),
 +        'amount': fields.float('Amount', required=True, help='Calculated by multiplying the quantity and the price given in the Product\'s cost price. Always expressed in the company main currency.', digits_compute=dp.get_precision('Account')),
 +        'unit_amount': fields.float('Quantity', help='Specifies the amount of quantity to count.'),
 +        'account_id': fields.many2one('account.analytic.account', 'Analytic Account', required=True, ondelete='restrict', select=True, domain=[('type','<>','view')]),
 +        'user_id': fields.many2one('res.users', 'User'),
 +        'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
 +        'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal', required=True, ondelete='restrict', select=True),
 +
 +    }
 +
 +    def _get_default_date(self, cr, uid, context=None):
 +        return fields.date.context_today(self, cr, uid, context=context)
 +
 +    def __get_default_date(self, cr, uid, context=None):
 +        return self._get_default_date(cr, uid, context=context)
 +
 +    _defaults = {
 +        'date': __get_default_date,
 +        'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context=c),
 +        'amount': 0.00
 +    }
 +
 +    _order = 'date desc'
 +
 +    def _check_no_view(self, cr, uid, ids, context=None):
 +        analytic_lines = self.browse(cr, uid, ids, context=context)
 +        for line in analytic_lines:
 +            if line.account_id.type == 'view':
 +                return False
 +        return True
 +
 +    _constraints = [
 +        (_check_no_view, 'You cannot create analytic line on view account.', ['account_id']),
 +    ]
 +
 +
 +class account_analytic_journal(osv.osv):
 +    _name = 'account.analytic.journal'
 +    _description = 'Analytic Journal'
 +    _columns = {
 +        'name': fields.char('Journal Name', required=True),
 +        'code': fields.char('Journal Code', size=8),
 +        'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the analytic journal without removing it."),
 +        'type': fields.selection(
 +            [('sale', 'Sale'), ('purchase', 'Purchase'), ('cash', 'Cash'),
 +             ('general', 'General'), ('situation', 'Situation')],
 +            'Type', required=True, help="Gives the type of the analytic journal. When it needs for a document (eg: an invoice) to create analytic entries, Odoo will look for a matching journal of the same type."),
 +        'line_ids': fields.one2many('account.analytic.line', 'journal_id', 'Lines'),
 +        'company_id': fields.many2one('res.company', 'Company', required=True),
 +    }
 +    _defaults = {
 +        'active': True,
 +        'type': 'general',
 +        'company_id': lambda self, cr, uid, c=None: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id,
 +    }
@@@ -416,11 -416,12 +416,12 @@@ class calendar_alarm_manager(osv.Abstra
          return res
  
      def get_next_mail(self, cr, uid, context=None):
-         cron = self.pool.get('ir.cron').search(cr, uid, [('model', 'ilike', self._name)], context=context)
-         if cron and len(cron) == 1:
-             cron = self.pool.get('ir.cron').browse(cr, uid, cron[0], context=context)
-         else:
-             _logger.exception("Cron for " + self._name + " can not be identified !")
+         try:
+             cron = self.pool['ir.model.data'].get_object(
+                 cr, uid, 'calendar', 'ir_cron_scheduler_alarm', context=context)
+         except ValueError:
+             _logger.error("Cron for " + self._name + " can not be identified !")
+             return False
  
          if cron.interval_type == "weeks":
              cron_interval = cron.interval_number * 7 * 24 * 60 * 60
              cron_interval = cron.interval_number * 60
          elif cron.interval_type == "seconds":
              cron_interval = cron.interval_number
+         else:
+             cron_interval = False
  
          if not cron_interval:
-             _logger.exception("Cron delay can not be computed !")
+             _logger.error("Cron delay can not be computed !")
+             return False
  
          all_events = self.get_next_potential_limit_alarm(cr, uid, cron_interval, notif=False, context=context)
  
@@@ -559,6 -563,35 +563,35 @@@ class calendar_alarm(osv.Model)
          'interval': 'hours',
      }
  
+     def _update_cron(self, cr, uid, context=None):
+         try:
+             cron = self.pool['ir.model.data'].get_object(
+                 cr, uid, 'calendar', 'ir_cron_scheduler_alarm', context=context)
+         except ValueError:
+             return False
+         return cron.toggle(model=self._name, domain=[('type', '=', 'email')])
+     def create(self, cr, uid, values, context=None):
+         res = super(calendar_alarm, self).create(cr, uid, values, context=context)
+         self._update_cron(cr, uid, context=context)
+         return res
+     def write(self, cr, uid, ids, values, context=None):
+         res = super(calendar_alarm, self).write(cr, uid, ids, values, context=context)
+         self._update_cron(cr, uid, context=context)
+         return res
+     def unlink(self, cr, uid, ids, context=None):
+         res = super(calendar_alarm, self).unlink(cr, uid, ids, context=context)
+         self._update_cron(cr, uid, context=context)
+         return res
  
  class ir_values(osv.Model):
      _inherit = 'ir.values'
@@@ -748,23 -781,23 +781,23 @@@ class calendar_event(osv.Model)
          res = {}
          if not isinstance(fields, list):
              fields = [fields]
-         for meeting_id in ids:
-             res[meeting_id] = {}
-             attendee = self._find_my_attendee(cr, uid, [meeting_id], context)
-             meeting = self.browse(cr, uid, [meeting_id], context=context)[0]
+         for meeting in self.browse(cr, uid, ids, context=context):
+             meeting_data = {}
+             res[meeting.id] = meeting_data
+             attendee = self._find_my_attendee(cr, uid, [meeting.id], context)
              for field in fields:
                  if field == 'is_attendee':
-                     res[meeting_id][field] = True if attendee else False
+                     meeting_data[field] = bool(attendee)
                  elif field == 'attendee_status':
-                     res[meeting_id][field] = attendee.state if attendee else 'needsAction'
+                     meeting_data[field] = attendee.state if attendee else 'needsAction'
                  elif field == 'display_time':
-                     res[meeting_id][field] = self._get_display_time(cr, uid, meeting.start, meeting.stop, meeting.duration, meeting.allday, context=context)
+                     meeting_data[field] = self._get_display_time(cr, uid, meeting.start, meeting.stop, meeting.duration, meeting.allday, context=context)
                  elif field == "display_start":
-                     res[meeting_id][field] = meeting.start_date if meeting.allday else meeting.start_datetime
+                     meeting_data[field] = meeting.start_date if meeting.allday else meeting.start_datetime
                  elif field == 'start':
-                     res[meeting_id][field] = meeting.start_date if meeting.allday else meeting.start_datetime
+                     meeting_data[field] = meeting.start_date if meeting.allday else meeting.start_datetime
                  elif field == 'stop':
-                     res[meeting_id][field] = meeting.stop_date if meeting.allday else meeting.stop_datetime
+                     meeting_data[field] = meeting.stop_date if meeting.allday else meeting.stop_datetime
          return res
  
      def _get_rulestring(self, cr, uid, ids, name, arg, context=None):
          if not isinstance(ids, list):
              ids = [ids]
  
-         for id in ids:
-             #read these fields as SUPERUSER because if the record is private a normal search could return False and raise an error
-             data = self.browse(cr, SUPERUSER_ID, id, context=context)
-             if data.interval and data.interval < 0:
-                 raise osv.except_osv(_('Warning!'), _('Interval cannot be negative.'))
-             if data.count and data.count <= 0:
-                 raise osv.except_osv(_('Warning!'), _('Count cannot be negative or 0.'))
-             data = self.read(cr, uid, id, ['id', 'byday', 'recurrency', 'final_date', 'rrule_type', 'month_by', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'week_list'], context=context)
-             event = data['id']
-             if data['recurrency']:
-                 result[event] = self.compute_rule_string(data)
+         #read these fields as SUPERUSER because if the record is private a normal search could raise an error
+         events = self.read(cr, SUPERUSER_ID, ids,
+                            ['id', 'byday', 'recurrency', 'final_date', 'rrule_type', 'month_by',
+                             'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa',
+                             'su', 'day', 'week_list'], context=context)
+         for event in events:
+             if event['recurrency']:
+                 result[event['id']] = self.compute_rule_string(event)
              else:
-                 result[event] = ""
+                 result[event['id']] = ''
          return result
  
      # retro compatibility function
          'is_attendee': fields.function(_compute, string='Attendee', type="boolean", multi='attendee'),
          'attendee_status': fields.function(_compute, string='Attendee Status', type="selection", selection=calendar_attendee.STATE_SELECTION, multi='attendee'),
          'display_time': fields.function(_compute, string='Event Time', type="char", multi='attendee'),
-         'display_start': fields.function(_compute, string='Date', type="char", multi='display_start', store=True),
+         'display_start': fields.function(_compute, string='Date', type="char", multi='attendee', store=True),
          'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
-         'start': fields.function(_compute, string='Calculated start', type="datetime", multi='start', store=True, required=True),
-         'stop': fields.function(_compute, string='Calculated stop', type="datetime", multi='stop', store=True, required=True),
+         'start': fields.function(_compute, string='Calculated start', type="datetime", multi='attendee', store=True, required=True),
+         'stop': fields.function(_compute, string='Calculated stop', type="datetime", multi='attendee', store=True, required=True),
          'start_date': fields.date('Start Date', states={'done': [('readonly', True)]}, track_visibility='onchange'),
          'start_datetime': fields.datetime('Start DateTime', states={'done': [('readonly', True)]}, track_visibility='onchange'),
          'stop_date': fields.date('End Date', states={'done': [('readonly', True)]}, track_visibility='onchange'),
          'stop_datetime': fields.datetime('End Datetime', states={'done': [('readonly', True)]}, track_visibility='onchange'),  # old date_deadline
          'duration': fields.float('Duration', states={'done': [('readonly', True)]}),
          'description': fields.text('Description', states={'done': [('readonly', True)]}),
 -        'class': fields.selection([('public', 'Public'), ('private', 'Private'), ('confidential', 'Public for Employees')], 'Privacy', states={'done': [('readonly', True)]}),
 +        'class': fields.selection([('public', 'Everyone'), ('private', 'Only me'), ('confidential', 'Only internal users')], 'Privacy', states={'done': [('readonly', True)]}),
          'location': fields.char('Location', help="Location of Event", track_visibility='onchange', states={'done': [('readonly', True)]}),
          'show_as': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Show Time as', states={'done': [('readonly', True)]}),
  
          'partner_ids': fields.many2many('res.partner', 'calendar_event_res_partner_rel', string='Attendees', states={'done': [('readonly', True)]}),
          'alarm_ids': fields.many2many('calendar.alarm', 'calendar_alarm_calendar_event_rel', string='Reminders', ondelete="restrict", copy=False),
      }
+     def _get_default_partners(self, cr, uid, ctx=None):
+         ret = [self.pool['res.users'].browse(cr, uid, uid, context=ctx).partner_id.id]
+         active_id = ctx.get('active_id')
+         if ctx.get('active_model') == 'res.partner' and active_id:
+             if active_id not in ret:
+                 ret.append(active_id)
+         return ret
      _defaults = {
          'end_type': 'count',
          'count': 1,
          'interval': 1,
          'active': 1,
          'user_id': lambda self, cr, uid, ctx: uid,
-         'partner_ids': lambda self, cr, uid, ctx: [self.pool['res.users'].browse(cr, uid, [uid], context=ctx)[0].partner_id.id]
+         'partner_ids': _get_default_partners,
      }
  
      def _check_closing_date(self, cr, uid, ids, context=None):
          @param data: dictionary of freq and interval value
          @return: string containing recurring rule (empty if no rule)
          """
+         if data['interval'] and data['interval'] < 0:
+             raise osv.except_osv(_('warning!'), _('interval cannot be negative.'))
+         if data['count'] and data['count'] <= 0:
+             raise osv.except_osv(_('warning!'), _('count cannot be negative or 0.'))
          def get_week_string(freq, data):
              weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
              if freq == 'weekly':
              select = [ids]
          else:
              select = ids
-         # FIXME: find a better way to not push virtual ids in the cache
-         # (leading to their prefetching and ultimately a type error when
-         # postgres tries to convert '14-3489274297' to an integer)
-         self.invalidate_cache(cr, uid, context=context)
          select = map(lambda x: (x, calendar_id2real_id(x)), select)
          result = []
          real_data = super(calendar_event, self).read(cr, uid, [real_id for calendar_id, real_id in select], fields=fields2, context=context, load=load)
                      res['start_datetime'] = ls[1]
                      res['stop_datetime'] = ls[2]
  
-                 res['display_time'] = self._get_display_time(cr, uid, ls[1], ls[2], res['duration'], res['allday'], context=context)
+                 if 'display_time' in fields:
+                     res['display_time'] = self._get_display_time(cr, uid, ls[1], ls[2], res['duration'], res['allday'], context=context)
  
              res['id'] = calendar_id
              result.append(res)
@@@ -41,7 -41,7 +41,7 @@@
                          <h1>
                              <field name="name"/>
                          </h1>
-                         <label for="partner_ids" class="oe_edit_only"/>
+                         <label for="partner_ids" string="Attendees" class="oe_edit_only"/>
                          <h2>
                              <field name="partner_ids" widget="many2manyattendee"
                                  context="{'force_email':True}"
                                      </group>
                                  </group>
                                  <group>
 -                                    <field name="class"/>
 +                                    <field name="class" widget="radio"/>
                                      <field name="show_as"/>
                                      <field name="rrule" invisible="1" readonly="0" />                                    
                                      <field name="recurrent_id" invisible="1" />
@@@ -47,7 -47,7 +47,7 @@@ Dashboard for CRM will include
  * Opportunities by Stage (graph)
  """,
      'author': 'OpenERP SA',
-     'website': 'http://www.openerp.com',
+     'website': 'https://www.odoo.com/page/crm',
      'depends': [
          'base_action_rule',
          'base_setup',
@@@ -63,7 -63,6 +63,7 @@@
          'crm_data.xml',
          'crm_lead_data.xml',
          'crm_phonecall_data.xml',
 +        'crm_tip_data.xml',
  
          'security/crm_security.xml',
          'security/ir.model.access.csv',
@@@ -75,7 -74,7 +75,7 @@@
          'wizard/crm_merge_opportunities_view.xml',
  
          'crm_view.xml',
-         
          'crm_phonecall_view.xml',
          'crm_phonecall_menu.xml',
  
@@@ -85,6 -84,7 +85,7 @@@
          'calendar_event_menu.xml',
  
          'report/crm_lead_report_view.xml',
+         'report/crm_opportunity_report_view.xml',
          'report/crm_phonecall_report_view.xml',
  
          'res_partner_view.xml',
diff --combined addons/crm/crm_lead.py
@@@ -68,7 -68,7 +68,7 @@@ class crm_lead(format_address, osv.osv)
      """ CRM Lead Case """
      _name = "crm.lead"
      _description = "Lead/Opportunity"
-     _order = "priority,date_action,id desc"
+     _order = "priority desc,date_action,id desc"
      _inherit = ['mail.thread', 'ir.needaction_mixin', 'crm.tracking.mixin']
  
      _track = {
          context['empty_list_help_document_name'] = _("leads")
          return super(crm_lead, self).get_empty_list_help(cr, uid, help, context=context)
  
-     def _get_default_section_id(self, cr, uid, context=None):
+     def _get_default_section_id(self, cr, uid, user_id=False, context=None):
          """ Gives default section by checking if present in the context """
          section_id = self._resolve_section_id_from_context(cr, uid, context=context) or False
          if not section_id:
-             section_id = self.pool.get('res.users').browse(cr, uid, uid, context).default_section_id.id or False
+             section_id = self.pool.get('res.users').browse(cr, uid, user_id or uid, context).default_section_id.id or False
          return section_id
  
      def _get_default_stage_id(self, cr, uid, context=None):
              select=True, help="Linked partner (optional). Usually created when converting the lead."),
  
          'id': fields.integer('ID', readonly=True),
 -        'name': fields.char('Subject', required=True, select=1),
 +        'name': fields.char('Opportunity', required=True, select=1),
          'active': fields.boolean('Active', required=False),
          'date_action_last': fields.datetime('Last Action', readonly=1),
          'date_action_next': fields.datetime('Next Action', readonly=1),
          'user_id': fields.many2one('res.users', 'Salesperson', select=True, track_visibility='onchange'),
          'referred': fields.char('Referred By'),
          'date_open': fields.datetime('Assigned', readonly=True),
-         'day_open': fields.function(_compute_day, string='Days to Open', \
-                                 multi='day_open', type="float", store=True),
-         'day_close': fields.function(_compute_day, string='Days to Close', \
-                                 multi='day_open', type="float", store=True),
+         'day_open': fields.function(_compute_day, string='Days to Assign',
+                                     multi='day_open', type="float",
+                                     store={'crm.lead': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
+         'day_close': fields.function(_compute_day, string='Days to Close',
+                                      multi='day_open', type="float",
+                                      store={'crm.lead': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
          'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
  
          # Messaging and marketing
          'type': 'lead',
          'user_id': lambda s, cr, uid, c: uid,
          'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
-         'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
+         'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, context=c),
          'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
          'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
          'color': 0,
      def on_change_user(self, cr, uid, ids, user_id, context=None):
          """ When changing the user, also set a section_id or restrict section id
              to the ones user_id is member of. """
-         section_id = self._get_default_section_id(cr, uid, context=context) or False
+         section_id = self._get_default_section_id(cr, uid, user_id=user_id, context=context) or False
          if user_id and not section_id:
              section_ids = self.pool.get('crm.case.section').search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', '=', user_id)], context=context)
              if section_ids:
                  values = {'res_id': opportunity_id,}
                  for attachment_in_first in first_attachments:
                      if attachment.name == attachment_in_first.name:
-                         name = "%s (%s)" % (attachment.name, count,),
+                         values['name'] = "%s (%s)" % (attachment.name, count,),
                  count+=1
                  attachment.write(values)
          return True
              context['default_type'] = vals.get('type')
          if vals.get('section_id') and not context.get('default_section_id'):
              context['default_section_id'] = vals.get('section_id')
+         if vals.get('user_id'):
+             vals['date_open'] = fields.datetime.now()
  
          # context: no_log, because subtype already handle this
          create_context = dict(context, mail_create_nolog=True)
          # stage change: update date_last_stage_update
          if 'stage_id' in vals:
              vals['date_last_stage_update'] = fields.datetime.now()
-         # stage change with new stage: update probability
+         if vals.get('user_id'):
+             vals['date_open'] = fields.datetime.now()
+         # stage change with new stage: update probability and date_closed
          if vals.get('stage_id') and not vals.get('probability'):
              onchange_stage_values = self.onchange_stage_id(cr, uid, ids, vals.get('stage_id'), context=context)['value']
              vals.update(onchange_stage_values)
                  </header>
                  <sheet>
                      <div class="oe_right oe_button_box" name="buttons">
-                         <button class="oe_inline oe_stat_button" type="action" 
+                         <button class="oe_inline oe_stat_button" type="action"
                              context="{'default_opportunity_id': active_id, 'search_default_opportunity_id': active_id, 'default_partner_id': partner_id, 'default_duration': 1.0}"
                              name="%(crm.crm_case_categ_phone_incoming0)d" icon="fa-phone">
 -                            <div>Schedule/Log<br/>Calls</div>
 +                            <div>Calls</div>
                          </button>
                      </div>
                      <div class="oe_title">
                          <filter string="Campaign"  domain="[]" context="{'group_by':'campaign_id'}"/>
                          <filter string="Source"  domain="[]" context="{'group_by':'source_id'}"/>
                          <filter string="Channel"  domain="[]" context="{'group_by':'medium_id'}"/>
-                         <filter string="Expected Closing" domain="[]" context="{'group_by':'date_deadline:week'}"/>
+                         <filter string="Creation Month" domain="[]" context="{'group_by':'create_date'}"/>
                          <filter string="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
                      </group>
                  </search>
                              domain="['&amp;', ('section_ids', '=', section_id), '|', ('type', '=', type), ('type', '=', 'both')]"/>
                      </header>
                      <sheet>
 -                        <div class="oe_right oe_button_box">
 +                        <div name="buttons" class="oe_right oe_button_box">
-                             <button class="oe_inline oe_stat_button" type="action" 
+                             <button class="oe_inline oe_stat_button" type="action"
                                  name="%(crm.crm_case_categ_phone_incoming0)d" icon="fa-phone"
                                  context="{'default_opportunity_id': active_id, 'search_default_opportunity_id': active_id, 'default_partner_id': partner_id, 'default_duration': 1.0}">
 -                                <div>Schedule/Log<br/>Calls</div>
 +                                <div>Calls</div>
                              </button>
-                             <button class="oe_inline oe_stat_button" type="object" 
+                             <button class="oe_inline oe_stat_button" type="object"
                                  context="{'partner_id': partner_id}"
                                  name="action_schedule_meeting" icon="fa-calendar">
                                  <field string="Meetings" name="meeting_count" widget="statinfo"/>
                          </div>
                          <div class="oe_title">
                              <label for="name" class="oe_edit_only"/>
 -                            <h1><field name="name"/></h1>
 +                            <h1><field name="name" placeholder="e.g. Product Pricing"/></h1>
                              <label for="planned_revenue" class="oe_edit_only"/>
                              <h2>
                                  <field name="company_currency" invisible="1"/>
              </field>
          </record>
  
          <!-- Opportunities Search View -->
          <record id="view_crm_case_opportunities_filter" model="ir.ui.view">
              <field name="name">CRM - Opportunities Search</field>
                      <field name="stage_id" domain="[]"/>
                      <field name="probability"/>
                      <separator/>
+                     <filter string="New" name="new"
+                             domain="[('probability', '=', 0), ('stage_id.sequence', '&lt;=', 1)]"/>
                      <filter string="Won" name="won"
                              domain="[('probability', '=', 100), ('stage_id.fold', '=', True)]"/>
                      <filter string="Lost" name="lost"
                      <separator/>
                      <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
                      <group expand="0" string="Group By" colspan="16">
-                         <filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
-                         <filter string="Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
-                         <filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
-                         <filter string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
-                         <filter string="Expected Closing" domain="[]" context="{'group_by':'date_deadline'}"/>
-                         <filter string="Last Message" name="group_message_last_post" domain="[]" context="{'group_by':'message_last_post:week'}"/>
+                         <filter string="Salesperson" context="{'group_by':'user_id'}"/>
+                         <filter string="Sales Team" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
+                         <filter string="Country" context="{'group_by':'country_id'}" />
+                         <filter string="Company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
+                         <filter string="Stage" context="{'group_by':'stage_id'}"/>
+                         <filter string="Campaign" domain="[]" context="{'group_by':'campaign_id'}"/>
+                         <filter string="Channel" domain="[]" context="{'group_by':'medium_id'}"/>
+                         <filter string="Source" domain="[]" context="{'group_by':'source_id'}"/>
+                         <separator orientation="vertical" />
+                         <filter string="Creation Month" context="{'group_by':'create_date:month'}" name="month"/>
+                         <filter string="Expected Closing" context="{'group_by':'date_deadline'}"/>
                      </group>
                  </search>
              </field>
  #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  #
  ##############################################################################
  {
      'name': 'Events Organisation',
      'version': '0.1',
+     'website' : 'https://www.odoo.com/page/events',
      'category': 'Tools',
      'summary': 'Trainings, Conferences, Meetings, Exhibitions, Registrations',
      'description': """
@@@ -47,7 -46,6 +46,7 @@@ Key Feature
          'event_data.xml',
          'report/report_event_registration_view.xml',
          'res_partner_view.xml',
 +        'res_config_view.xml',
          'email_template.xml',
          'views/event.xml',
      ],
@@@ -6,38 -6,40 +6,38 @@@
              <field name="model">fleet.vehicle.model</field>
              <field name="arch" type="xml">
                  <form string="Model">
 -                    <sheet>
 -                        <field name="image_medium" widget='image' class="oe_left oe_avatar"/>
 -                        <div class="oe_title">
 -                            <label for="modelname" class="oe_edit_only"/>
 -                            <h1>
 -                                <field name="modelname" />
 -                            </h1>
 -                            <label for="brand_id" class="oe_edit_only"/>
 -                            <h2>
 -                                <field name="brand_id" on_change="on_change_brand(brand_id)"/>
 -                            </h2>
 -                        </div>
 -                        <notebook>
 -                            <page string="Vendors">
 -                                <field name="vendors" widget="many2many_kanban">
 -                                    <kanban quick_create="false" create="true">
 -                                        <field name="name"/>
 -                                        <templates>
 -                                            <t t-name="kanban-box">
 -                                                <div style="position: relative">
 -                                                    <a t-if="! read_only_mode" type="delete" style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a>
 -                                                    <div class="oe_module_vignette">
 -                                                        <div class="oe_module_desc">
 -                                                            <field name="name"/>
 -                                                        </div>
 +                    <field name="image_medium" widget='image' class="oe_left oe_avatar"/>
 +                    <div class="oe_title">
 +                        <label for="modelname" class="oe_edit_only"/>
 +                        <h1>
 +                            <field name="modelname" />
 +                        </h1>
 +                        <label for="brand_id" class="oe_edit_only"/>
 +                        <h2>
 +                            <field name="brand_id" on_change="on_change_brand(brand_id)"/>
 +                        </h2>
 +                    </div>
 +                    <notebook>
 +                        <page string="Vendors">
 +                            <field name="vendors" widget="many2many_kanban">
 +                                <kanban quick_create="false" create="true">
 +                                    <field name="name"/>
 +                                    <templates>
 +                                        <t t-name="kanban-box">
 +                                            <div style="position: relative">
 +                                                <a t-if="! read_only_mode" type="delete" style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a>
 +                                                <div class="oe_module_vignette">
 +                                                    <div class="oe_module_desc">
 +                                                        <field name="name"/>
                                                      </div>
                                                  </div>
 -                                            </t>
 -                                        </templates>
 -                                    </kanban>
 -                                </field>
 -                            </page>
 -                        </notebook>
 -                    </sheet>
 +                                            </div>
 +                                        </t>
 +                                    </templates>
 +                                </kanban>
 +                            </field>
 +                        </page>
 +                    </notebook>
                  </form>
              </field>
          </record>
@@@ -60,7 -62,7 +60,7 @@@
                  <search string="Vehicles costs" >
                      <field name="brand_id" />
                      <group expand="1" string="Group By">
 -                        <filter name="groupby_brand" context="{'group_by' : 'brand_id'}" string="Brand"/>
 +                        <filter name="groupby_brand" context="{'group_by' : 'brand_id'}" string="Make"/>
                      </group>
                  </search>
              </field>
                <p class="oe_view_nocontent_create">
                  Click to create a new model.
                </p><p>
 -                You can define several models (e.g. A3, A4) for each brand (Audi).
 +                You can define several models (e.g. A3, A4) for each make (Audi).
                </p>
              </field>
          </record>
-  
          <record model='ir.ui.view' id='fleet_vehicle_model_brand_tree'>
              <field name="name">fleet.vehicle.model.brand.tree</field>
              <field name="model">fleet.vehicle.model.brand</field>
              <field name="arch" type="xml">
 -                <tree string="Model Brand">
 +                <tree string="Model Make">
                      <field name="name" />
                  </tree>
              </field>
              <field name="name">fleet.vehicle.model.brand.form</field>
              <field name="model">fleet.vehicle.model.brand</field>
              <field name="arch" type="xml">
 -                <form string="Model Brand">
 -                    <sheet>
 -                        <group>
 -                            <div>
 -                                <field name="image_medium" widget="image" class="oe_left oe_avatar"/>
 -                                <label for="name" class="oe_edit_only"/>
 -                                <h1>
 -                                    <field name="name" class="oe_inline" />
 -                                </h1>
 -                            </div>
 -                        </group>
 -                    </sheet>
 +                <form string="Model Make">
 +                    <group>
 +                        <div>
 +                            <field name="image_medium" widget="image" class="oe_left oe_avatar"/>
 +                            <label for="name" class="oe_edit_only"/>
 +                            <h1>
 +                                <field name="name" class="oe_inline" />
 +                            </h1>
 +                        </div>
 +                    </group>
                  </form>
              </field>
          </record>
          </record>
  
          <record model='ir.actions.act_window' id='fleet_vehicle_model_brand_act'>
 -            <field name="name">Model brand of Vehicle</field>
 +            <field name="name">Model make of Vehicle</field>
              <field name="res_model">fleet.vehicle.model.brand</field>
              <field name="view_type">form</field>
              <field name="view_mode">kanban,tree,form</field>
              <field name="help" type="html">
                <p class="oe_view_nocontent_create">
 -                Click to create a new brand.
 +                Click to create a new make.
                </p>
              </field>
          </record>
                  <tree string="State" editable="bottom">
                      <field name="sequence" widget="handler" invisible="1"/>
                      <field name="name" />
-                     
                  </tree>
              </field>
          </record>
                      <header>
                          <field name="state_id"  widget="statusbar" clickable="True" />
                      </header>
-                     <sheet> 
-                         <field name="image_medium" widget='image' class="oe_left oe_avatar"/> 
+                     <sheet>
+                         <field name="image_medium" widget='image' class="oe_left oe_avatar"/>
                          <div class="oe_title">
                              <label for="model_id" class="oe_edit_only"/>
                              <h1>
                              <field name="tag_ids" widget="many2many_tags" />
                          </div>
                          <div class="oe_right oe_button_box">
-                             <button name="return_action_to_open" 
+                             <button name="return_action_to_open"
                                  type="object"
                                  class="oe_stat_button"
-                                 icon="fa-book" 
-                                 context="{'xml_id':'fleet_vehicle_log_contract_act'}" 
+                                 icon="fa-book"
+                                 context="{'xml_id':'fleet_vehicle_log_contract_act'}"
                                  help="show the contract for this vehicle">
                                  <field name="contract_count" widget="statinfo" string="Contracts"/>
                              </button>
-                             <button name="act_show_log_cost" 
+                             <button name="act_show_log_cost"
                                  type="object"
                                  class="oe_stat_button"
-                                 icon="fa-usd" 
-                                 help="show all the costs for this vehicle" 
+                                 icon="fa-usd"
+                                 help="show all the costs for this vehicle"
                                  groups="fleet.group_fleet_manager">
                                  <field name="cost_count" widget="statinfo" string="Costs"/>
                              </button>
-                             <button name="return_action_to_open" 
+                             <button name="return_action_to_open"
                                  type="object"
                                  class="oe_stat_button"
-                                 icon="fa-wrench" 
-                                 context="{'xml_id':'fleet_vehicle_log_services_act'}" 
+                                 icon="fa-wrench"
+                                 context="{'xml_id':'fleet_vehicle_log_services_act'}"
                                  help="show the services logs for this vehicle" >
                                  <field name="service_count" widget="statinfo" string="Services"/>
                              </button>
-                             <button name="return_action_to_open" 
+                             <button name="return_action_to_open"
                                  type="object"
                                  class="oe_stat_button"
-                                 icon="fa-flask" 
-                                 context="{'xml_id':'fleet_vehicle_log_fuel_act'}" 
+                                 icon="fa-flask"
+                                 context="{'xml_id':'fleet_vehicle_log_fuel_act'}"
                                  help="show the fuel logs for this vehicle" >
                                  <field name="fuel_logs_count" widget="statinfo" string="Fuel"/>
                              </button>
-                             <button name="return_action_to_open" 
-                                 type="object" 
+                             <button name="return_action_to_open"
+                                 type="object"
                                  class="oe_stat_button"
                                  icon="fa-tachometer"
-                                 context="{'xml_id':'fleet_vehicle_odometer_act'}" 
+                                 context="{'xml_id':'fleet_vehicle_odometer_act'}"
                                  help="show the odometer logs for this vehicle" >
                                  <field name="odometer_count" widget="statinfo" string="Odometer"/>
                              </button>
-                         </div> 
+                         </div>
                          <group col="2" string="General Properties">
                              <group >
                                  <field name="driver_id" />
                      <field name="contract_renewal_overdue" />
                      <field name="contract_renewal_name" />
                      <field name="contract_renewal_total" />
-                     
                      <templates>
                          <t t-name="kanban-box">
                              <div class="oe_kanban_vignette oe_semantic_html_override">
                                              </span>
                                          </a>
                                      </t>
-                                     
                                      <ul>
                                          <li>
                                              <t t-if="record.driver_id.raw_value"><field name="driver_id"/></t>
                                          <li>
                                              <t t-if="record.location.raw_value"><field name="location"/></t>
                                          </li>
-                                         
                                      </ul>
  
                                      <field name="tag_ids"/>
                  </kanban>
              </field>
          </record>
-         
  
          <record model='ir.actions.act_window' id='fleet_vehicle_act'>
              <field name="name">Vehicles</field>
              <field name="view_mode">kanban,tree,form</field>
              <field name="help" type="html">
                <p class="oe_view_nocontent_create">
-                 Click to create a new vehicle. 
+                 Click to create a new vehicle.
                </p><p>
                  You will be able to manage your fleet by keeping track of the
                  contracts, services, fixed and recurring costs, odometers and
                                  <field name="expiration_date" />
                              </group>
                              <group>
-                                 <field name="insurer_id" />
+                                 <field name="insurer_id" context="{'default_supplier': True}"/>
                                  <field name="purchaser_id" />
                                  <field name="ins_ref" />
                              </group>
          </record>
  
          <act_window
-             
              id="act_renew_contract"
              name="Renew Contract"
              res_model="fleet.vehicle.log.contract"
              <field name="view_mode">tree,form,graph</field>
              <field name="help" type="html">
                <p class="oe_view_nocontent_create">
-                 Click to create a new contract. 
+                 Click to create a new contract.
                </p><p>
                  Manage all your contracts (leasing, insurances, etc.) with
                  their related services, costs. Odoo will automatically warn
                  </tree>
              </field>
          </record>
-         
          <record model='ir.ui.view' id='fleet_vehicle_odometer_search'>
              <field name="name">fleet.vehicle.odometer.search</field>
              <field name="model">fleet.vehicle.odometer</field>
             <field name="model">fleet.vehicle.odometer</field>
             <field name="arch" type="xml">
                  <graph string="Odometer Values Per Vehicle">
-                     <field name="vehicle_id" type="row"/> 
+                     <field name="vehicle_id" type="row"/>
                      <field name="value" type="measure"/>
                  </graph>
              </field>
              <field name="context">{"search_default_groupby_vehicle" : True}</field>
              <field name="help" type="html">
                <p class="oe_view_nocontent_create">
-                 Click to create a new odometer log. 
+                 Click to create a new odometer log.
                </p>
                <p>
                  Here you can add various odometer entries for all vehicles.
                                  <field name="date" />
                                  <field name="purchaser_id" />
                                  <field name="inv_ref" />
-                                 <field name="vendor_id" />
+                                 <field name="vendor_id" context="{'default_supplier': True}"/>
                              </group>
                          </group>
                          <group string="Notes">
              <field name="model">fleet.vehicle.log.fuel</field>
              <field name="arch" type="xml">
                  <tree string="Fuel Logs">
-                     <field name="date" /> 
+                     <field name="date" />
                      <field name="vehicle_id" />
                      <field name="odometer" invisible="1"/>
                      <field name="odometer_unit" invisible="1"/>
-                     <field name="purchaser_id" />             
+                     <field name="purchaser_id" />
                      <field name="inv_ref" invisible="1"/>
                      <field name="vendor_id" invisible="1"/>
                      <field name="liter" />
                  </tree>
              </field>
          </record>
-         
          <record model='ir.ui.view' id='fleet_vehicle_log_fuel_search'>
              <field name="name">fleet.vehicle.log.fuel.search</field>
              <field name="model">fleet.vehicle.log.fuel</field>
              <field name="context">{"search_default_groupby_vehicle" : True}</field>
              <field name="help" type="html">
                <p class="oe_view_nocontent_create">
-                 Click to create a new fuel log. 
+                 Click to create a new fuel log.
                </p><p>
                  Here you can add refuelling entries for all vehicles.  You can
                  also filter logs of a particular vehicle using the search
                              <group string="Additional Details">
                                  <field name="date" />
                                  <field name="purchaser_id" />
-                                 <field name="vendor_id" />
+                                 <field name="vendor_id" context="{'default_supplier': True}"/>
                                  <field name="inv_ref" />
                              </group>
                          </group>
             <field name="arch" type="xml">
                  <graph string="Services Costs Per Month" stacked="True">
                      <field name="date" type="row"/>
-                     <field name="vehicle_id" type="row"/> 
+                     <field name="vehicle_id" type="row"/>
                      <field name="cost_amount" type="measure"/>
                  </graph>
              </field>
              <field name="view_mode">tree,form,graph</field>
              <field name="help" type="html">
                <p class="oe_view_nocontent_create">
-                 Click to create a new service entry. 
+                 Click to create a new service entry.
                </p><p>
                  Odoo helps you keeping track of all the services done
                  on your vehicle. Services can be of many type: occasional
              </field>
          </record>
  
-         <record model='ir.ui.view' id='fleet_vehicle_costs_report'>
+         <record model='ir.ui.view' id='fleet_vehicle_effective_costs_report'>
              <field name="name">fleet.vehicle.cost.graph</field>
              <field name="model">fleet.vehicle.cost</field>
              <field name="arch" type="xml">
                  <graph string="Vehicles costs" type="pivot">
-                     <field name="date" interval="year" type="row"/>
                      <field name="vehicle_id" type="row"/>
+                     <field name="cost_type" type="row"/>
+                     <field name="date" interval="year" type="col"/>
+                     <field name="amount" type="measure"/>
+                 </graph>
+             </field>
+         </record>
+         <record model='ir.ui.view' id='fleet_vehicle_indicative_costs_report'>
+             <field name="name">fleet.vehicle.cost.graph</field>
+             <field name="model">fleet.vehicle.cost</field>
+             <field name="arch" type="xml">
+                 <graph string="Vehicles costs" type="pivot">
+                     <field name="parent_id" type="row"/>
                      <field name="cost_type" type="col"/>
-                     <!-- <field name="cost_subtype_id"/> -->
                      <field name="amount" type="measure"/>
                  </graph>
              </field>
                      <filter name="parent_false" domain="[('parent_id','=',False)]" string="Effective Costs"/>
                      <filter name="parent_true" domain="[('parent_id','!=',False)]" string="Indicative Costs"/>
                      <group expand="1" string="Group By">
-                         <filter name="groupby_year" context="{'group_by' : 'date:year'}" string="Year"/>
-                         <filter name="groupby_date" context="{'group_by' : 'date'}" string="Vehicle Costs Month" help="Vehicle Costs by Month"/>
-                         <filter name="groupby_cost_type" context="{'group_by' : 'cost_type'}" string="Cost Type"/>
-                         <filter name="groupby_cost_subtype_id" context="{'group_by' : 'cost_subtype_id'}" string="Cost Subtype"/>
-                         <filter name="groupby_vehicle_id" context="{'group_by' : 'vehicle_id'}" string="Vehicle"/>
-                         <filter name="groupby_parent_id" context="{'group_by' : 'parent_id'}" string="Parent"/>
+                         <filter name="groupby_cost_type" context="{'group_by': 'cost_type'}" string="Cost Type"/>
+                         <filter name="groupby_cost_subtype_id" context="{'group_by': 'cost_subtype_id'}" string="Cost Subtype"/>
+                         <filter name="groupby_vehicle_id" context="{'group_by': 'vehicle_id'}" string="Vehicle"/>
+                         <filter name="groupby_parent_id" context="{'group_by': 'parent_id'}" string="Parent"/>
+                         <separator/>
+                         <filter name="groupby_date" context="{'group_by': 'date:month'}" string="Month" help="Vehicle Costs by Month"/>
+                         <filter name="groupby_year" context="{'group_by': 'date:year'}" string="Year"/>
                      </group>
                  </search>
              </field>
diff --combined addons/hr/hr.py
@@@ -2,7 -2,7 +2,7 @@@
  ##############################################################################
  #
  #    OpenERP, Open Source Management Solution
 -#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
 +#    Copyright (C) 2004-Today OpenERP S.A. (<http://openerp.com>).
  #
  #    This program is free software: you can redistribute it and/or modify
  #    it under the terms of the GNU Affero General Public License as
@@@ -94,7 -94,7 +94,7 @@@ class hr_job(osv.Model)
  
      _name = "hr.job"
      _description = "Job Position"
 -    _inherit = ['mail.thread', 'ir.needaction_mixin']
 +    _inherit = ['mail.thread']
      _columns = {
          'name': fields.char('Job Name', required=True, select=True),
          'expected_employees': fields.function(_get_nbr_employees, string='Total Forecasted Employees',
@@@ -207,7 -207,7 +207,7 @@@ class hr_employee(osv.osv)
          'parent_id': fields.many2one('hr.employee', 'Manager'),
          'category_ids': fields.many2many('hr.employee.category', 'employee_category_rel', 'emp_id', 'category_id', 'Tags'),
          'child_ids': fields.one2many('hr.employee', 'parent_id', 'Subordinates'),
-         'resource_id': fields.many2one('resource.resource', 'Resource', ondelete='cascade', required=True),
+         'resource_id': fields.many2one('resource.resource', 'Resource', ondelete='cascade', required=True, auto_join=True),
          'coach_id': fields.many2one('hr.employee', 'Coach'),
          'job_id': fields.many2one('hr.job', 'Job Title'),
          # image: all image fields are base64 encoded and PIL-supported
          if context.get("mail_broadcast"):
              context['mail_create_nolog'] = True
  
 +        if data.get('user_id', False) == SUPERUSER_ID and data.get('name',False) == 'Administrator':
 +            user_name = self.pool.get('res.users').browse(cr, uid, data.get('user_id'), context=context).name
 +            if data['name'] != user_name:
 +                data['name'] = user_name
 +
          employee_id = super(hr_employee, self).create(cr, uid, data, context=context)
  
          if context.get("mail_broadcast"):
  
  
  class hr_department(osv.osv):
 +    _name = "hr.department"
 +    _description = "HR Department"
 +    _inherit = ['mail.thread', 'ir.needaction_mixin']
  
      def _dept_name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
          res = self.name_get(cr, uid, ids, context=context)
          return dict(res)
  
 -    _name = "hr.department"
      _columns = {
          'name': fields.char('Department Name', required=True),
          'complete_name': fields.function(_dept_name_get_fnc, type="char", string='Name'),
          'company_id': fields.many2one('res.company', 'Company', select=True, required=False),
          'parent_id': fields.many2one('hr.department', 'Parent Department', select=True),
          'child_ids': fields.one2many('hr.department', 'parent_id', 'Child Departments'),
 -        'manager_id': fields.many2one('hr.employee', 'Manager'),
 +        'manager_id': fields.many2one('hr.employee', 'Manager', track_visibility='onchange'),
          'member_ids': fields.one2many('hr.employee', 'department_id', 'Members', readonly=True),
          'jobs_ids': fields.one2many('hr.job', 'department_id', 'Jobs'),
          'note': fields.text('Note'),
              res.append((record['id'], name))
          return res
  
 +    def create(self, cr, uid, vals, context=None):
 +        # TDE note: auto-subscription of manager done by hand, because currently
 +        # the tracking allows to track+subscribe fields linked to a res.user record
 +        # An update of the limited behavior should come, but not currently done.
 +        manager_id = vals.get("manager_id")
 +        new_id = super(hr_department, self).create(cr, uid, vals, context=context)
 +        if manager_id:
 +            employee = self.pool.get('hr.employee').browse(cr, uid, manager_id, context=context)
 +            if employee.user_id:
 +                self.message_subscribe_users(cr, uid, [new_id], user_ids=[employee.user_id.id], context=context)
 +        return new_id
 +
 +    def write(self, cr, uid, ids, vals, context=None):
 +        # TDE note: auto-subscription of manager done by hand, because currently
 +        # the tracking allows to track+subscribe fields linked to a res.user record
 +        # An update of the limited behavior should come, but not currently done.
 +        if isinstance(ids, (int, long)):
 +            ids = [ids]
 +        manager_id = vals.get("manager_id")
 +        if manager_id:
 +            employee = self.pool.get('hr.employee').browse(cr, uid, manager_id, context=context)
 +            if employee.user_id:
 +                self.message_subscribe_users(cr, uid, [ids], user_ids=[employee.user_id.id], context=context)
 +        return super(hr_department, self).write(cr, uid, ids, vals, context=context)
 +
  
  class res_users(osv.osv):
      _name = 'res.users'
      _inherit = 'res.users'
 -    _columns = {
 -        'employee_ids': fields.one2many('hr.employee', 'user_id', 'Related employees'),
 -    }
  
 +    def write(self, cr, uid, ids, vals, context=None):
 +        if isinstance(ids, (int, long)):
 +            ids = [ids]
 +        result = super(res_users, self).write(cr, uid, ids, vals, context=context)
 +        employee_obj = self.pool.get('hr.employee')
 +        if vals.get('name'):
 +            for user_id in ids:
 +                if user_id == SUPERUSER_ID:
 +                    employee_ids = employee_obj.search(cr, uid, [('user_id', '=', user_id)])
 +                    employee_obj.write(cr, uid, employee_ids, {'name': vals['name']}, context=context)
 +        return result
  
 -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
                  <search string="Hr Attendance Search">
                      <field name="name" string="Attendance"/>
                      <field name="action"/>
-                     <filter icon="terp-go-today" string="Today" name="today" domain="[('name','&gt;=',current_date),('name','&lt;=',current_date)]" />
+                     <filter string="Today" name="today" domain="[('name','&gt;=',current_date),('name','&lt;=',current_date)]" />
                      <filter string="Current Month" domain="[('name','&gt;=', datetime.datetime.now().strftime('%Y-%m-01'))]" />
                      <separator/>
-                     <filter icon="terp-stock_align_left_24" string="My Attendance" domain="[('employee_id.user_id.id', '=', uid)]" />
+                     <filter string="My Attendance" domain="[('employee_id.user_id.id', '=', uid)]" />
                      <field name="employee_id"/>
                      <group expand="0" string="Group By">
-                         <filter name="employee" string="Employee" icon="terp-personal" domain="[]" context="{'group_by':'employee_id'}"/>
-                         <filter name="name" string="Date" icon="terp-personal" domain="[]" context="{'group_by':'name'}"/>
+                         <filter name="employee" string="Employee" context="{'group_by':'employee_id'}"/>
+                         <separator/>
+                         <filter name="name" string="Month" context="{'group_by':'name'}"/>
                      </group>
                  </search>
              </field>
          </record>
  
          <menuitem
 -            sequence="35" id="hr.menu_open_view_attendance_reason_new_config" parent="hr.menu_hr_configuration" groups="base.group_hr_attendance" name="Attendance"/>
 +            sequence="35" id="hr.menu_open_view_attendance_reason_new_config" parent="hr.menu_hr_configuration" groups="base.group_hr_attendance,base.group_hr_manager" name="Attendance"/>
          <menuitem action="open_view_attendance_reason" id="menu_open_view_attendance_reason" parent="hr.menu_open_view_attendance_reason_new_config" groups="base.group_hr_attendance"/>
  
          <record id="hr_attendance_employee" model="ir.ui.view">
                  <field name="active" position="before">
                      <label for="state" groups="base.group_hr_attendance"/>
                      <div groups="base.group_hr_attendance">
-                         <field name="state" class="oe_inline"/> - 
+                         <field name="state" class="oe_inline"/> -
                          <button class="oe_link" name="attendance_action_change" states="present" string="Sign Out" type="object" context="{'type':'sign_out'}" groups="base.group_hr_user"/>
                          <button class="oe_link" name="attendance_action_change" states="absent" string="Sign In" type="object" context="{'type':'sign_in'}" groups="base.group_hr_user"/>
                      </div>
  
      </data>
  </openerp>
@@@ -2,7 -2,7 +2,7 @@@
  ##############################################################################
  #
  #    OpenERP, Open Source Management Solution
 -#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
 +#    Copyright (C) 2004-Today OpenERP S.A. (<http://www.openerp.com>).
  #
  #    This program is free software: you can redistribute it and/or modify
  #    it under the terms of the GNU Affero General Public License as
  #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  #
  ##############################################################################
 +
  import time
  
+ from openerp import SUPERUSER_ID
  from openerp.osv import fields, osv
  
  class hr_employee(osv.osv):
@@@ -32,7 -32,7 +33,7 @@@
          res = {}
          obj_contract = self.pool.get('hr.contract')
          for emp in self.browse(cr, uid, ids, context=context):
 -            contract_ids = obj_contract.search(cr, uid, [('employee_id','=',emp.id),], order='date_start', context=context)
 +            contract_ids = obj_contract.search(cr, uid, [('employee_id', '=', emp.id)], order='date_start', context=context)
              if contract_ids:
                  res[emp.id] = contract_ids[-1:][0]
              else:
@@@ -42,7 -42,7 +43,7 @@@
      def _contracts_count(self, cr, uid, ids, field_name, arg, context=None):
          Contract = self.pool['hr.contract']
          return {
-             employee_id: Contract.search_count(cr,uid, [('employee_id', '=', employee_id)], context=context)
+             employee_id: Contract.search_count(cr, SUPERUSER_ID, [('employee_id', '=', employee_id)], context=context)
              for employee_id in ids
          }
  
@@@ -54,7 -54,7 +55,7 @@@
          'vehicle': fields.char('Company Vehicle'),
          'vehicle_distance': fields.integer('Home-Work Dist.', help="In kilometers"),
          'contract_ids': fields.one2many('hr.contract', 'employee_id', 'Contracts'),
-         'contract_id':fields.function(_get_latest_contract, string='Contract', type='many2one', relation="hr.contract", help='Latest contract of the employee'),
+         'contract_id': fields.function(_get_latest_contract, string='Contract', type='many2one', relation="hr.contract", help='Latest contract of the employee'),
          'contracts_count': fields.function(_contracts_count, type='integer', string='Contracts'),
      }
  
@@@ -66,40 -66,26 +67,40 @@@ class hr_contract_type(osv.osv)
          'name': fields.char('Contract Type', required=True),
      }
  
 +
  class hr_contract(osv.osv):
      _name = 'hr.contract'
      _description = 'Contract'
 +    _inherit = ['mail.thread', 'ir.needaction_mixin']
 +
 +    _track = {
 +        'state': {
 +            'hr_contract.mt_contract_pending': lambda self, cr, uid, obj, ctx=None: obj.state == 'pending',
 +            'hr_contract.mt_contract_close': lambda self, cr, uid, obj, ctx=None: obj.state == 'close',
 +        },
 +    }
 +
      _columns = {
          'name': fields.char('Contract Reference', required=True),
          'employee_id': fields.many2one('hr.employee', "Employee", required=True),
 -        'department_id': fields.related('employee_id','department_id', type='many2one', relation='hr.department', string="Department", readonly=True),
 +        'department_id': fields.many2one('hr.department', string="Department"),
          'type_id': fields.many2one('hr.contract.type', "Contract Type", required=True),
          'job_id': fields.many2one('hr.job', 'Job Title'),
          'date_start': fields.date('Start Date', required=True),
          'date_end': fields.date('End Date'),
          'trial_date_start': fields.date('Trial Start Date'),
          'trial_date_end': fields.date('Trial End Date'),
 -        'working_hours': fields.many2one('resource.calendar','Working Schedule'),
 -        'wage': fields.float('Wage', digits=(16,2), required=True, help="Basic Salary of the employee"),
 +        'working_hours': fields.many2one('resource.calendar', 'Working Schedule'),
 +        'wage': fields.float('Wage', digits=(16, 2), required=True, help="Basic Salary of the employee"),
          'advantages': fields.text('Advantages'),
          'notes': fields.text('Notes'),
          'permit_no': fields.char('Work Permit No', required=False, readonly=False),
          'visa_no': fields.char('Visa No', required=False, readonly=False),
          'visa_expire': fields.date('Visa Expire Date'),
 +        'state': fields.selection(
 +            [('draft', 'New'), ('open', 'Running'), ('pending', 'To Renew'), ('close', 'Expired')],
 +            string='Status', track_visibility='onchange',
 +            help='Status of the contract'),
      }
  
      def _get_type(self, cr, uid, context=None):
  
      _defaults = {
          'date_start': lambda *a: time.strftime("%Y-%m-%d"),
 -        'type_id': _get_type
 +        'type_id': _get_type,
 +        'state': 'draft',
      }
  
      def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
          if not employee_id:
 -            return {'value': {'job_id': False}}
 +            return {'value': {'job_id': False, 'department_id': False}}
          emp_obj = self.pool.get('hr.employee').browse(cr, uid, employee_id, context=context)
 -        job_id = False
 +        job_id = dept_id = False
          if emp_obj.job_id:
              job_id = emp_obj.job_id.id
 -        return {'value': {'job_id': job_id}}
 +        if emp_obj.department_id:
 +            dept_id = emp_obj.department_id.id
 +        return {'value': {'job_id': job_id, 'department_id': dept_id}}
  
      def _check_dates(self, cr, uid, ids, context=None):
          for contract in self.read(cr, uid, ids, ['date_start', 'date_end'], context=context):
 -             if contract['date_start'] and contract['date_end'] and contract['date_start'] > contract['date_end']:
 -                 return False
 +            if contract['date_start'] and contract['date_end'] and contract['date_start'] > contract['date_end']:
 +                return False
          return True
  
      _constraints = [
          (_check_dates, 'Error! Contract start-date must be less than contract end-date.', ['date_start', 'date_end'])
      ]
  
 -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
 +    def set_as_pending(self, cr, uid, ids, context=None):
 +        return self.write(cr, uid, ids, {'state': 'pending'}, context=context)
 +
 +    def set_as_close(self, cr, uid, ids, context=None):
 +        return self.write(cr, uid, ids, {'state': 'close'}, context=context)
  #
  ##############################################################################
  
 +
 +import calendar
  import datetime
 +from datetime import date
  import math
  import time
  from operator import attrgetter
@@@ -114,14 -111,13 +114,14 @@@ class hr_holidays(osv.osv)
      _inherit = ['mail.thread', 'ir.needaction_mixin']
      _track = {
          'state': {
 +            'hr_holidays.mt_holidays_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm',
 +            'hr_holidays.mt_holidays_first_validated': lambda self, cr, uid, obj, ctx=None: obj.state == 'validate1',
              'hr_holidays.mt_holidays_approved': lambda self, cr, uid, obj, ctx=None: obj.state == 'validate',
              'hr_holidays.mt_holidays_refused': lambda self, cr, uid, obj, ctx=None: obj.state == 'refuse',
          },
      }
  
 -    def _employee_get(self, cr, uid, context=None):        
 +    def _employee_get(self, cr, uid, context=None):
          emp_id = context.get('default_employee_id', False)
          if emp_id:
              return emp_id
              \nThe status is \'To Approve\', when holiday request is confirmed by user.\
              \nThe status is \'Refused\', when holiday request is refused by manager.\
              \nThe status is \'Approved\', when holiday request is approved by manager.'),
 +        'payslip_status': fields.boolean(string='Payslip Status',
 +            help='Check this field when the leave has been taken into account in the payslip.'),
 +        'report_note': fields.text('Comment by Manager'),
          'user_id':fields.related('employee_id', 'user_id', type='many2one', relation='res.users', string='User', store=True),
          'date_from': fields.datetime('Start Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, select=True, copy=False),
          'date_to': fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, copy=False),
          'state': 'confirm',
          'type': 'remove',
          'user_id': lambda obj, cr, uid, context: uid,
 -        'holiday_type': 'employee'
 +        'holiday_type': 'employee',
 +        'payslip_status': False,
      }
      _constraints = [
          (_check_date, 'You can not have 2 leaves that overlaps on same day!', ['date_from','date_to']),
          """
          Update the number_of_days.
          """
 -
          # date_to has to be greater than date_from
          if (date_from and date_to) and (date_from > date_to):
              raise osv.except_osv(_('Warning!'),_('The start date must be anterior to the end date.'))
              result['value']['number_of_days_temp'] = round(math.floor(diff_day))+1
          else:
              result['value']['number_of_days_temp'] = 0
 -
          return result
  
      def create(self, cr, uid, values, context=None):
          if context is None:
              context = {}
          context = dict(context, mail_create_nolog=True)
-         hol_id = super(hr_holidays, self).create(cr, uid, values, context=context)
-         return hol_id
+         if values.get('state') and values['state'] not in ['draft', 'confirm', 'cancel'] and not self.pool['res.users'].has_group(cr, uid, 'base.group_hr_user'):
+             raise osv.except_osv(_('Warning!'), _('You cannot set a leave request as \'%s\'. Contact a human resource manager.') % values.get('state'))
+         return super(hr_holidays, self).create(cr, uid, values, context=context)
+     def write(self, cr, uid, ids, vals, context=None):
+         if vals.get('state') and vals['state'] not in ['draft', 'confirm', 'cancel'] and not self.pool['res.users'].has_group(cr, uid, 'base.group_hr_user'):
+             raise osv.except_osv(_('Warning!'), _('You cannot set a leave request as \'%s\'. Contact a human resource manager.') % vals.get('state'))
+         return super(hr_holidays, self).write(cr, uid, ids, vals, context=context)
  
      def holidays_reset(self, cr, uid, ids, context=None):
          self.write(cr, uid, ids, {
          obj_emp = self.pool.get('hr.employee')
          ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
          manager = ids2 and ids2[0] or False
 -        self.holidays_first_validate_notificate(cr, uid, ids, context=context)
 -        return self.write(cr, uid, ids, {'state':'validate1', 'manager_id': manager})
 +        return self.write(cr, uid, ids, {'state': 'validate1', 'manager_id': manager}, context=context)
  
      def holidays_validate(self, cr, uid, ids, context=None):
          obj_emp = self.pool.get('hr.employee')
          ids2 = obj_emp.search(cr, uid, [('user_id', '=', uid)])
          manager = ids2 and ids2[0] or False
 -        self.write(cr, uid, ids, {'state':'validate'})
 +        self.write(cr, uid, ids, {'state': 'validate'}, context=context)
          data_holiday = self.browse(cr, uid, ids)
          for record in data_holiday:
              if record.double_validation:
                                  'Please verify also the leaves waiting for validation.'))
          return True
  
 -    # -----------------------------
 -    # OpenChatter and notifications
 -    # -----------------------------
 +    def set_payslip_status(self, cr, uid, ids, context=None):
 +        return self.write(cr, uid, ids, {'payslip_status': True}, context=context)
  
 -    def _needaction_domain_get(self, cr, uid, context=None):
 -        emp_obj = self.pool.get('hr.employee')
 -        empids = emp_obj.search(cr, uid, [('parent_id.user_id', '=', uid)], context=context)
 -        dom = ['&', ('state', '=', 'confirm'), ('employee_id', 'in', empids)]
 -        # if this user is a hr.manager, he should do second validations
 -        if self.pool.get('res.users').has_group(cr, uid, 'base.group_hr_manager'):
 -            dom = ['|'] + dom + [('state', '=', 'validate1')]
 -        return dom
 +    def unset_payslip_status(self, cr, uid, ids, context=None):
 +        return self.write(cr, uid, ids, {'payslip_status': False}, context=context)
  
 -    def holidays_first_validate_notificate(self, cr, uid, ids, context=None):
 -        for obj in self.browse(cr, uid, ids, context=context):
 -            self.message_post(cr, uid, [obj.id],
 -                _("Request approved, waiting second validation."), context=context)
  
  class resource_calendar_leaves(osv.osv):
      _inherit = "resource.calendar.leaves"
      }
  
  
 -
 -class hr_employee(osv.osv):
 -    _inherit="hr.employee"
 +class hr_employee(osv.Model):
 +    _inherit = "hr.employee"
  
      def create(self, cr, uid, vals, context=None):
          # don't pass the value of remaining leave if it's 0 at the creation time, otherwise it will trigger the inverse
          return result
  
      def _leaves_count(self, cr, uid, ids, field_name, arg, context=None):
 +        res = {}
          Holidays = self.pool['hr.holidays']
 -        return {
 -            employee_id: Holidays.search_count(cr,uid, [('employee_id', '=', employee_id)], context=context) 
 -            for employee_id in ids
 -        }
 +        date_begin = date.today().replace(day=1)
 +        date_end = date_begin.replace(day=calendar.monthrange(date_begin.year, date_begin.month)[1])
 +        for employee_id in ids:
 +            leaves = Holidays.search_count(cr, uid, [('employee_id', '=', employee_id), ('type', '=', 'remove')], context=context)
 +            approved_leaves = Holidays.search_count(cr, uid, [('employee_id', '=', employee_id), ('type', '=', 'remove'), ('date_from', '>=', date_begin.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)), ('date_from', '<=', date_end.strftime(tools.DEFAULT_SERVER_DATE_FORMAT)), ('state', '=', 'validate'), ('payslip_status', '=', False)], context=context)
 +            res[employee_id] = {'leaves_count': leaves, 'approved_leaves_count': approved_leaves}
 +        return res
  
      _columns = {
          'remaining_leaves': fields.function(_get_remaining_days, string='Remaining Legal Leaves', fnct_inv=_set_remaining_days, type="float", help='Total number of legal leaves allocated to this employee, change this value to create allocation/leave request. Total based on all the leave types without overriding limit.'),
 -        'current_leave_state': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Status", type="selection",
 +        'current_leave_state': fields.function(
 +            _get_leave_status, multi="leave_status", string="Current Leave Status", type="selection",
              selection=[('draft', 'New'), ('confirm', 'Waiting Approval'), ('refuse', 'Refused'),
 -            ('validate1', 'Waiting Second Approval'), ('validate', 'Approved'), ('cancel', 'Cancelled')]),
 -        'current_leave_id': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Type",type='many2one', relation='hr.holidays.status'),
 +                       ('validate1', 'Waiting Second Approval'), ('validate', 'Approved'), ('cancel', 'Cancelled')]),
 +        'current_leave_id': fields.function(_get_leave_status, multi="leave_status", string="Current Leave Type", type='many2one', relation='hr.holidays.status'),
          'leave_date_from': fields.function(_get_leave_status, multi='leave_status', type='date', string='From Date'),
          'leave_date_to': fields.function(_get_leave_status, multi='leave_status', type='date', string='To Date'),
 -        'leaves_count': fields.function(_leaves_count, type='integer', string='Leaves'),
 -
 +        'leaves_count': fields.function(_leaves_count, multi='_leaves_count', type='integer', string='Number of Leaves (current month)'),
 +        'approved_leaves_count': fields.function(_leaves_count, multi='_leaves_count', type='integer', string='Approved Leaves not in Payslip', help="These leaves are approved but not taken into account for payslip"),
      }
 -
 -
 -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@@ -1,39 -1,47 +1,52 @@@
  <openerp>
      <data>
  
+         <!-- Graph report -->
+         <record id="view_evaluation_report_graph" model="ir.ui.view">
+                 <field name="name">hr.holidays.graph</field>
+                 <field name="model">hr.holidays</field>
+                 <field name="arch" type="xml">
+                 <graph string="Appraisal Analysis" type="pivot" stacked="True">
+                     <field name="employee_id" type="row"/>
+                     <field name="holiday_status_id" type="row"/>
+                     <field name="date_from" type="col"/>
+                     <field name="number_of_days" type="measure"/>
+                  </graph>
+              </field>
+         </record>
          <record id="view_hr_holidays_filter" model="ir.ui.view">
              <field name="name">hr.holidays.filter</field>
              <field name="model">hr.holidays</field>
              <field name="arch" type="xml">
                  <search string="Search Leave">
                      <field name="name"/>
 -                    <separator/>
                      <filter domain="[('state','=','draft')]" string="To Confirm"/>
                      <filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
                      <filter domain="[('state','=','validate')]" string="Validated" name="validated"/>
                      <separator/>
 +                    <filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
 +                    <separator/>
 +                    <filter string="Approved Leaves" name="validated" domain="[('state','=','validate')]"/>
 +                    <separator/>
 +                    <filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="base.group_hr_manager"/>
 +                    <separator/>
-                     <filter icon="terp-go-year" name="year" string="Year" domain="[('holiday_status_id.active','=',True)]" help="Filters only on allocations and requests that belong to an holiday type that is 'active' (active field is True)"/>
+                     <filter name="year" string="Year" domain="[('holiday_status_id.active','=',True)]" help="Filters only on allocations and requests that belong to an holiday type that is 'active' (active field is True)"/>
                      <separator/>
-                     <filter string="My Requests" icon="terp-personal" name="my_leaves" domain="[('employee_id.user_id','=', uid)]" help="My Leave Requests"/>
+                     <filter string="My Requests" name="my_leaves" domain="[('employee_id.user_id','=', uid)]" help="My Leave Requests"/>
                      <separator/>
-                     <filter string="My Department Leaves" icon="terp-personal+" help="My Department Leaves" domain="[('department_id.manager_id','=',uid)]"/>
+                     <filter string="My Department Leaves" help="My Department Leaves" domain="[('department_id.manager_id','=',uid)]"/>
                      <field name="employee_id"/>
                      <field name="department_id"/>
                      <field name="holiday_status_id"/>
                      <group expand="0" string="Group By">
-                         <filter name="group_name" string="Description" domain="[]" context="{'group_by':'name'}"/>
-                         <filter name="group_date_from" string="Start Month" icon="terp-personal" domain="[]" context="{'group_by':'date_from'}"/>
-                         <filter name="group_employee" string="Employee" icon="terp-personal" domain="[]" context="{'group_by':'employee_id'}"/>
-                         <filter name="group_category" string="Category" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'category_id'}"/>
-                         <filter string="Manager" icon="terp-personal" domain="[]" context="{'group_by':'manager_id'}"/>
-                         <filter string="Department" icon="terp-personal+" domain="[]" context="{'group_by':'department_id','set_visible':True}"/>
-                         <filter name="group_type" string="Type" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'holiday_status_id'}"/>
-                         <filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
+                         <filter name="group_name" string="Description" context="{'group_by':'name'}"/>
+                         <filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
+                         <filter name="group_category" string="Category" context="{'group_by':'category_id'}"/>
+                         <filter string="Manager" context="{'group_by':'manager_id'}"/>
+                         <filter name="group_type" string="Type" context="{'group_by':'holiday_status_id'}"/>
+                         <separator/>
+                         <filter name="group_date_from" string="Start Month" context="{'group_by':'date_from:month'}"/>
                      </group>
                  </search>
              </field>
@@@ -45,9 -53,9 +58,9 @@@
              <field name="name">Leave Request</field>
              <field name="model">hr.holidays</field>
              <field name="arch" type="xml">
-                 <calendar string="Leave Request" color="employee_id" 
-                           date_start="date_from" date_stop="date_to"  
-                           quick_add="False" avatar_model="hr.employee"> 
+                 <calendar string="Leave Request" color="employee_id"
+                           date_start="date_from" date_stop="date_to"
+                           quick_add="False" avatar_model="hr.employee">
                      <field name="employee_id"/>
                      <field name="holiday_status_id"/>
                  </calendar>
                              <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','=','category')]}" on_change="onchange_employee(employee_id)" groups="base.group_hr_user"/>
                              <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'readonly': [('type', '=', 'remove'),('state','!=','draft'), ('state','!=','confirm')], 'invisible':[('holiday_type','=','employee')]}"/>
                              <field name="department_id" attrs="{'readonly':['|', ('type','=','add'),('holiday_type','=','category')],'invisible':[('holiday_type','=','category')]}" groups="base.group_hr_user"/>
 +                            <field name="payslip_status" groups="base.group_hr_manager" attrs="{'invisible':[('type','=','add')]}"/>
                          </group>
 +                        <field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..." attrs="{'invisible': [('type', '=', 'remove')]}"/>
 +                        <div groups="base.group_hr_manager" attrs="{'invisible':[('type','=','add')]}">
 +                            <separator string="Comment by Manager"/>
 +                            <field name="report_note" placeholder="e.g. Report to the next month..."/>
 +                        </div>
                      </group>
 -                    <field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..." attrs="{'invisible': [('type', '=', 'remove')]}"/>
                  </sheet>
                  <div class="oe_chatter">
                      <field name="message_follower_ids" widget="mail_followers"/>
              <field name="name">hr.holidays.allocation.tree</field>
              <field name="model">hr.holidays</field>
              <field name="arch" type="xml">
 -                <tree colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Allocation Requests">
 +                <tree colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Allocation Requests"
 +                        fonts="bold: message_unread == True">
                      <field name="employee_id"/>
                      <field name="holiday_type"/>
                      <field name="category_id"/>
                      <field name="manager_id" invisible="1"/>
                      <field name="user_id" invisible="1"/>
                      <field name="date_from" invisible="1"/>
 -                    <!--field name="type"/-->
 +                    <field name="message_unread" invisible="1"/>
                      <field name="state"/>
                  </tree>
              </field>
          </record>
 -
 -        <!-- Holidays: Leaves Management -->
 -        <record model="ir.ui.view" id="allocation_company_new">
 -            <field name="name">Leaves Management</field>
 +        <record model="ir.ui.view" id="view_holiday_allocation_tree_customize">
 +            <field name="name">hr.holidays.allocation.tree.customize</field>
              <field name="model">hr.holidays</field>
              <field name="arch" type="xml">
 -                <form string="Leaves Management">
 -                    <header>
 -                        <button string="Submit to Manager" name="confirm" states="draft" type="workflow" icon="gtk-yes"/>
 -                        <button string="Approve" name="validate" states="confirm" type="workflow" icon="gtk-apply"/>
 -                        <button string="Refuse" name="refuse" states="confirm,validate,draft" type="workflow" icon="gtk-no"/>
 -                        <button string="Reset to Draft" name="reset" states="confirm" type="workflow" groups="base.group_hr_manager"/>
 -                        <field name="state"/>
 -                    </header>
 -                    <group col="4">
 -                        <field name="holiday_status_id"/>
 -                        <field name="type"/>
 -                        <field name="date_from" on_change="onchange_date_from(date_to, date_from)" attrs="{'readonly':[('type','=','add')], 'required':[('type','=','remove')]}"/>
 -                        <field name="date_to" on_change="onchange_date_from(date_to, date_from)" attrs="{'readonly':[('type','=','add')], 'required':[('type','=','remove')]}"/>
 -                        <field name="number_of_days_temp"/>
 -                        <field name="manager_id"/>
 -                    </group>
 -                    <field name="name" placeholder="Add a reason..."/>
 -                </form>
 +                <tree string="Allocation Requests" editable="top">
 +                    <field name="employee_id"/>
 +                    <field name="holiday_type"/>
 +                    <field name="holiday_status_id"/>
 +                    <field name="name" readonly="1"/>
 +                    <field name="date_from" required="1" on_change="onchange_date_from(date_to, date_from)"/>
 +                    <field name="date_to" required="1" on_change="onchange_date_to(date_to, date_from)"/>
 +                    <field name="number_of_days_temp" string="Allocated Days" sum="Remaining Days"/>
 +                    <field name="state"/>
 +                    <field name="report_note" groups="base.group_hr_manager"/>
 +                    <field name="payslip_status" invisible="1"/>
 +                    <button string="To Report in Payslip" name="set_payslip_status"
 +                        type="object" class="oe_link oe_right" icon="gtk-normal"
 +                        attrs="{'invisible': [('payslip_status', '=', True)]}" groups="base.group_hr_manager"/>
 +                    <button string="Reported in last payslips" name="unset_payslip_status"
 +                        type="object" class="oe_link oe_right" icon="gtk-yes"
 +                        attrs="{'invisible': [('payslip_status', '=', False)]}" groups="base.group_hr_manager"/>
 +                </tree>
              </field>
          </record>
  
              <field name="model">hr.holidays</field>
              <field name="priority">20</field>
              <field name="arch" type="xml">
 -                <tree colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leaves Summary">
 +                <tree colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Leaves Summary">
                      <field name="employee_id"/>
                      <field name="category_id" invisible="1"/>
                      <field name="department_id" invisible="1"/>
              </field>
          </record>
  
 +        <record model="ir.ui.view" id="view_holiday_employee">
 +            <field name="name">hr.holidays.report_employee_tree</field>
 +            <field name="model">hr.holidays</field>
 +            <field name="priority">21</field>
 +            <field name="arch" type="xml">
 +                <tree colors="red:state == 'refuse';blue:state == 'draft';black:state in ('confirm','validate','validate1')" string="Employee's Leave">
 +                    <field name="employee_id"/>
 +                    <field name="type"/>
 +                    <field name="name"/>
 +                    <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
 +                    <field name="date_from"/>
 +                    <field name="date_to"/>
 +                    <field name="holiday_status_id"/>
 +                    <field name="state"/>
 +                    <field name="report_note"/>
 +                    <field name="payslip_status" invisible="1"/>
 +                    <button string="To Report in Payslip" name="set_payslip_status"
 +                        type="object" class="oe_link oe_right"
 +                        attrs="{'invisible': [('payslip_status', '=', True)]}" groups="base.group_hr_manager"/>
 +                    <button string="Reported in last payslips" name="unset_payslip_status"
 +                        type="object" class="oe_link oe_right"
 +                        attrs="{'invisible': [('payslip_status', '=', False)]}" groups="base.group_hr_manager"/>
 +                </tree>
 +            </field>
 +        </record>
 +
          <record model="ir.ui.view" id="view_holiday">
              <field name="name">hr.holidays.tree</field>
              <field name="model">hr.holidays</field>
              <field name="arch" type="xml">
 -                <tree colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leave Requests">
 +                <tree colors="red:state == 'refuse';blue:state == ' draft';black:state in ('confirm','validate','validate1')" string="Leave Requests"
 +                        fonts="bold: message_unread == True">
                      <field name="employee_id"/>
                      <field name="holiday_type" string="Mode" groups="base.group_no_one"/>
                      <field name="holiday_status_id"/>
                      <field name="date_to"/>
                      <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
                      <field name="state"/>
 +                    <field name="payslip_status" invisible="1"/>
 +                    <button string="To Report in Payslip" name="set_payslip_status"
 +                        type="object" class="oe_link oe_right"
 +                        attrs="{'invisible': [('payslip_status', '=', True)]}" groups="base.group_hr_manager"/>
 +                    <button string="Reported in last payslips" name="unset_payslip_status"
 +                        type="object" class="oe_link oe_right"
 +                        attrs="{'invisible': [('payslip_status', '=', False)]}" groups="base.group_hr_manager"/>
                      <field name="category_id" invisible="1"/>
                      <field name="department_id" invisible="not context.get('set_visible',False)"/>
                      <field name="manager_id" invisible="1"/>
                      <field name="user_id" invisible="1"/>
 +                    <field name="message_unread" invisible="1"/>
                  </tree>
              </field>
          </record>
              <field name="res_model">hr.holidays</field>
              <field name="view_type">form</field>
              <field name="view_id" ref="edit_holiday_new"/>
 -            <field name="context">{'default_type': 'remove', 'search_default_my_leaves':1}</field>
 -            <field name="domain">[('type','=','remove')]</field>
 +            <field name="context">{
 +                'default_type': 'remove', 
 +                'search_default_my_leaves':1,
 +                'needaction_menu_ref': 
 +                [
 +                    'hr_holidays.menu_open_company_allocation', 
 +                ]
 +            }</field>
 +            <field name="domain">[('type','=','remove'), ('employee_id.user_id','=', uid)]</field>
              <field name="search_view_id" ref="view_hr_holidays_filter"/>
              <field name="help" type="html">
                <p class="oe_view_nocontent_create">
              <field name="name">Requests to Approve</field>
              <field name="res_model">hr.holidays</field>
              <field name="view_type">form</field>
 -            <field name="context">{'default_type': 'remove', 'search_default_approve':1}</field>
 +            <field name="context">{
 +                'default_type': 'remove', 
 +                'search_default_approve':1, 
 +                'needaction_menu_ref': 
 +                [
 +                    'hr_holidays.menu_open_ask_holidays_new', 
 +                    'hr_holidays.menu_open_company_allocation', 
 +                    'hr_holidays.menu_open_employee_leave',
 +                ]
 +            }</field>
              <field name="domain">[('type','=','remove')]</field>
              <field name="view_id" ref="edit_holiday_new"/>
              <field name="search_view_id" ref="view_hr_holidays_filter"/>
              <field name="name">Allocation Requests</field>
              <field name="res_model">hr.holidays</field>
              <field name="view_type">form</field>
 -            <field name="context">{'default_type':'add', 'search_default_my_leaves':1}</field>
 -            <field name="domain">[('type','=','add')]</field>
 +            <field name="context">{
 +                'default_type':'add', 
 +                'search_default_my_leaves':1,
 +                'needaction_menu_ref': 
 +                [
 +                    'hr_holidays.menu_open_company_allocation', 
 +                ]
 +            }</field>
 +            <field name="domain">[('type','=','add'), ('employee_id.user_id','=', uid)]</field>
              <field name="view_id" ref="edit_holiday_new"/>
              <field name="search_view_id" ref="view_hr_holidays_filter"/>
          </record>
              <field name="name">Allocation Requests to Approve</field>
              <field name="res_model">hr.holidays</field>
              <field name="view_type">form</field>
 -            <field name="context">{'default_type': 'add', 'search_default_approve':1}</field>
 +            <field name="context">{
 +                'default_type': 'add', 
 +                'search_default_approve':1, 
 +                'needaction_menu_ref': 
 +                [
 +                    'hr_holidays.menu_open_allocation_holidays', 
 +                    'hr_holidays.menu_open_company_allocation'
 +                ]
 +            }</field>
              <field name="domain">[('type','=','add')]</field>
              <field name="view_id" ref="edit_holiday_new"/>
              <field name="search_view_id" ref="view_hr_holidays_filter"/>
              <field name="view_type">form</field>
              <field name="view_mode">tree,form</field>
              <field name="view_id" eval="view_holiday_simple"/>
 -            <field name="context">{'search_default_group_type': 1}</field>
 -            <field name="domain">[('holiday_type','=','employee'), ('state', '!=', 'refuse')]</field>
 +            <field name="context">{
 +                'search_default_group_type': 1, 
 +                'search_default_year': 1 ,
 +                'needaction_menu_ref': 
 +                [
 +                    'hr_holidays.menu_open_ask_holidays_new',
 +                    'hr_holidays.menu_request_approve_holidays',
 +                    'hr_holidays.menu_open_allocation_holidays',
 +                    'hr_holidays.menu_request_approve_allocation',
 +                    'hr_holidays.menu_open_employee_leave',
 +                ]
 +            }</field>
 +            <field name="domain">[('holiday_type','=','employee')]</field>
              <field name="search_view_id" ref="view_hr_holidays_filter"/>
          </record>
  
          <menuitem name="Leaves Summary" parent="menu_open_ask_holidays" id="menu_open_company_allocation" action="open_company_allocation" sequence="40"/>
 +        
 +        <record model="ir.actions.act_window" id="open_employee_leaves">
 +            <field name="name">Employee's Leaves</field>
 +            <field name="res_model">hr.holidays</field>
 +            <field name="view_type">form</field>
 +            <field name="view_mode">tree,form</field>
 +            <field name="view_id" eval="view_holiday_employee"/>
 +            <field name="context">{'default_type': 'remove', 'search_default_gray': 1, 'search_default_year': 1, 'search_default_group_employee': 1}</field>
 +            <field name="domain">[('type','=','remove')]</field>
 +            <field name="search_view_id" ref="view_hr_holidays_filter"/>
 +        </record>
 +
 +        <menuitem name="Employee's Leaves" parent="menu_open_ask_holidays" id="menu_open_employee_leave" groups="base.group_hr_manager,base.group_hr_user" action="open_employee_leaves" sequence="41"/>
  
          <!-- Holidays status -->
          <record id="view_holidays_status_filter" model="ir.ui.view">
              <field name="model">hr.holidays.status</field>
              <field name="arch" type="xml">
                  <form string="Leave Type">
 -                <sheet string="Leave Type">
                      <group col="4">
                          <field name="name"/>
                          <field name="categ_id"/>
                      </group>
                      <notebook>
 -                      <page string="Details">
 -                          <group>
 -                              <group name="selection" string="Validation">
 -                                  <field name="double_validation"/>
 -                                  <field name="limit"/>
 -                              </group>
 -                              <group name="second" string="Misc">
 -                                  <field name="color_name"/>
 -                                  <field name="active"/>
 -                              </group>
 -                          </group>
 -                      </page>
 +                        <page string="Details">
 +                            <group>
 +                                <group name="selection" string="Validation">
 +                                    <field name="double_validation"/>
 +                                    <field name="limit"/>
 +                                </group>
 +                                <group name="second" string="Misc">
 +                                    <field name="color_name"/>
 +                                    <field name="active"/>
 +                                </group>
 +                            </group>
 +                        </page>
                      </notebook>
 -                </sheet>
                  </form>
              </field>
          </record>
              <field name="search_view_id" ref="view_hr_holidays_status_search"/>
          </record>
  
 -        <menuitem sequence="3" id="hr.menu_open_view_attendance_reason_config" parent="hr.menu_hr_configuration" name="Leaves"/>
 -        <menuitem name="Leaves Types" action="open_view_holiday_status" id="menu_open_view_holiday_status" parent="hr.menu_hr_configuration" sequence="10"/>
 +        <menuitem sequence="3" id="hr.menu_open_view_attendance_reason_config" parent="hr.menu_hr_configuration" name="Leaves" groups="base.group_hr_manager"/>
 +        <menuitem name="Leaves Types" action="open_view_holiday_status" id="menu_open_view_holiday_status" parent="hr.menu_hr_configuration" sequence="10" groups="base.group_hr_manager"/>
  
          <!-- Holiday on resource leave -->
          <record id="resource_calendar_leave_form_inherit" model="ir.ui.view">
              <field name="domain">[('type','=','remove')]</field>
              <field name="view_id" eval="view_holiday"/>
          </record>
 +        
 +        <record id="act_hr_employee_holiday_request_approved" model="ir.actions.act_window">
 +            <field name="name">Leaves to be reported in Payslip</field>
 +            <field name="type">ir.actions.act_window</field>
 +            <field name="res_model">hr.holidays</field>
 +            <field name="src_model">hr.employee</field>
 +            <field name="view_type">form</field>
 +            <field name="view_mode">tree,form</field>
 +            <field name="context">{'search_default_employee_id': [active_id], 'search_default_validated': True, 'search_default_gray': True}</field>
 +            <field name="domain">[('date_from','&gt;=', context_today().strftime("%Y-%m-1")), ('date_from','&lt;', ((context_today() + relativedelta(months=1)).strftime('%Y-%m-1')) )]</field>
 +            <field name="view_id" eval="view_holiday_allocation_tree_customize"/>
 +        </record>
  
         <!-- Assing leave -->
          <record id="hr_holidays_leaves_assign_tree_view" model="ir.ui.view">
                      </group>
                  </xpath>
                  <xpath expr="//div[@name='button_box']" position="inside">
 +                    <button name="%(act_hr_employee_holiday_request_approved)d"
 +                            icon="fa-calendar"
 +                            class="oe_stat_button"
 +                            type="action"
 +                            groups="base.group_hr_user">
 +                        <field name="approved_leaves_count" widget="statinfo"/>
 +                    </button>
-                     <button name="%(act_hr_employee_holiday_request)d" 
+                     <button name="%(act_hr_employee_holiday_request)d"
 -                        type="action"
 -                        class="oe_stat_button"
 -                        icon="fa-calendar"
 -                        groups="base.group_hr_user">
 -                        <field name="leaves_count" widget="statinfo" string="Leaves"/>
 +                            type="action"
 +                            class="oe_stat_button"
-                             icon="fa-calendar" 
++                            icon="fa-calendar"
 +                            groups="base.group_hr_user">
 +                        <field name="leaves_count" widget="statinfo"/>
                      </button>
                  </xpath>
              </field>
@@@ -79,13 -79,10 +79,13 @@@ class hr_recruitment_degree(osv.osv)
  class hr_applicant(osv.Model):
      _name = "hr.applicant"
      _description = "Applicant"
-     _order = "id desc"
+     _order = "priority desc, id desc"
      _inherit = ['mail.thread', 'ir.needaction_mixin']
  
      _track = {
 +        'emp_id': {
 +            'hr_recruitment.mt_applicant_hired': lambda self, cr, uid, obj, ctx=None: obj.emp_id,
 +        },
          'stage_id': {
              # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages
              'hr_recruitment.mt_applicant_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence <= 1,
          'day_close': fields.function(_compute_day, string='Days to Close', \
                                  multi='day_close', type="float", store=True),
          'color': fields.integer('Color Index'),
 -        'emp_id': fields.many2one('hr.employee', string='Employee', help='Employee linked to the applicant.'),
 +        'emp_id': fields.many2one('hr.employee', string='Employee', track_visibility='onchange', help='Employee linked to the applicant.'),
          'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
          'attachment_number': fields.function(_get_attachment_number, string='Number of Attachments', type="integer"),
      }
          'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
          'company_id': lambda s, cr, uid, c: s._get_default_company_id(cr, uid, s._get_default_department_id(cr, uid, c), c),
          'color': 0,
+         'priority': '0',
          'date_last_stage_update': fields.datetime.now,
      }
  
                          'model': self._name,
                          'composition_mode': 'mass_mail',
                          'template_id': stage.template_id.id,
-                         'same_thread': True,
                          'post': True,
                          'notify': True,
                      }, context=compose_ctx)
@@@ -574,7 -571,6 +574,7 @@@ class hr_job(osv.osv)
              'hr.applicant', self._columns['alias_id'], 'name', alias_prefix='job+', alias_defaults={'job_id': 'id'}, context=context)
  
      def create(self, cr, uid, vals, context=None):
 +        # TDE note: shouldn't it be in mail_create_nolog ?
          alias_context = dict(context, alias_model_name='hr.applicant', alias_parent_model_name=self._name)
          job_id = super(hr_job, self).create(cr, uid, vals, context=alias_context)
          job = self.browse(cr, uid, job_id, context=context)
@@@ -614,3 -610,5 +614,3 @@@ class applicant_category(osv.osv)
      _columns = {
          'name': fields.char('Name', required=True, translate=True),
      }
 -
 -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@@ -26,8 -26,9 +26,9 @@@
      'category': 'Hardware Drivers',
      'sequence': 6,
      'summary': 'Hardware Driver for Weighting Scales',
+     'website': 'https://www.odoo.com/page/point-of-sale',
      'description': """
 -Barcode Scanner Hardware Driver
 +Weighting Scale Hardware Driver
  ================================
  
  This module allows the point of sale to connect to a scale using a USB HSM Serial Scale Interface,
@@@ -72,12 -72,14 +72,13 @@@ V2.2 specifications
  If required, you can manually adjust the descriptions via the CODA configuration menu.
  ''',
      'images': ['images/coda_logs.jpeg', 'images/import_coda_logs.jpeg'],
 -    'depends': ['account_voucher', 'base_iban', 'l10n_be_invoice_bba'],
 +    'depends': ['account_voucher', 'base_iban', 'l10n_be_invoice_bba', 'account_bank_statement_import'],
      'demo': ['l10n_be_coda_demo.xml'],
      'data': [
          'l10n_be_coda_view.xml',
      ],
      'auto_install': False,
+     'website': 'https://www.odoo.com/page/accounting',
      'installable': True,
      'license': 'AGPL-3',
  }
@@@ -22,7 -22,7 +22,7 @@@
  import base64
  import time
  
 -from openerp.osv import fields, osv
 +from openerp.osv import osv
  from openerp.tools.translate import _
  from openerp import tools
  
@@@ -30,18 -30,36 +30,19 @@@ import loggin
  
  _logger = logging.getLogger(__name__)
  
 -class account_coda_import(osv.osv_memory):
 -    _name = 'account.coda.import'
 -    _description = 'Import CODA File'
 -    _columns = {
 -        'coda_data': fields.binary('CODA File', required=True),
 -        'coda_fname': fields.char('CODA Filename', required=True),
 -        'note': fields.text('Log'),
 -    }
 +from openerp.addons.account_bank_statement_import import account_bank_statement_import as coda_ibs
  
 -    _defaults = {
 -        'coda_fname': 'coda.txt',
 -    }
 +coda_ibs.add_file_type(('coda', 'CODA'))
  
 -    def coda_parsing(self, cr, uid, ids, context=None, batch=False, codafile=None, codafilename=None):
 +class account_bank_statement_import(osv.TransientModel):
 +    _inherit = "account.bank.statement.import"
 +
 +    def process_coda(self, cr, uid, codafile=None, journal_id=False, context=None):
          if context is None:
              context = {}
 -        if batch:
 -            codafile = str(codafile)
 -            codafilename = codafilename
 -        else:
 -            data = self.browse(cr, uid, ids)[0]
 -            try:
 -                codafile = data.coda_data
 -                codafilename = data.coda_fname
 -            except:
 -                raise osv.except_osv(_('Error'), _('Wizard in incorrect state. Please hit the Cancel button'))
 -                return {}
          recordlist = unicode(base64.decodestring(codafile), 'windows-1252', 'strict').split('\n')
          statements = []
+         globalisation_comm = {}
          for line in recordlist:
              if not line:
                  pass
@@@ -52,7 -70,6 +53,6 @@@
                  statement['version'] = line[127]
                  if statement['version'] not in ['1', '2']:
                      raise osv.except_osv(_('Error') + ' R001', _('CODA V%s statements are not supported, please contact your bank') % statement['version'])
-                 statement['globalisation_stack'] = []
                  statement['lines'] = []
                  statement['date'] = time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT, time.strptime(rmspaces(line[5:11]), '%d%m%y'))
                  statement['separateApplication'] = rmspaces(line[83:88])
                      raise osv.except_osv(_('Error') + ' R1004', _("No matching Bank Account (with Account Journal) found.\n\nPlease set-up a Bank Account with as Account Number '%s' and as Currency '%s' and an Account Journal.") % (statement['acc_number'], statement['currency']))
                  statement['description'] = rmspaces(line[90:125])
                  statement['balance_start'] = float(rmspaces(line[43:58])) / 1000
 -                if line[42] == '1':    #1 = Debit, the starting balance is negative
 +                if line[42] == '1':  # 1 = Debit, the starting balance is negative
                      statement['balance_start'] = - statement['balance_start']
                  statement['balance_start_date'] = time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT, time.strptime(rmspaces(line[58:64]), '%d%m%y'))
                  statement['accountHolder'] = rmspaces(line[64:90])
                      statementLine['entryDate'] = time.strftime(tools.DEFAULT_SERVER_DATE_FORMAT, time.strptime(rmspaces(line[115:121]), '%d%m%y'))
                      statementLine['type'] = 'normal'
                      statementLine['globalisation'] = int(line[124])
-                     if len(statement['globalisation_stack']) > 0 and statementLine['communication'] != '':
-                         statementLine['communication'] = "\n".join([statement['globalisation_stack'][-1]['communication'], statementLine['communication']])
                      if statementLine['globalisation'] > 0:
-                         if len(statement['globalisation_stack']) > 0 and statement['globalisation_stack'][-1]['globalisation'] == statementLine['globalisation']:
-                             # Destack
-                             statement['globalisation_stack'].pop()
-                         else:
-                             #Stack
-                             statementLine['type'] = 'globalisation'
-                             statement['globalisation_stack'].append(statementLine)
+                         statementLine['type'] = 'globalisation'
+                         globalisation_comm[statementLine['ref_move']] = statementLine['communication']
+                     if not statementLine.get('communication'):
+                         statementLine['communication'] = globalisation_comm.get(statementLine['ref_move'], '')
                      statement['lines'].append(statementLine)
                  elif line[1] == '2':
                      if statement['lines'][-1]['ref'][0:4] != line[2:6]:
                      statement['balance_end_real'] = statement['balance_start'] + statement['balancePlus'] - statement['balanceMin']
          for i, statement in enumerate(statements):
              statement['coda_note'] = ''
 +            statement_line = []
              balance_start_check_date = (len(statement['lines']) > 0 and statement['lines'][0]['entryDate']) or statement['date']
              cr.execute('SELECT balance_end_real \
                  FROM account_bank_statement \
                  ORDER BY date DESC,id DESC LIMIT 1', (statement['journal_id'].id, balance_start_check_date))
              res = cr.fetchone()
              balance_start_check = res and res[0]
 -            if balance_start_check == None:
 +            if balance_start_check is None:
                  if statement['journal_id'].default_debit_account_id and (statement['journal_id'].default_credit_account_id == statement['journal_id'].default_debit_account_id):
                      balance_start_check = statement['journal_id'].default_debit_account_id.balance
                  else:
                  statement['coda_note'] = _("The CODA Statement %s Starting Balance (%.2f) does not correspond with the previous Closing Balance (%.2f) in journal %s!") % (statement['description'] + ' #' + statement['paperSeqNumber'], statement['balance_start'], balance_start_check, statement['journal_id'].name)
              if not(statement.get('period_id')):
                  raise osv.except_osv(_('Error') + ' R3006', _(' No transactions or no period in coda file !'))
 -            data = {
 +            statement_data = {
                  'name': statement['paperSeqNumber'],
                  'date': statement['date'],
                  'journal_id': statement['journal_id'].id,
                  'balance_start': statement['balance_start'],
                  'balance_end_real': statement['balance_end_real'],
              }
 -            statement['id'] = self.pool.get('account.bank.statement').create(cr, uid, data, context=context)
              for line in statement['lines']:
                  if line['type'] == 'information':
                      statement['coda_note'] = "\n".join([statement['coda_note'], line['type'].title() + ' with Ref. ' + str(line['ref']), 'Date: ' + str(line['entryDate']), 'Communication: ' + line['communication'], ''])
  
                      if 'counterpartyAddress' in line and line['counterpartyAddress'] != '':
                          note.append(_('Counter Party Address') + ': ' + line['counterpartyAddress'])
-                     line['name'] = "\n".join(filter(None, [line['counterpartyName'], line['communication']]))
-                     structured_com = ""
 -                    partner_id = None
+                     structured_com = False
 -                    bank_account_id = False
                      if line['communication_struct'] and 'communication_type' in line and line['communication_type'] == '101':
                          structured_com = line['communication']
 +                    bank_account_id = False
 +                    partner_id = False
                      if 'counterpartyNumber' in line and line['counterpartyNumber']:
 -                        ids = self.pool.get('res.partner.bank').search(cr, uid, [('acc_number', '=', str(line['counterpartyNumber']))])
 -                        if ids:
 -                            bank_account_id = ids[0]
 -                            partner_id = self.pool.get('res.partner.bank').browse(cr, uid, bank_account_id, context=context).partner_id.id
 -                        else:
 -                            #create the bank account, not linked to any partner. The reconciliation will link the partner manually
 -                            #chosen at the bank statement final confirmation time.
 -                            try:
 -                                type_model, type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'bank_normal')
 -                                type_id = self.pool.get('res.partner.bank.type').browse(cr, uid, type_id, context=context)
 -                                bank_code = type_id.code
 -                            except ValueError:
 -                                bank_code = 'bank'
 -                            bank_account_id = self.pool.get('res.partner.bank').create(cr, uid, {'acc_number': str(line['counterpartyNumber']), 'state': bank_code}, context=context)
 +                        bank_account_id, partner_id = self._detect_partner(cr, uid, str(line['counterpartyNumber']), identifying_field='acc_number', context=context)
-                     if 'communication' in line and line['communication'] != '':
+                     if line.get('communication', ''):
                          note.append(_('Communication') + ': ' + line['communication'])
 -                    data = {
 +                    line_data = {
-                         'name': line['name'],
+                         'name': structured_com or (line.get('communication', '') != '' and line['communication'] or '/'),
                          'note': "\n".join(note),
                          'date': line['entryDate'],
                          'amount': line['amount'],
                          'partner_id': partner_id,
-                         'ref': structured_com,
+                         'partner_name': line['counterpartyName'],
 -                        'statement_id': statement['id'],
+                         'ref': line['ref'],
                          'sequence': line['sequence'],
                          'bank_account_id': bank_account_id,
                      }
 -                    self.pool.get('account.bank.statement.line').create(cr, uid, data, context=context)
 +                    statement_line.append((0, 0, line_data))
              if statement['coda_note'] != '':
 -                self.pool.get('account.bank.statement').write(cr, uid, [statement['id']], {'coda_note': statement['coda_note']}, context=context)
 -        model, action_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'action_bank_reconcile_bank_statements')
 -        action = self.pool[model].browse(cr, uid, action_id, context=context)
 -        statements_ids = [statement['id'] for statement in statements]
 -        return {
 -            'name': action.name,
 -            'tag': action.tag,
 -            'context': {'statement_ids': statements_ids},
 -            'type': 'ir.actions.client',
 -        }
 +                statement_data.update({'coda_note': statement['coda_note']})
 +            statement_data.update({'journal_id': journal_id, 'line_ids': statement_line})
 +        return [statement_data]
  
  def rmspaces(s):
      return " ".join(s.split())
  
@@@ -44,18 -44,18 +44,18 @@@ Main Feature
  * Includes an extensible generic email composition assistant, that can turn into a mass-mailing assistant and is capable of interpreting simple *placeholder expressions* that will be replaced with dynamic data when each email is actually sent.
      """,
      'author': 'OpenERP SA',
-     'website': 'http://www.openerp.com',
+     'website': 'https://www.odoo.com/page/enterprise-social-network',
      'depends': ['base', 'base_setup'],
      'data': [
          'wizard/invite_view.xml',
          'wizard/mail_compose_message_view.xml',
          'mail_message_subtype.xml',
 -        'res_config_view.xml',
          'mail_message_view.xml',
          'mail_mail_view.xml',
          'mail_followers_view.xml',
          'mail_thread_view.xml',
          'mail_group_view.xml',
 +        'res_config_view.xml',
          'res_partner_view.xml',
          'data/mail_data.xml',
          'data/mail_group_data.xml',
@@@ -49,7 -49,7 +49,7 @@@ class mail_group(osv.Model)
          'name': fields.char('Name', required=True, translate=True),
          'description': fields.text('Description'),
          'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"),
 -        'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')], 'Privacy', required=True,
 +        'public': fields.selection([('public', 'Everyone'), ('private', 'Invited people only'), ('groups', 'Selected group of users')], 'Privacy', required=True,
              help='This group is visible by non members. \
              Invisible groups can add members through the invite button.'),
          'group_public_id': fields.many2one('res.groups', string='Authorized Group'),
      def message_get_email_values(self, cr, uid, id, notif_mail=None, context=None):
          res = super(mail_group, self).message_get_email_values(cr, uid, id, notif_mail=notif_mail, context=context)
          group = self.browse(cr, uid, id, context=context)
-         try:
-             headers = eval(res.get('headers', '{}'))
-         except Exception:
-             headers = {}
+         headers = {}
+         if res.get('headers'):
+             try:
+                 headers.update(eval(res['headers']))
+             except Exception:
+                 pass
          headers['Precedence'] = 'list'
          # avoid out-of-office replies from MS Exchange
          # http://blogs.technet.com/b/exchange/archive/2006/10/06/3395024.aspx
              # X-Forge-To: will replace To: after SMTP envelope is determined by ir.mail.server
              list_to = '"%s" <%s@%s>' % (group.name, group.alias_name, group.alias_domain)
              headers['X-Forge-To'] = list_to
-         res['headers'] = '%s' % headers
+         res['headers'] = repr(headers)
          return res
diff --combined addons/mail/mail_mail.py
@@@ -21,7 -21,7 +21,7 @@@
  
  import base64
  import logging
- import re
+ from email.utils import formataddr
  from urlparse import urljoin
  
  from openerp import api, tools
@@@ -30,7 -30,6 +30,7 @@@ from openerp.addons.base.ir.ir_mail_ser
  from openerp.osv import fields, osv
  from openerp.tools.safe_eval import safe_eval as eval
  from openerp.tools.translate import _
 +import openerp.tools as tools
  
  _logger = logging.getLogger(__name__)
  
@@@ -45,7 -44,7 +45,7 @@@ class mail_mail(osv.Model)
      _rec_name = 'subject'
  
      _columns = {
-         'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade'),
+         'mail_message_id': fields.many2one('mail.message', 'Message', required=True, ondelete='cascade', auto_join=True),
          'state': fields.selection([
              ('outgoing', 'Outgoing'),
              ('sent', 'Sent'),
@@@ -61,7 -60,6 +61,7 @@@
          'email_cc': fields.char('Cc', help='Carbon copy message recipients'),
          'body_html': fields.text('Rich-text Contents', help="Rich-text/HTML message"),
          'headers': fields.text('Headers', copy=False),
 +        'failure_reason': fields.text('Failure Reason', help="Failure reason. This is usually the exception thrown by the email server, stored to ease the debugging of mailing issues.", readonly=1),
          # Auto-detected based on create() - if 'mail_message_id' was passed then this mail is a notification
          # and during unlink() we will not cascade delete the parent and its attachments
          'notification': fields.boolean('Is Notification',
@@@ -70,7 -68,6 +70,6 @@@
  
      _defaults = {
          'state': 'outgoing',
-         'headers': '{}',
      }
  
      def default_get(self, cr, uid, fields, context=None):
              base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
              mail_model = mail.model or 'mail.thread'
              url = urljoin(base_url, self.pool[mail_model]._get_access_link(cr, uid, mail, partner, context=context))
-             return _("""<span class='oe_mail_footer_access'><small>about <a style='color:inherit' href="%s">%s %s</a></small></span>""") % (url, context.get('model_name', ''), mail.record_name)
+             return "<span class='oe_mail_footer_access'><small>%(access_msg)s <a style='color:inherit' href='%(portal_link)s'>%(portal_msg)s</a></small></span>" % {
+                 'access_msg': _('about') if mail.record_name else _('access'),
+                 'portal_link': url,
+                 'portal_msg': '%s %s' % (context.get('model_name', ''), mail.record_name) if mail.record_name else _('your messages'),
+             }
          else:
              return None
  
          is to be inherited to add custom content depending on some module."""
          body = mail.body_html
  
-         # generate footer
-         link = self._get_partner_access_link(cr, uid, mail, partner, context=context)
+         # generate access links for notifications or emails linked to a specific document with auto threading
+         link = None
+         if mail.notification or (mail.model and mail.res_id and not mail.no_auto_thread):
+             link = self._get_partner_access_link(cr, uid, mail, partner, context=context)
          if link:
              body = tools.append_content_to_html(body, link, plaintext=False, container_tag='div')
          return body
  
      def send_get_mail_to(self, cr, uid, mail, partner=None, context=None):
          """Forge the email_to with the following heuristic:
-           - if 'partner' and mail is a notification on a document: followers (Followers of 'Doc' <email>)
-           - elif 'partner', no notificatoin or no doc: recipient specific (Partner Name <email>)
+           - if 'partner', recipient specific (Partner Name <email>)
            - else fallback on mail.email_to splitting """
          if partner:
-             email_to = ['"%s" <%s>' % (partner.name, partner.email)]
+             email_to = [formataddr((partner.name, partner.email))]
          else:
              email_to = tools.email_split(mail.email_to)
          return email_to
                      mail.write({'state': 'sent', 'message_id': res})
                      mail_sent = True
                  else:
 -                    mail.write({'state': 'exception'})
 +                    mail.write({'state': 'exception', 'failure_reason': _('Error without exception. Probably due do sending an email without computed recipients.')})
                      mail_sent = False
  
                  # /!\ can't use mail.state here, as mail.refresh() will cause an error
                                    mail.id, mail.message_id)
                  raise
              except Exception as e:
 -                _logger.exception('failed sending mail.mail %s', mail.id)
 -                mail.write({'state': 'exception'})
 +                failure_reason = tools.ustr(e)
 +                _logger.exception('failed sending mail (id: %s) due to %s', mail.id, failure_reason)
 +                mail.write({'state': 'exception', 'failure_reason': failure_reason})
                  self._postprocess_sent_message(cr, uid, mail, context=context, mail_sent=False)
                  if raise_exception:
                      if isinstance(e, AssertionError):
@@@ -86,6 -86,9 +86,9 @@@
  .openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body pre {
      white-space: pre-wrap;
  }
+ .openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body blockquote {
+     font-size: 1em;
+ }
  .openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body * {
      text-overflow:ellipsis;
      word-wrap: break-word;
@@@ -93,6 -96,7 +96,6 @@@
  .openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body .oe_mail_cleaned {
      display: none;
  }
 -
  /* a) Indented Messages */
  
  .openerp .oe_mail .oe_msg_indented{
@@@ -448,7 -448,7 +448,7 @@@ class test_mail(TestMail)
                          'message_post: mail.mail notifications should have been auto-deleted!')
  
          # Test: notifications emails: to a and b, c is email only, r is author
-         test_emailto = ['"Administrator" <a@a>', '"Bert Tartopoils" <b@b>']
+         test_emailto = ['Administrator <a@a>', 'Bert Tartopoils <b@b>']
          # test_emailto = ['"Followers of -Pigs-" <a@a>', '"Followers of -Pigs-" <b@b>']
          self.assertEqual(len(sent_emails), 2,
                          'message_post: notification emails wrong number of send emails')
                              'message_post: notification email sent to more than one email address instead of a precise partner')
              self.assertIn(sent_email['email_to'][0], test_emailto,
                              'message_post: notification email email_to incorrect')
-             self.assertEqual(sent_email['reply_to'], '"YourCompany -Pigs-" <group+pigs@schlouby.fr>',
+             self.assertEqual(sent_email['reply_to'], u'"YourCompany \\"Pigs\\" !ù $%-" <group+pigs@schlouby.fr>',
                              'message_post: notification email reply_to incorrect')
              self.assertEqual(_subject, sent_email['subject'],
                              'message_post: notification email subject incorrect')
          self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', msg2_id)]), 'mail.mail notifications should have been auto-deleted!')
  
          # Test: emails send by server (to a, b, c, d)
-         test_emailto = [u'"Administrator" <a@a>', u'"Bert Tartopoils" <b@b>', u'"Carine Poilvache" <c@c>', u'"D\xe9d\xe9 Grosbedon" <d@d>']
+         test_emailto = [u'Administrator <a@a>', u'Bert Tartopoils <b@b>', u'Carine Poilvache <c@c>', u'D\xe9d\xe9 Grosbedon <d@d>']
          # test_emailto = [u'"Followers of Pigs" <a@a>', u'"Followers of Pigs" <b@b>', u'"Followers of Pigs" <c@c>', u'"Followers of Pigs" <d@d>']
          # self.assertEqual(len(sent_emails), 3, 'sent_email number of sent emails incorrect')
          for sent_email in sent_emails:
          mail_compose.send_mail(cr, user_raoul.id, [compose_id], {'mail_post_autofollow': True, 'mail_create_nosubscribe': True})
          group_pigs.refresh()
          message = group_pigs.message_ids[0]
 -
 +        # Test: mail_mail: notifications have been deleted
 +        self.assertFalse(self.mail_mail.search(cr, uid, [('mail_message_id', '=', message.id)]),'message_send: mail.mail message should have been auto-deleted!')
          # Test: mail.group: followers (c and d added by auto follow key; raoul not added by nosubscribe key)
          pigs_pids = [p.id for p in group_pigs.message_follower_ids]
          test_pids = [self.partner_admin_id, p_b_id, p_c_id, p_d_id]
  
          # Test: Pigs and Bird did receive their message
          test_msg_ids = self.mail_message.search(cr, uid, [], limit=2)
 +        mail_ids = self.mail_mail.search(cr, uid, [('mail_message_id', '=', message2.id)])
 +        mail_record_id = self.mail_mail.browse(cr, uid, mail_ids)[0]
 +        self.assertTrue(mail_record_id, "'message_send: mail.mail message should have in processing mail queue'" )
 +        #check mass mail state...
 +        test_mail_ids = self.mail_mail.search(cr, uid, [('state', '=', 'exception')])
 +        self.assertNotIn(mail_ids, test_mail_ids, 'compose wizard: Mail sending Failed!!')
          self.assertIn(message1.id, test_msg_ids, 'compose wizard: Pigs did not receive its mass mailing message')
          self.assertIn(message2.id, test_msg_ids, 'compose wizard: Bird did not receive its mass mailing message')
  
          # Test: first produced message: no subtype, name change tracked
          last_msg = self.group_pigs.message_ids[-1]
          self.assertFalse(last_msg.subtype_id, 'tracked: message should not have been linked to a subtype')
 -        self.assertIn(u'SelectedGroupOnly\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
 +        self.assertIn(u"Selectedgroupofusers\u2192Everyone", _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
          self.assertIn('Pigs', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
  
          # Test: change name as supername, public as private -> 2 subtypes
          last_msg = self.group_pigs.message_ids[-3]
          self.assertEqual(last_msg.subtype_id.id, mt_name_supername_id, 'tracked: message should be linked to mt_name_supername subtype')
          self.assertIn('Supername name', last_msg.body, 'tracked: message body does not hold the subtype description')
 -        self.assertIn(u'Public\u2192Private', _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
 +        self.assertIn(u"Everyone\u2192Invitedpeopleonly", _strip_string_spaces(last_msg.body), 'tracked: message body incorrect')
          self.assertIn(u'Pigs\u2192supername', _strip_string_spaces(last_msg.body), 'tracked feature: message body does not hold always tracked field')
  
          # Test: change public as public, group_public_id -> 2 subtypes, name always tracked
          last_msg = self.group_pigs.message_ids[-4]
          self.assertEqual(last_msg.subtype_id.id, mt_group_public_set_id, 'tracked: message should be linked to mt_group_public_set_id')
          self.assertIn('Group set', last_msg.body, 'tracked: message body does not hold the subtype description')
 -        self.assertIn(u'Private\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
 +        self.assertIn(u"Invitedpeopleonly\u2192Everyone", _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
          self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
          # Test: second produced message: mt_group_public_id, with name always tracked, public tracked on change
          last_msg = self.group_pigs.message_ids[-5]
          self.assertEqual(last_msg.subtype_id.id, mt_group_public_id, 'tracked: message should be linked to mt_group_public_id')
          self.assertIn('Group changed', last_msg.body, 'tracked: message body does not hold the subtype description')
 -        self.assertIn(u'Private\u2192Public', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
 +        self.assertIn(u"Invitedpeopleonly\u2192Everyone", _strip_string_spaces(last_msg.body), 'tracked: message body does not hold changed tracked field')
          self.assertIn(u'HumanResources/Employee\u2192Administration/Settings', _strip_string_spaces(last_msg.body), 'tracked: message body does not hold always tracked field')
  
          # Test: change group_public_id to False -> 1 subtype, name always tracked
@@@ -1,18 -1,16 +1,21 @@@
  <?xml version="1.0" encoding="utf-8"?>
  <openerp>
- <data>
- <template id="head" inherit_id="website.layout" name="Mail customization">
-     <xpath expr="//head" position="inside">
-         <script type="text/javascript" src="/mass_mailing/static/src/js/website_mass_mailing.editor.js" groups="base.group_website_publisher"></script>
+     <data>
+         <template id="assets_frontend" inherit_id="website.assets_frontend" name="wesbite_mass_mailing assets">
+             <xpath expr="." position="inside">
 +    </xpath>
 +</template>
 +
 +<template id="assets_frontend" inherit_id="website.assets_frontend" name="Mail Frontend Assets">
 +    <xpath expr="." position="inside">
-         <script type="text/javascript" src="/mass_mailing/static/src/js/website_mass_mailing.js"></script>
-     </xpath>
- </template>
+                 <script type="text/javascript" src="/mass_mailing/static/src/js/website_mass_mailing.js"></script>
+             </xpath>
+         </template>
  
- </data>
+         <template id="assets_editor" inherit_id="website.assets_editor" name="wesbite_mass_mailing Editor assets">
+             <xpath expr="/t" position="inside">
+                 <script type="text/javascript" src="/mass_mailing/static/src/js/website_mass_mailing.editor.js" groups="base.group_website_publisher"></script>
+             </xpath>
+         </template>
+     </data>
  </openerp>
diff --combined addons/mrp/mrp.py
@@@ -195,12 -195,12 +195,12 @@@ class mrp_bom(osv.osv)
          'name': fields.char('Name'),
          'code': fields.char('Reference', size=16),
          'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the bills of material without removing it."),
 -        'type': fields.selection([('normal', 'Normal'), ('phantom', 'Set')], 'BoM Type', required=True,
 +        'type': fields.selection([('normal','Manufacture this product as a normal bill of material'),('phantom','Sell and ship this product as a set of components(phantom)')], 'BoM Type', required=True,
                  help= "Set: When processing a sales order for this product, the delivery order will contain the raw materials, instead of the finished product."),
          'position': fields.char('Internal Reference', help="Reference to a position in an external plan."),
-         'product_tmpl_id': fields.many2one('product.template', 'Product', required=True),
+         'product_tmpl_id': fields.many2one('product.template', 'Product', domain="[('type', '!=', 'service')]", required=True),
          'product_id': fields.many2one('product.product', 'Product Variant',
-             domain="[('product_tmpl_id','=',product_tmpl_id)]",
+             domain="['&', ('product_tmpl_id','=',product_tmpl_id), ('type','!=', 'service')]",
              help="If a product variant is defined the BOM is available only for this product."),
          'bom_line_ids': fields.one2many('mrp.bom.line', 'bom_id', 'BoM Lines', copy=True),
          'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')),
          """
          if properties is None:
              properties = []
-         domain = None
          if product_id:
-             domain = ['|',('product_id', '=', product_id),('product_tmpl_id.product_variant_ids', '=', product_id)]
-         else:
+             if not product_tmpl_id:
+                 product_tmpl_id = self.pool['product.product'].browse(cr, uid, product_id).product_tmpl_id.id
+             domain = [
+                 '|',
+                     ('product_id', '=', product_id),
+                     '&',
+                         ('product_id', '=', False),
+                         ('product_tmpl_id', '=', product_tmpl_id)
+             ]
+         elif product_tmpl_id:
              domain = [('product_id', '=', False), ('product_tmpl_id', '=', product_tmpl_id)]
+         else:
+             # neither product nor template, makes no sense to search
+             return False
          if product_uom:
              domain +=  [('product_uom','=',product_uom)]
          domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
                              '|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
-         ids = self.search(cr, uid, domain)
+         # order to prioritize bom with product_id over the one without
+         ids = self.search(cr, uid, domain, order='product_id')
          for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
              if not set(map(int,bom.property_ids or [])) - set(properties or []):
                  return bom.id
@@@ -498,7 -509,7 +509,7 @@@ class mrp_production(osv.osv)
          for production in self.browse(cr, uid, ids, context=context):
              res[production.id] = True
              states = [x.state != 'assigned' for x in production.move_lines if x]
-             if any(states) or len(states) == 0:
+             if any(states) or len(states) == 0: #When no moves, ready_production will be False, but test_ready will pass
                  res[production.id] = False
          return res
  
          'priority': fields.selection([('0', 'Not urgent'), ('1', 'Normal'), ('2', 'Urgent'), ('3', 'Very Urgent')], 'Priority',
              select=True, readonly=True, states=dict.fromkeys(['draft', 'confirmed'], [('readonly', False)])),
  
-         'product_id': fields.many2one('product.product', 'Product', required=True, readonly=True, states={'draft': [('readonly', False)]}),
+         'product_id': fields.many2one('product.product', 'Product', required=True, readonly=True, states={'draft': [('readonly', False)]}, 
+                                       domain=[('type','!=','service')]),
          'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True, readonly=True, states={'draft': [('readonly', False)]}),
          'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}),
          'product_uos_qty': fields.float('Product UoS Quantity', readonly=True, states={'draft': [('readonly', False)]}),
          dicts = {}
          # Find product qty to be consumed and consume it
          for scheduled in production.product_lines:
+             if scheduled.product_id.type == 'service':
+                 continue
              product_id = scheduled.product_id.id
  
              consumed_qty = consumed_data.get(product_id, 0.0)
                  dicts[product_id] = {}
  
              # total qty of consumed product we need after this consumption
-             total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty)
+             if product_qty + produced_qty <= production.product_qty: 
+                 total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty)
+             else:
+                 total_consume = (production.product_qty * scheduled.product_qty / production.product_qty)
              qty = total_consume - consumed_qty
  
              # Search for quants related to this related move
          main_production_move = False
          if production_mode == 'consume_produce':
              # To produce remaining qty of final product
-             #vals = {'state':'confirmed'}
-             #final_product_todo = [x.id for x in production.move_created_ids]
-             #stock_mov_obj.write(cr, uid, final_product_todo, vals)
-             #stock_mov_obj.action_confirm(cr, uid, final_product_todo, context)
              produced_products = {}
              for produced_product in production.move_created_ids2:
                  if produced_product.scrapped:
                  produced_qty = produced_products.get(produce_product.product_id.id, 0)
                  subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context)
                  rest_qty = (subproduct_factor * production.product_qty) - produced_qty
-                 if float_compare(rest_qty, (subproduct_factor * production_qty), precision_rounding=produce_product.product_id.uom_id.rounding) < 0:
-                     prod_name = produce_product.product_id.name_get()[0][1]
-                     raise osv.except_osv(_('Warning!'), _('You are going to produce total %s quantities of "%s".\nBut you can only produce up to total %s quantities.') % ((subproduct_factor * production_qty), prod_name, rest_qty))
-                 if float_compare(rest_qty, 0, precision_rounding=produce_product.product_id.uom_id.rounding) > 0:
-                     lot_id = False
-                     if wiz:
-                         lot_id = wiz.lot_id.id
-                     new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context)
-                     stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context)
-                     if produce_product.product_id.id == production.product_id.id and new_moves:
-                         main_production_move = new_moves[0]
+                 lot_id = False
+                 if wiz:
+                     lot_id = wiz.lot_id.id
+                 new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context)
+                 stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context)
+                 if produce_product.product_id.id == production.product_id.id and new_moves:
+                     main_production_move = new_moves[0]
  
          if production_mode in ['consume', 'consume_produce']:
              if wiz:
          return res
  
      def test_ready(self, cr, uid, ids):
-         res = False
+         res = True
          for production in self.browse(cr, uid, ids):
-             if production.ready_production:
-                 res = True
+             if production.move_lines and not production.ready_production:
+                 res = False
          return res
      
      
      def _make_production_produce_line(self, cr, uid, production, context=None):
      def _make_production_consume_line(self, cr, uid, line, context=None):
          return self._make_consume_line_from_data(cr, uid, line.production_id, line.product_id, line.product_uom.id, line.product_qty, line.product_uos.id, line.product_uos_qty, context=context)
  
+     def _make_service_procurement(self, cr, uid, line, context=None):
+         prod_obj = self.pool.get('product.product')
+         if prod_obj.need_procurement(cr, uid, [line.product_id.id], context=context):
+             vals = {
+                 'name': line.production_id.name,
+                 'origin': line.production_id.name,
+                 'company_id': line.production_id.company_id.id,
+                 'date_planned': line.production_id.date_planned,
+                 'product_id': line.product_id.id,
+                 'product_qty': line.product_qty,
+                 'product_uom': line.product_uom.id,
+                 'product_uos_qty': line.product_uos_qty,
+                 'product_uos': line.product_uos.id,
+                 }
+             proc_obj = self.pool.get("procurement.order")
+             proc = proc_obj.create(cr, uid, vals, context=context)
+             proc_obj.run(cr, uid, [proc], context=context)
      def action_confirm(self, cr, uid, ids, context=None):
          """ Confirms production order.
          @return: Newly generated Shipment Id.
  
              stock_moves = []
              for line in production.product_lines:
-                 stock_move_id = self._make_production_consume_line(cr, uid, line, context=context)
-                 if stock_move_id:
+                 if line.product_id.type != 'service':
+                     stock_move_id = self._make_production_consume_line(cr, uid, line, context=context)
                      stock_moves.append(stock_move_id)
+                 else:
+                     self._make_service_procurement(cr, uid, line, context=context)
              if stock_moves:
                  self.pool.get('stock.move').action_confirm(cr, uid, stock_moves, context=context)
              production.write({'state': 'confirmed'}, context=context)
          """
          Checks the availability on the consume lines of the production order
          """
+         from openerp import workflow
          move_obj = self.pool.get("stock.move")
          for production in self.browse(cr, uid, ids, context=context):
              move_obj.action_assign(cr, uid, [x.id for x in production.move_lines], context=context)
+             if self.pool.get('mrp.production').test_ready(cr, uid, [production.id]):
+                 workflow.trg_validate(uid, 'mrp.production', production.id, 'moves_ready', cr)
  
  
      def force_production(self, cr, uid, ids, *args):
          @param *args: Arguments
          @return: True
          """
+         from openerp import workflow
          move_obj = self.pool.get('stock.move')
          for order in self.browse(cr, uid, ids):
              move_obj.force_assign(cr, uid, [x.id for x in order.move_lines])
+             if self.pool.get('mrp.production').test_ready(cr, uid, [order.id]):
+                 workflow.trg_validate(uid, 'mrp.production', order.id, 'moves_ready', cr)
          return True
  
  
diff --combined addons/mrp/mrp_view.xml
              <field name="model">mrp.property.group</field>
              <field name="arch" type="xml">
                  <form string="Properties categories">
 -                    <label for="name"/>
 -                    <field name="name"/>
 -                    <label for="description"/>
 -                    <field name="description"/>
 +                    <group>
 +                        <field name="name"/>
 +                    </group>
 +                    <separator string="Description"/>
 +                    <field name="description" nolabel="1"/>
                  </form>
              </field>
          </record>
@@@ -85,8 -84,8 +85,8 @@@
                          <field name="group_id"/>
                          <field name="composition"/>
                      </group>
 -                    <label for="description"/>
 -                    <field name="description"/>
 +                    <separator string="Description"/>
 +                    <field name="description" nolabel="1"/>
                  </form>
              </field>
          </record>
                                      <field name="costs_general_account_id" attrs="{ 'required':['|',('costs_cycle_account_id', '=', True),('costs_hour_account_id', '=', True)]}" groups="analytic.group_analytic_accounting"/>
                                  </group>
                              </group>
 -                            <label for="note"/>
 -                            <field name="note"/>
 +                            <separator string="Description"/>
 +                            <field name="note" nolabel="1"/>
                          </page>
                      </notebook>
                  </form>
                          <group>
                              <field name="product_tmpl_id" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/>
                              <field name="product_id"/>
-                             <field name="product_uom" class="oe_inline" on_change="onchange_uom(product_tmpl_id, product_uom)" groups="product.group_uom"/>
                              <label for="product_qty" string="Quantity"/>
                              <div>
                                  <field name="product_qty" class="oe_inline" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/>
                          <group>
                              <field name="name" groups="product.group_mrp_properties"/>
                              <field name="code" string="Reference"/>
 -                            <field name="type"/>
 +                            <field name="type" widget="radio"/>
                              <p colspan="2" class="oe_grey" attrs="{'invisible': [('type','=','normal')]}">
                                  When processing a sales order for this product, the delivery order
                                  will contain the raw materials, instead of the finished product.
                                      <field name="move_lines" nolabel="1" options="{'reload_on_button': true}">
                                          <tree colors="blue:state == 'draft';black:state in ('ready','assigned','in_production');gray:state in ('cancel','done');red:state in ('confirmed','waiting')" string="Products to Consume">
                                              <field name="product_id"/>
-                                             <field name="product_qty"  string="Quantity"/>
+                                             <field name="product_uom_qty"  string="Quantity"/>
                                              <field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
                                              <field name="state" invisible="1"/>
                                              <button name="%(mrp.move_consume)d"
                                         <tree colors="red:scrapped==True;blue:state == 'draft';black:state in ('confirmed','ready','in_production');gray:state == 'cancel' " string="Consumed Products" editable="bottom">
                                             <field name="product_id" readonly="1"/>
                                             <field name="restrict_lot_id" context="{'product_id': product_id}" groups="stock.group_production_lot"/>
-                                            <field name="product_qty" readonly="1"/>
+                                            <field name="product_uom_qty" readonly="1"/>
                                             <field name="product_uom" readonly="1" string="Unit of Measure" groups="product.group_uom"/>
                                             <field name="state" invisible="1"/>
                                             <field name="scrapped" invisible="1"/>
                                      <field name="move_created_ids" nolabel="1" options="{'reload_on_button': true}">
                                          <tree string="Products to Finish">
                                              <field name="product_id"/>
-                                             <field name="product_qty"/>
+                                             <field name="product_uom_qty"/>
                                              <field name="product_uom"  string="Unit of Measure" groups="product.group_uom"/>
                                              <field name="state" invisible="1"/>
-                                             <!--TODO: should have a partial picking here-->
                                          </tree>
                                      </field>
                                  </group>
                                      <field name="move_created_ids2" nolabel="1" options="{'reload_on_button': true}">
                                          <tree colors="red:scrapped==True;blue:state == 'draft';black:state in('confirmed','ready','in_production');gray:state in('cancel','done') " string="Finished Products">
                                              <field name="product_id" readonly="1"/>
-                                             <field name="product_qty" readonly="1"/>
+                                             <field name="product_uom_qty" readonly="1"/>
                                              <field name="restrict_lot_id" groups="stock.group_production_lot"/>
                                              <field name="product_uom" readonly="1" string="Unit of Measure" groups="product.group_uom"/>
                                              <field name="location_dest_id"  readonly="1" string="Destination Loc." widget="selection" groups="stock.group_locations"/>
                <p class="oe_view_nocontent_create">
                  Click to create a manufacturing order. 
                </p><p>
-                 A manufacuring order, based on a bill of materials, will
+                 A manufacturing order, based on a bill of materials, will
                  consume raw materials and produce finished products.
                </p><p>
                  Manufacturing orders are usually proposed automatically based
                <p class="oe_view_nocontent_create">
                  Click to start a new manufacturing order. 
                </p><p>
-                 A manufacuring order, based on a bill of materials, will
+                 A manufacturing order, based on a bill of materials, will
                  consume raw materials and produce finished products.
                </p><p>
                  Manufacturing orders are usually proposed automatically based
              <field name="name">product.template.procurement</field>
              <field name="model">product.template</field>
              <field name="inherit_id" ref="product.product_template_only_form_view"/>
+             <field name="groups_id" eval="[(4, ref('mrp.group_mrp_user'))]"/>
              <field name="arch" type="xml">
                  <div name="buttons" position="inside">
                      <button class="oe_inline oe_stat_button" name="%(template_open_bom)d" type="action"
-                         groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
-                         <field string="Bill of Materials" name="bom_count" widget="statinfo" />
-                     </button>
-                 </div>
-             </field>
-         </record>
-         <record model="ir.ui.view" id="product_product_form_view_bom_button">
-             <field name="name">product.product.procurement</field>
-             <field name="model">product.product</field>
-             <field name="inherit_id" ref="product.product_normal_form_view"/>
-             <field name="arch" type="xml">
-                 <div name="buttons" position="inside">
-                     <button class="oe_inline oe_stat_button" name="%(product_open_bom)d" type="action"
-                         groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
+                         attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
                          <field string="Bill of Materials" name="bom_count" widget="statinfo" />
                      </button>
                      <button class="oe_inline oe_stat_button" name="action_view_mos" type="object" 
-                         groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt">
+                         attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt">
                          <field string="Manufacturing" name="mo_count" widget="statinfo" />
                      </button>
                  </div>
              </field>
          </record>
-         
-         <record model="ir.ui.view" id="product_normal_form_view_bom_button">
-             <field name="name">product.product.mrp.button</field>
+         <record model="ir.ui.view" id="product_product_form_view_bom_button">
+             <field name="name">product.product.procurement</field>
              <field name="model">product.product</field>
              <field name="inherit_id" ref="product.product_normal_form_view"/>
+             <field name="groups_id" eval="[(4, ref('mrp.group_mrp_user'))]"/>
              <field name="arch" type="xml">
                  <div name="buttons" position="inside">
                      <button class="oe_inline oe_stat_button" name="action_view_bom" type="object" 
-                         groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
+                         attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
                          <field string="Bill of Materials" name="bom_count" widget="statinfo" />
                      </button>
                      <button class="oe_inline oe_stat_button" name="%(act_product_mrp_production)d" type="action" 
-                         groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt">
+                         attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt">
                          <field string="Manufacturing" name="mo_count" widget="statinfo" />
                      </button>
                  </div>
              </field>
          </record>
-         
          <record id="view_warehouse_inherited" model="ir.ui.view">
              <field name="name">Stock Warehouse Inherited</field>
              <field name="model">stock.warehouse</field>
@@@ -1,19 -1,44 +1,24 @@@
  # -*- coding: utf-8 -*-
  import logging
  
  from openerp import http
  from openerp.http import request
 -from openerp.addons.web.controllers.main import module_boot, login_redirect
 +from openerp.addons.web.controllers.main import login_redirect
  
  _logger = logging.getLogger(__name__)
  
  
  class PosController(http.Controller):
  
-     @http.route('/pos/web', type='http', auth='none')
+     @http.route('/pos/web', type='http', auth='user')
      def a(self, debug=False, **k):
+         cr, uid, context, session = request.cr, request.uid, request.context, request.session
  
-         if not request.session.uid:
+         if not session.uid:
              return login_redirect()
  
+         PosSession = request.registry['pos.session']
+         pos_session_ids = PosSession.search(cr, uid, [('state','=','opened'),('user_id','=',session.uid)], context=context)
 -        PosSession.login(cr,uid,pos_session_ids,context=context)
++        PosSession.login(cr, uid, pos_session_ids, context=context)
+         
 -        modules =  simplejson.dumps(module_boot(request.db))
 -        init =  """
 -                 var wc = new s.web.WebClient();
 -                 wc.show_application = function(){
 -                     wc.action_manager.do_action("pos.ui");
 -                 };
 -                 wc.setElement($(document.body));
 -                 wc.start();
 -                 """
 -
 -        html = request.registry.get('ir.ui.view').render(cr, session.uid,'point_of_sale.index',{
 -            'modules': modules,
 -            'init': init,
 -        })
 -
 -        return html
 +        return request.render('point_of_sale.index')
@@@ -72,7 -72,6 +72,7 @@@ class pos_config(osv.osv)
          'iface_scan_via_proxy' : fields.boolean('Scan via Proxy', help="Enable barcode scanning with a remotely connected barcode scanner"),
          'iface_invoicing': fields.boolean('Invoicing',help='Enables invoice generation from the Point of Sale'),
          'iface_big_scrollbars': fields.boolean('Large Scrollbars',help='For imprecise industrial touchscreens'),
 +        'iface_fullscreen':     fields.boolean('Fullscreen', help='Display the Point of Sale in full screen mode'),
          'receipt_header': fields.text('Receipt Header',help="A short text that will be inserted as a header in the printed receipt"),
          'receipt_footer': fields.text('Receipt Footer',help="A short text that will be inserted as a footer in the printed receipt"),
          'proxy_ip':       fields.char('IP Address', help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty', size=45),
              for record in self.browse(cr, uid, ids, context=context)
          )
  
+     def _check_company_location(self, cr, uid, ids, context=None):
+         for config in self.browse(cr, uid, ids, context=context):
+             if config.stock_location_id.company_id and config.stock_location_id.company_id.id != config.company_id.id:
+                 return False
+         return True
+     def _check_company_journal(self, cr, uid, ids, context=None):
+         for config in self.browse(cr, uid, ids, context=context):
+             if config.journal_id and config.journal_id.company_id.id != config.company_id.id:
+                 return False
+         return True
      _constraints = [
          (_check_cash_control, "You cannot have two cash controls in one Point Of Sale !", ['journal_ids']),
+         (_check_company_location, "The company of the stock location is different than the one of point of sale", ['company_id', 'stock_location_id']),
+         (_check_company_journal, "The company of the sale journal is different than the one of point of sale", ['company_id', 'journal_id']),
      ]
  
      def name_get(self, cr, uid, ids, context=None):
          return self.write(cr, uid, ids, {'state' : 'deprecated'}, context=context)
  
      def create(self, cr, uid, values, context=None):
-         proxy = self.pool.get('ir.sequence')
-         sequence_values = dict(
-             name='PoS %s' % values['name'],
-             padding=5,
-             prefix="%s/"  % values['name'],
-         )
-         sequence_id = proxy.create(cr, uid, sequence_values, context=context)
-         values['sequence_id'] = sequence_id
+         ir_sequence = self.pool.get('ir.sequence')
+         # force sequence_id field to new pos.order sequence
+         values['sequence_id'] = ir_sequence.create(cr, uid, {
+             'name': 'POS Order %s' % values['name'],
+             'padding': 4,
+             'prefix': "%s/"  % values['name'],
+             'code': "pos.order",
+             'company_id': values.get('company_id', False),
+         }, context=context)
+         # TODO master: add field sequence_line_id on model
+         # this make sure we always have one available per company
+         ir_sequence.create(cr, uid, {
+             'name': 'POS order line %s' % values['name'],
+             'padding': 4,
+             'prefix': "%s/"  % values['name'],
+             'code': "pos.order.line",
+             'company_id': values.get('company_id', False),
+         }, context=context)
          return super(pos_config, self).create(cr, uid, values, context=context)
  
      def unlink(self, cr, uid, ids, context=None):
@@@ -240,7 -265,8 +266,8 @@@ class pos_session(osv.osv)
                  required=True, readonly=True,
                  select=1, copy=False),
          
-         'sequence_number': fields.integer('Order Sequence Number'),
+         'sequence_number': fields.integer('Order Sequence Number', help='A sequence number that is incremented with each order'),
+         'login_number':  fields.integer('Login Sequence Number', help='A sequence number that is incremented each time a user resumes the pos session'),
  
          'cash_control' : fields.function(_compute_cash_all,
                                           multi='cash',
          'user_id' : lambda obj, cr, uid, context: uid,
          'state' : 'opening_control',
          'sequence_number': 1,
+         'login_number': 0,
      }
  
      _sql_constraints = [
              bank_statement_ids.append(statement_id)
  
          values.update({
-             'name' : pos_config.sequence_id._next(),
+             'name': self.pool['ir.sequence'].get(cr, uid, 'pos.session'),
              'statement_ids' : [(6, 0, bank_statement_ids)],
              'config_id': config_id
          })
                  statement.unlink(context=context)
          return super(pos_session, self).unlink(cr, uid, ids, context=context)
  
      def open_cb(self, cr, uid, ids, context=None):
          """
          call the Point Of Sale interface and set the pos.session to 'opened' (in progress)
              'target': 'self',
          }
  
+     def login(self, cr, uid, ids, context=None):
+         this_record = self.browse(cr, uid, ids[0], context=context)
+         this_record.write({
+             'login_number': this_record.login_number+1,
+         })
      def wkf_action_open(self, cr, uid, ids, context=None):
          # second browse because we need to refetch the data from the DB for cash_register_id
          for record in self.browse(cr, uid, ids, context=context):
  
      def wkf_action_close(self, cr, uid, ids, context=None):
          # Close CashBox
-         bsl = self.pool.get('account.bank.statement.line')
          for record in self.browse(cr, uid, ids, context=context):
              for st in record.statement_ids:
                  if abs(st.difference) > st.journal_id.amount_authorized_diff:
                  if (st.journal_id.type not in ['bank', 'cash']):
                      raise osv.except_osv(_('Error!'), 
                          _("The type of the journal for your payment method should be bank or cash "))
-                 if st.difference and st.journal_id.cash_control == True:
-                     if st.difference > 0.0:
-                         name= _('Point of Sale Profit')
-                     else:
-                         name= _('Point of Sale Loss')
-                     bsl.create(cr, uid, {
-                         'statement_id': st.id,
-                         'amount': st.difference,
-                         'ref': record.name,
-                         'name': name,
-                     }, context=context)
-                 if st.journal_id.type == 'bank':
-                     st.write({'balance_end_real' : st.balance_end})
                  getattr(st, 'button_confirm_%s' % st.journal_id.type)(context=context)
          self._confirm_orders(cr, uid, ids, context=context)
          self.write(cr, uid, ids, {'state' : 'closed'}, context=context)
@@@ -716,7 -733,13 +734,13 @@@ class pos_order(osv.osv)
      }
  
      def create(self, cr, uid, values, context=None):
-         values['name'] = self.pool.get('ir.sequence').get(cr, uid, 'pos.order')
+         if values.get('session_id'):
+             # set name based on the sequence specified on the config
+             session = self.pool['pos.session'].browse(cr, uid, values['session_id'], context=context)
+             values['name'] = session.config_id.sequence_id._next()
+         else:
+             # fallback on any pos.order sequence
+             values['name'] = self.pool.get('ir.sequence').get_id(cr, uid, 'pos.order', 'code', context=context)
          return super(pos_order, self).create(cr, uid, values, context=context)
  
      def test_paid(self, cr, uid, ids, context=None):
              'amount': data['amount'],
              'date': data.get('payment_date', time.strftime('%Y-%m-%d')),
              'name': order.name + ': ' + (data.get('payment_name', '') or ''),
-             'partner_id': order.partner_id and order.partner_id.id or None,
+             'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False,
          }
          account_def = property_obj.get(cr, uid, 'property_account_receivable', 'res.partner', context=context)
          args['account_id'] = (order.partner_id and order.partner_id.property_account_receivable \
                  values.update({
                      'date': order.date_order[:10],
                      'ref': order.name,
+                     'partner_id': order.partner_id and self.pool.get("res.partner")._find_accounting_partner(order.partner_id).id or False,
                      'journal_id' : sale_journal_id,
                      'period_id' : period,
                      'move_id' : move_id,
@@@ -1369,7 -1393,7 +1394,7 @@@ class product_template(osv.osv)
          'income_pdt': fields.boolean('Point of Sale Cash In', help="Check if, this is a product you can use to put cash into a statement for the point of sale backend."),
          'expense_pdt': fields.boolean('Point of Sale Cash Out', help="Check if, this is a product you can use to take cash from a statement for the point of sale backend, example: money lost, transfer to bank, etc."),
          'available_in_pos': fields.boolean('Available in the Point of Sale', help='Check if you want this product to appear in the Point of Sale'), 
-         'to_weight' : fields.boolean('To Weigh', help="Check if the product should be weighted (mainly used with self check-out interface)."),
+         'to_weight' : fields.boolean('To Weigh With Scale', help="Check if the product should be weighted using the hardware scale integration"),
          'pos_categ_id': fields.many2one('pos.category','Point of Sale Category', help="Those categories are used to group similar products for point of sale."),
      }
  
          'available_in_pos': True,
      }
  
-     def edit_ean(self, cr, uid, ids, context):
-         return {
-             'name': _("Assign a Custom EAN"),
-             'type': 'ir.actions.act_window',
-             'view_type': 'form',
-             'view_mode': 'form',
-             'res_model': 'pos.ean_wizard',
-             'target' : 'new',
-             'view_id': False,
-             'context':context,
-         }
  # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@@ -60,6 -60,7 +60,7 @@@
                                  </div>
                                  <field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator"/>
                              </group>
+                             <div class="oe_clear"/>
                          </page>
                          <page string="Payments">
                              <field name="statement_ids" colspan="4" nolabel="1">
          <menuitem action="product_pos_category_action" id="menu_product_pos_category" parent="menu_point_of_sale_product" sequence="0" />
          <!-- END -->
  
+         <record id="action_edit_ean" model="ir.actions.act_window">
+             <field name="name">Edit Ean</field>
+             <field name="res_model">pos.ean_wizard</field>
+             <field name="target">new</field>
+             <field name="view_type">form</field>
+             <field name="view_mode">form</field>
+         </record>
  
          <record id="product_template_form_view" model="ir.ui.view">
              <field name="name">product.template.form.inherit</field>
                      </group>
                  </group>
                  <field name="ean13" position="after">
-                     <button colspan="2" name="edit_ean" type="object" string="Set a Custom EAN" class="oe_link oe_edit_only"/>
+                     <button colspan="2" name="%(action_edit_ean)d" type="action" string="Set a Custom EAN" class="oe_link oe_edit_only"/>
                  </field>
              </field>
          </record>
                                  <field name="iface_invoicing" />
                              </group>
                              <group>
 +                                <field name="iface_fullscreen" />
                                  <field name="iface_big_scrollbars" />
                              </group>
                          </group>
  
      </data>
  </openerp>
 +
@@@ -26,8 -26,9 +26,9 @@@ class pos_order_report(osv.osv)
      _name = "report.pos.order"
      _description = "Point of Sale Orders Statistics"
      _auto = False
      _columns = {
-         'date': fields.date('Date Order', readonly=True),
+         'date': fields.datetime('Date Order', readonly=True),
          'partner_id':fields.many2one('res.partner', 'Partner', readonly=True),
          'product_id':fields.many2one('product.product', 'Product', readonly=True),
          'state': fields.selection([('draft', 'New'), ('paid', 'Closed'), ('done', 'Synchronized'), ('invoiced', 'Invoiced'), ('cancel', 'Cancelled')],
          'average_price': fields.float('Average Price', readonly=True,group_operator="avg"),
          'location_id':fields.many2one('stock.location', 'Location', readonly=True),
          'company_id':fields.many2one('res.company', 'Company', readonly=True),
-         'nbr':fields.integer('# of Lines', readonly=True),
-         'product_qty':fields.integer('# of Qty', readonly=True),
+         'nbr':fields.integer('# of Lines', readonly=True),  # TDE FIXME master: rename into nbr_lines
+         'product_qty':fields.integer('Product Quantity', readonly=True),
          'journal_id': fields.many2one('account.journal', 'Journal'),
          'delay_validation': fields.integer('Delay Validation'),
+         'product_categ_id': fields.many2one('product.category', 'Product Category', readonly=True),
      }
      _order = 'date desc'
  
                      s.location_id as location_id,
                      s.company_id as company_id,
                      s.sale_journal as journal_id,
-                     l.product_id as product_id
+                     l.product_id as product_id,
+                     pt.categ_id as product_categ_id
                  from pos_order_line as l
                      left join pos_order s on (s.id=l.order_id)
 -                    left join product_product p on (p.id=l.product_id)
 -                    left join product_template pt on (pt.id=p.product_tmpl_id)
 +                    left join product_product p on (l.product_id=p.id)
 +                    left join product_template pt on (p.product_tmpl_id=pt.id)
                      left join product_uom u on (u.id=pt.uom_id)
                  group by
-                     s.date_order, s.partner_id,s.state,
+                     s.date_order, s.partner_id,s.state, pt.categ_id,
                      s.user_id,s.location_id,s.company_id,s.sale_journal,l.product_id,s.create_date
                  having
                      sum(l.qty * u.factor) != 0)""")
@@@ -7,12 -7,14 +7,12 @@@
              <field name="model">res.partner</field>
              <field name="inherit_id" ref="base.view_partner_form"/>
              <field name="arch" type="xml">
 -                <notebook position="inside">
 -                    <page string="Point of Sale"> 
 -                        <group>
 -                            <field name="ean13" />
 -                            <button name="%(action_edit_ean)d" type="action" string="Set a Custom EAN" />
 -                        </group>
 -                    </page>
 -                </notebook>
 +                <group name="point_of_sale" position="replace">
 +                    <group string="Point of Sale">
 +                        <field name="ean13" />
-                         <button name="edit_ean" type="object" string="Edit" />
++                        <button name="%(action_edit_ean)d" type="action" string="Set a Custom EAN" />
 +                    </group>
 +                </group>
              </field>
          </record>
  
  .ui-dialog .ui-icon-closethick{
      float: right;
  }
 +div.modal.in {
 +    position: absolute;
 +    background: white;
 +    padding: 20px;
 +    box-shadow: 0px 10px 20px black;
 +    border-radius: 3px;
 +    max-width: 600px;
 +    max-height: 400px;
 +    margin-top: -200px;
 +    margin-left: -300px;
 +    top: 50%;
 +    left: 50%;
 +}
  /* --- Generic Restyling and Resets --- */
  
  html {
@@@ -267,7 -254,7 +267,7 @@@ td 
      left:0;
      top:0;
      width: 100%;
-     height: 34px;
+     height: 48px;
      margin:0;
      padding:0;
      color: gray;
      vertical-align: middle;
  }
  .pos .pos-logo {
-     height: 32px;
-     width: 116px;
-     margin-left:5px;
+     height: 35px;
+     margin-left: 7px;
+     margin-top: 2px;
      vertical-align:middle;
  } 
  .pos .pos-branding .username{
      color:#DDD;
      font-size:16px;
      margin-right:32px;
-     margin-top:10px;
+     line-height: 48px;
      font-style:italic;
  }
  
      display: inline-block;
      box-sizing: border-box;
      -moz-box-sizing: border-box;
-     height: 32px;
+     height: 46px;
      padding: 4px 8px;
      margin: 3px;
      margin-bottom: 0px;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px;
      vertical-align: top;
-     line-height: 26px;
+     line-height: 42px;
      text-align: center;
      box-shadow: 0px -5px 10px -6px rgb(82,82,82) inset;
      cursor: pointer;
-     min-width: 34px;
+     min-width: 45px;
  }
  .pos .order-button.selected{
      font-weight: 900;
      background: #EEEEEE;
      color: rgb(75,75,75);
-     height: 31px;
+     height: 45px;
      border-bottom: solid 1px rgb(196, 196, 196);
      box-shadow: none;
  }
  }
  
  .pos .order-button.square{
      margin-left:1px;
      background: #5c5c5c;
      color: rgb(160,160,160);
+     font-size: 18px;
+     line-height: 45px;
  }
  .pos .order-button:not(.square) > .fa {
      font-size: 16px;
  /*  c) The session buttons */
  
  .pos .pos-rightheader .header-button{
-     float:right;
-     height:32px;
-     padding-left:10px;
-     padding-right:10px;
-     border-right:  1px solid #292929;
-     border-left:  1px solid #292929;
-     color:#DDD;
-     line-height:32px;
-     text-align:center;
+     float: right;
+     height: 48px;
+     padding-left: 16px;
+     padding-right: 16px;
+     border-right: 1px solid #292929;
+     border-left: 1px solid #292929;
+     color: #DDD;
+     line-height: 48px;
+     text-align: center;
      cursor: pointer;
  
      -webkit-transition-property: background;
      background: rgba(0,0,0,0.2);
      color:#EEE;
  }
+ .pos .pos-rightheader .header-button.confirm {
+     background: #359766;
+     color: white;
+     font-weight: bold;
+ }
  
  /*  c) The notifications indicator */
  
  .pos .oe_status{
      float:right; 
      color: rgba(255,255,255,0.4);
-     padding: 8px;
-     line-height:16px;
-     font-size:16px;
+     padding: 14px;
+     line-height: 20px;
+     font-size: 20px;
      vertical-align:middle;
      font-style: italic;
      cursor:pointer;
  .pos .pos-content {
      width: 100%;
      position: absolute;
-     top: 34px;
+     top: 48px;
      bottom: 0;
      background: #F0EEEE;
  }
      margin-right: 4px;
  }
  
 -.pos .control-button.highlight{
 -    background: #6EC89B;
 -    border: solid 1px #6EC89B;
 -    color: white;
 +.pos .control-button.highlight,
 +.pos .button.highlight {
 +    background: #6EC89B !important;
 +    border: solid 1px #6EC89B !important;
 +    color: white !important;
  }
  .pos .control-button:active {
      background: #7F82AC;
      color: inherit;
  }
  
 -/*  ********* The paypad contains the payment buttons ********* */
 +/*  ********* The actionpad (payment, set customer) ********* */
  
 -.pos .paypad {
 -    padding: 8px 4px 8px 8px;
 +.pos .actionpad{
 +    padding: 8px 3px 8px 19px;
      display: inline-block;
      text-align: center;
      vertical-align: top;
 -    width: 205px;
 +    width: 183px;
      max-height: 350px;
      overflow-y: auto;
      overflow-x: hidden;
  }
 -.pos .paypad button {
 -    height: 39px;
 +.pos .actionpad .button {
 +    height: 50px;
      display: block;
      width: 100%;
      margin: 0px -6px 4px -2px;
      color: #555555;
      font-size: 14px;
  }
 -.pos .paypad button:active, 
 -.pos .numpad button:active, 
 -.pos .numpad .selected-mode, 
 -.pos .popup  button:active{
 -    border: none;
 +.pos .actionpad .button.pay {
 +    height: 158px;
 +}
 +.pos .actionpad .button.pay .fa {
 +    display: block;
 +    font-size: 32px;
 +    line-height: 54px;
 +    padding-top: 6px;
 +    background: rgb(86, 86, 86);
      color: white;
 -    background: #7f82ac;
 +    width: 60px;
 +    margin: auto;
 +    border-radius: 30px;
 +    margin-bottom: 10px;
  }
  
  /*  ********* The Numpad ********* */
  .pos .numpad button {
      height: 50px;
      width: 50px;
 -    margin: 0px 0px 4px 0px;
 +    margin: 0px 3px 4px 0px;
      font-weight: bold;
      vertical-align: middle;
      color: #555555;
  
  .pos .categories {
      position: relative;
-     border-bottom: 1px solid #cecece;
  }
  .pos .categories h4 {
      display: inline-block;
      margin: 9px 5px;
  }
  
- .pos .category-list{
+ .pos .category-list {
      text-align: left;
      padding: 10px;
      background: rgb(229, 229, 229);
+     border-bottom: 1px solid #cecece;
+ }
+ .pos .category-list.simple {
+     padding: 0px;
+     background: #cecece;
+     display: -webkit-flex;
+     display: flex;
+     -webkit-flex-flow: row wrap;
+     flex-flow: row wrap;
+     border-bottom: none;
  }
  /*  d) the category button */
  
  .pos .category-button {
      position: relative;
      display: inline-block;
      font-size: 14px;
-     margin-right:10px;
+     margin-right: 1px;
+     margin-bottom: 1px;
      padding: 5px 12px;
-     line-height: 24px;
-     border-radius: 3px;
+     line-height: 32px;
+     flex-grow: 1;
+     -webkit-flex-grow: 1;
      cursor: pointer;
-     border: 1px solid #cacaca;
      background: #e2e2e2;
  }
  .pos .category-simple-button:active{
      color: white;
      background: #7f82ac;
-     border: 1px solid #7f82ac;
  
      -webkit-transition-property: background, border;
      -webkit-transition-duration: 0.2s;
      overflow-y: auto;
      border-right: dashed 1px rgb(215,215,215);
  }
 +.screen .left-content.pc40{
 +    right: 66%;
 +}
  .screen .right-content{
      position: absolute;
      right:0px; top: 64px; bottom: 0px;
      overflow-x: hidden;
      overflow-y: auto;
  }
 +.screen .right-content.pc60{
 +    left:34%
 +}
  .screen .centered-content{
      position: absolute;
      right:25%; top: 64px; bottom: 0px;
      position:relative;
  }
  
 -/* b) The payment screen */
  
 +/* b) The payment screen */
  
 -.pos .pos-payment-container {
 +.pos .payment-numpad {
      display: inline-block;
 -    font-size: 16px;
 -    text-align: left;
 -    width: 360px;
 -}
 -.pos .payment-due-total {
 +    width: 50%;
 +    box-sizing: border-box;
 +    padding: 8px;
      text-align: center;
 -    font-weight: bold;
 -    font-size: 48px;
 -    margin: 27px;
 -    text-shadow: 0px 2px rgb(202, 202, 202);
 +    float: left;
  }
 -.pos .paymentline{
 -    position: relative;
 +.pos .payment-numpad .numpad button {
 +    width: 66px;
 +    height: 66px;
 +}
 +
 +.pos .paymentlines-container {
 +    padding: 16px;
 +    padding-top: 0;
 +    border-bottom: dashed 1px gainsboro;
 +    min-height: 154px;
 +}
 +
 +.pos .paymentlines {
 +    width: 100%;
 +}
 +.pos .paymentlines .controls {
 +    width: 40px;
 +}
 +.pos .paymentlines .label > * {
 +    font-size: 16px;
      padding: 8px;
 -    border-box: 3px;
 -    box-sizing: border-box;
 -    -moz-box-sizing: border-box;
 +}
 +.pos .paymentlines tbody{
 +    background: white;
      border-radius: 3px;
  }
 -.pos .paymentline-name{
 -    margin-bottom: 8px;
 +.pos .paymentline{
 +    font-size: 22px;
  }
 -.pos .paymentline-input{
 -    font-size: 20px;
 -    font-family: Lato;
 -    display: block;
 -    width: 100%;
 -    box-sizing: border-box;
 -    -moz-box-sizing: border-box;
 -    outline: none;
 -    border: none;
 -    padding: 6px 8px;
 +.pos .paymentline.selected {
 +    font-size: 22px;
 +    background: #6EC89B;
 +    color: white;
 +}
 +.pos .paymentline.selected .edit {
      background: white;
 -    color: #484848;
 -    text-align: right;
 +    color: #6EC89B;
 +    outline: 3px blue;
 +    box-shadow: 0px 0px 0px 3px #6EC89B;
 +    position: relative;
      border-radius: 3px;
 -    box-shadow: 0px 2px rgba(143, 143, 143, 0.3) inset;
  }
  
 -.pos .paymentline-input:focus{
 -    color: rgb(130, 124, 255);
 -    box-shadow: 0px 2px rgba(219, 219, 219, 0.3) inset;
 -    -webkit-animation: all 250ms linear;
 +.pos .paymentline > *{
 +    padding: 8px 12px;
  }
 -
 -.paymentline-delete {
 -    width: 32px;
 -    height: 32px;
 -    padding: 5px;
 -    text-align: center;
 -    box-sizing: border-box;
 -    -moz-box-sizing: border-box;
 -    position: absolute;
 -    bottom: 10px;
 -    left: 8px;
 +.pos .paymentline .col-due,
 +.pos .paymentline .col-tendered,
 +.pos .paymentline .col-change {
 +    min-width: 90px;
 +}
 +.pos .paymentline .col-change.highlight {
 +    background: rgb(184, 152, 204);
 +}
 +.pos .paymentline .col-name {
 +    font-size: 16px;
 +}
 +.pos .paymentline .delete-button{
      cursor: pointer;
 +    text-align: center;
  }
 -
 -.pos .pos-payment-container .left-block{
 +.pos .payment-buttons {
      display: inline-block;
 -    width:49%;
 -    margin:0;
 -    padding:0;
 -    text-align:left;
 +    width: 50%;
 +    box-sizing: border-box;
 +    padding: 16px;
 +    float: right;
  }
 -.pos .pos-payment-container .infoline{
 -    margin-top:5px;
 -    margin-bottom:5px;
 +
 +.payment-screen   .payment-buttons .button {
 +    background: rgb(221, 221, 221);
 +    line-height: 48px;
 +    margin-bottom: 4px;
 +    border-radius: 3px;
 +    font-size: 16px;
      padding: 0px 8px;
 -    opacity: 0.5;
 -}
 -.pos .pos-payment-container .infoline.bigger{
 -    opacity: 1;
 -    font-size: 20px;
 +    border: solid 1px rgb(202, 202, 202);
 +    cursor: pointer;
 +    text-align: center;
  }
 -.pos .pos-payment-container .right-block{
 -    display: inline-block;
 -    width:49%;
 -    margin:0;
 -    padding:0;
 -    text-align:right;
 +.payment-screen  .paymentlines-empty .total {
 +    text-align: center;
 +    padding: 24px 0px 18px;
 +    font-size: 64px;
 +    color: #43996E;
 +    text-shadow: 0px 2px white, 0px 2px 2px rgba(0, 0, 0, 0.27);
  }
 -.pos .paymentline.selected{
 -    background: rgb(220,220,220);
 +.payment-screen  .paymentlines-empty .message {
 +    text-align: center;
  }
  
  /* c) The receipt screen */
  
 +.pos .receipt-screen .centered-content .button {
 +    line-height: 40px;
 +    padding: 3px 13px;
 +    font-size: 20px;
 +    text-align: center;
 +    background: rgb(230, 230, 230);
 +    margin: 16px;
 +    margin-bottom: 0px;
 +    border-radius: 3px;
 +    border: solid 1px rgb(209, 209, 209);
 +    cursor: pointer;
 +}
  .pos .pos-receipt-container {
      font-size: 0.75em;
 +    text-align: center;
  }
  
  .pos .pos-sale-ticket {
  }
  
  @media print {
 +    body {
 +        margin: 0;
 +    }
      .oe_leftbar,
      .pos .pos-topheader, 
      .pos .pos-leftpane, 
 -    .pos .keyboard_frame {
 +    .pos .keyboard_frame, 
 +    .pos .receipt-screen header,
 +    .pos .receipt-screen .top-content,
 +    .pos .receipt-screen .centered-content .button {
          display: none !important;
      }
      .pos,
          left: 0px !important;
          background-color: white;
      }
      .pos .receipt-screen {
          text-align: left;
      }
 +    .pos .receipt-screen .centered-content{
 +        position: static;
 +        border: none;
 +        margin: none;
 +    }
 +    .pos .pos-receipt-container {
 +        text-align: left;
 +    }
      .pos-actionbar {
          display: none !important;
      }
      font-size: 24px;
      vertical-align: top;
  }
 -.splitbill-screen .paymentmethods {
 +.splitbill-screen .paymentmethods,
 +.payment-screen   .paymentmethods {
      margin: 16px;
  }
 -.splitbill-screen .paymentmethod {
 +.splitbill-screen .paymentmethod,
 +.payment-screen   .paymentmethod {
      background: rgb(221, 221, 221);
      line-height: 40px;
      margin-bottom: 4px;
      cursor: pointer;
      text-align: center;
  }
 +.splitbill-screen .paymentmethod.active,
 +.payment-screen   .paymentmethod.active {
 +    background: #6EC89B;
 +    color: white;
 +    border-color: #6EC89B;
 +}
 +
 +
  
  /*  ********* The ActionBarWidget  ********* */
  
  .pos .popup .comment{
      font-weight: normal;
      font-size: 18px;
 -    margin: 0px 16px;
 +    margin: 0px 16px 16px 16px;
 +}
 +.pos .popup .comment.traceback {
 +    height: 264px;
 +    overflow: auto;
 +    font-size: 14px;
 +    text-align: left;
 +    font-family: 'Inconsolata';
 +    -webkit-user-select: text;
 +       -moz-user-select: text;
 +            user-select: text;
  }
+ .pos .popup .comment.traceback {
+     height: 264px;
+     overflow: auto;
+     font-size: 14px;
+     text-align: left;
+     font-family: 'Inconsolata';
+     -webkit-user-select: text;
+        -moz-user-select: text;
+             user-select: text;
+ }
  .pos .popup .footer{
      position:absolute;
      bottom:0;
@@@ -159,7 -159,7 +159,7 @@@ function openerp_pos_models(instance, m
              loaded: function(self,users){ self.users = users; },
          },{
              model:  'res.partner',
-             fields: ['name','street','city','country_id','phone','zip','mobile','email','ean13'],
+             fields: ['name','street','city','country_id','phone','zip','mobile','email','ean13','write_date'],
              domain: null,
              loaded: function(self,partners){
                  self.partners = partners;
              loaded: function(self,taxes){ self.taxes = taxes; },
          },{
              model:  'pos.session',
-             fields: ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at','sequence_number'],
+             fields: ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at','sequence_number','login_number'],
              domain: function(self){ return [['state','=','opened'],['user_id','=',self.session.uid]]; },
-             loaded: function(self,pos_sessions){ self.pos_session = pos_sessions[0]; },
+             loaded: function(self,pos_sessions){
+                 self.pos_session = pos_sessions[0]; 
+                 var orders = self.db.get_orders();
+                 for (var i = 0; i < orders.length; i++) {
+                     self.pos_session.sequence_number = Math.max(self.pos_session.sequence_number, orders[i].data.sequence_number+1);
+                 }
+             },
          },{
              model: 'pos.config',
              fields: [],
              },
          },{
              model:  'product.product',
-             fields: ['name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code', 'variants',
+             fields: ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code', 
                       'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
                       'product_tmpl_id'],
              domain:  function(self){ return [['sale_ok','=',true],['available_in_pos','=',true]]; },
-             context: function(self){ return { pricelist: self.pricelist.id }; },
+             context: function(self){ return { pricelist: self.pricelist.id, display_default_code: false }; },
              loaded: function(self, products){
                  self.db.add_products(products);
              },
                  self.cashregisters = bankstatements;
              },
          },{
+             label: 'fonts',
+             loaded: function(self){
+                 var fonts_loaded = new $.Deferred();
 -
+                 // Waiting for fonts to be loaded to prevent receipt printing
+                 // from printing empty receipt while loading Inconsolata
+                 // ( The font used for the receipt ) 
+                 waitForWebfonts(['Lato','Inconsolata'], function(){
+                     fonts_loaded.resolve();
+                 });
 -
+                 // The JS used to detect font loading is not 100% robust, so
+                 // do not wait more than 5sec
+                 setTimeout(function(){
+                     fonts_loaded.resolve();
+                 },5000);
+                 return fonts_loaded;
+             },
+         },{
+             label: 'pictures',
              loaded: function(self){
                  self.company_logo = new Image();
                  self.company_logo.crossOrigin = 'anonymous';
                      loaded.resolve();
                  }else{
                      var model = self.models[index];
-                     self.pos_widget.loading_message(_t('Loading')+' '+(model.model || ''), progress);
+                     self.pos_widget.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress);
                      var fields =  typeof model.fields === 'function'  ? model.fields(self,tmp)  : model.fields;
                      var domain =  typeof model.domain === 'function'  ? model.domain(self,tmp)  : model.domain;
                      var context = typeof model.context === 'function' ? model.context(self,tmp) : model.context; 
              return loaded;
          },
  
+         // reload the list of partner, returns as a deferred that resolves if there were
+         // updated partners, and fails if not
+         load_new_partners: function(){
+             var self = this;
+             var def  = new $.Deferred();
+             var fields = _.find(this.models,function(model){ return model.model === 'res.partner'; }).fields;
+             new instance.web.Model('res.partner')
+                 .query(fields)
+                 .filter([['write_date','>',this.db.get_partner_write_date()]])
+                 .all({'timeout':3000, 'shadow': true})
+                 .then(function(partners){
+                     if (self.db.add_partners(partners)) {   // check if the partners we got were real updates
+                         def.resolve();
+                     } else {
+                         def.reject();
+                     }
+                 }, function(){ def.reject(); });    
+             return def;
+         },
          // this is called when an order is removed from the order collection. It ensures that there is always an existing
          // order and a valid selected order
          on_removed_order: function(removed_order,index,reason){
          // wrapper around the _save_to_server that updates the synch status widget
          _flush_orders: function(orders, options) {
              var self = this;
--
              this.set('synch',{ state: 'connecting', pending: orders.length});
  
              return self._save_to_server(orders, options).done(function (server_ids) {
                  unit_name:          this.get_unit().name,
                  price:              this.get_unit_price(),
                  discount:           this.get_discount(),
-                 product_name:       this.get_product().name,
+                 product_name:       this.get_product().display_name,
                  price_display :     this.get_display_price(),
                  price_with_tax :    this.get_price_with_tax(),
                  price_without_tax:  this.get_price_without_tax(),
      module.Order = Backbone.Model.extend({
          initialize: function(attributes){
              Backbone.Model.prototype.initialize.apply(this, arguments);
+             this.pos = attributes.pos; 
+             this.sequence_number = this.pos.pos_session.sequence_number++;
              this.uid =     this.generateUniqueId();
              this.set({
                  creationDate:   new Date(),
                  orderLines:     new module.OrderlineCollection(),
                  paymentLines:   new module.PaymentlineCollection(),
-                 name:           "Order " + this.uid,
+                 name:           _t("Order ") + this.uid,
                  client:         null,
              });
-             this.pos = attributes.pos; 
              this.selected_orderline   = undefined;
              this.selected_paymentline = undefined;
              this.screen_data = {};  // see ScreenSelector
              this.receipt_type = 'receipt';  // 'receipt' || 'invoice'
              this.temporary = attributes.temporary || false;
-             this.sequence_number = this.pos.pos_session.sequence_number++;
 +            this.to_invoice = false;
              return this;
          },
          is_empty: function(){
              return (this.get('orderLines').models.length === 0);
          },
+         // Generates a public identification number for the order.
+         // The generated number must be unique and sequential. They are made 12 digit long
+         // to fit into EAN-13 barcodes, should it be needed 
          generateUniqueId: function() {
-             return new Date().getTime();
+             function zero_pad(num,size){
+                 var s = ""+num;
+                 while (s.length < size) {
+                     s = "0" + s;
+                 }
+                 return s;
+             }
+             return zero_pad(this.pos.pos_session.id,5) +'-'+
+                    zero_pad(this.pos.pos_session.login_number,3) +'-'+
+                    zero_pad(this.sequence_number,4);
          },
          addOrderline: function(line){
              if(line.order){
                  return sum + paymentLine.get_amount();
              }), 0);
          },
 -        getChange: function() {
 -            return this.getPaidTotal() - this.getTotalTaxIncluded();
 +        getChange: function(paymentline) {
 +            if (!paymentline) {
 +                var change = this.getPaidTotal() - this.getTotalTaxIncluded();
 +            } else {
 +                var change = -this.getTotalTaxIncluded(); 
 +                var lines  = this.get('paymentLines').models;
 +                for (var i = 0; i < lines.length; i++) {
 +                    change += lines[i].get_amount();
 +                    if (lines[i] === paymentline) {
 +                        break;
 +                    }
 +                }
 +            }
 +            return round_pr(Math.max(0,change), this.pos.currency.rounding);
 +        },
 +        getDueLeft: function(paymentline) {
 +            if (!paymentline) {
 +                var due = this.getTotalTaxIncluded() - this.getPaidTotal();
 +            } else {
 +                var due = this.getTotalTaxIncluded();
 +                var lines = this.get('paymentLines').models;
 +                for (var i = 0; i < lines.length; i++) {
 +                    if (lines[i] === paymentline) {
 +                        break;
 +                    } else {
 +                        due -= lines[i].get_amount();
 +                    }
 +                }
 +            }
 +            return round_pr(Math.max(0,due), this.pos.currency.rounding);
          },
 -        getDueLeft: function() {
 -            return this.getTotalTaxIncluded() - this.getPaidTotal();
 +        isPaid: function(){
 +            return this.getDueLeft() === 0;
 +        },
 +        isPaidWithCash: function(){
 +            return !!this.get('paymentLines').find( function(pl){
 +                return pl.cashregister.journal.type === 'cash';
 +            });
 +        },
 +        finalize: function(){
 +            this.destroy();
          },
          // sets the type of receipt 'receipt'(default) or 'invoice'
          set_receipt_type: function(type){
                  }
              }
          },
 +        set_to_invoice: function(to_invoice) {
 +            this.to_invoice = to_invoice;
 +        },
 +        is_to_invoice: function(){
 +            return this.to_invoice;
 +        },
 +        // remove all the paymentlines with zero money in it
 +        clean_empty_paymentlines: function() {
 +            var lines = this.get('paymentLines').models;
 +            var empty = [];
 +            for ( var i = 0; i < lines.length; i++) {
 +                if (!lines[i].get_amount()) {
 +                    empty.push(lines[i]);
 +                }
 +            }
 +            for ( var i = 0; i < empty.length; i++) {
 +                this.removePaymentline(empty[i]);
 +            }
 +        },
          //see set_screen_data
          get_screen_data: function(key){
              return this.screen_data[key];
@@@ -211,6 -211,26 +211,6 @@@ function openerp_pos_screens(instance, 
          barcode_error_action: function(code){
              this.pos_widget.screen_selector.show_popup('error-barcode',code.code);
          },
 -        // shows an action bar on the screen. The actionbar is automatically shown when you add a button
 -        // with add_action_button()
 -        show_action_bar: function(){
 -            this.pos_widget.action_bar.show();
 -        },
 -
 -        // hides the action bar. The actionbar is automatically hidden when it is empty
 -        hide_action_bar: function(){
 -            this.pos_widget.action_bar.hide();
 -        },
 -
 -        // adds a new button to the action bar. The button definition takes three parameters, all optional :
 -        // - label: the text below the button
 -        // - icon:  a small icon that will be shown
 -        // - click: a callback that will be executed when the button is clicked.
 -        // the method returns a reference to the button widget, and automatically show the actionbar.
 -        add_action_button: function(button_def){
 -            this.show_action_bar();
 -            return this.pos_widget.action_bar.add_new_button(button_def);
 -        },
  
          // this method shows the screen and sets up all the widget related to this screen. Extend this method
          // if you want to alter the behavior of the screen.
                  this.$el.removeClass('oe_hidden');
              }
  
 -            if(this.pos_widget.action_bar.get_button_count() > 0){
 -                this.show_action_bar();
 -            }else{
 -                this.hide_action_bar();
 -            }
 -            
              var self = this;
  
              this.pos_widget.set_numpad_visible(this.show_numpad);
              if(this.pos.barcode_reader){
                  this.pos.barcode_reader.reset_action_callbacks();
              }
 -            this.pos_widget.action_bar.destroy_buttons();
          },
  
          // this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the
      });
  
  
 +    module.FullscreenPopup = module.PopUpWidget.extend({
 +        template:'FullscreenPopupWidget',
 +        show: function(){
 +            var self = this;
 +            this._super();
 +            this.renderElement();
 +            this.$('.button.fullscreen').off('click').click(function(){
 +                window.document.body.webkitRequestFullscreen();
 +                self.pos_widget.screen_selector.close_popup();
 +            });
 +            this.$('.button.cancel').off('click').click(function(){
 +                self.pos_widget.screen_selector.close_popup();
 +            });
 +        },
 +        ismobile: function(){
 +            return typeof window.orientation !== 'undefined'; 
 +        }
 +    });
 +
 +
      module.ChooseReceiptPopupWidget = module.PopUpWidget.extend({
          template:'ChooseReceiptPopupWidget',
          show: function(){
  
              $('body').append('<audio src="/point_of_sale/static/src/sounds/error.wav" autoplay="true"></audio>');
  
-             if( text && (text.message || text.comment) ){
-                 this.$('.message').text(text.message);
-                 this.$('.comment').text(text.comment);
+             if( text ) {
+                 if ( text.message || text.comment ) {
+                     this.$('.message').text(text.message);
+                     this.$('.comment').text(text.comment);
+                 } else {
+                     this.$('.message').text(_t('Error'));
+                     this.$('.comment').html(text);
+                 }
              }
  
              this.pos.barcode_reader.save_callbacks();
          },
      });
  
 -    module.ErrorNoClientPopupWidget = module.ErrorPopupWidget.extend({
 -        template: 'ErrorNoClientPopupWidget',
 -    });
 -
      module.ErrorInvoiceTransferPopupWidget = module.ErrorPopupWidget.extend({
          template: 'ErrorInvoiceTransferPopupWidget',
      });
          },
          get_product_name: function(){
              var product = this.get_product();
-             return (product ? product.name : undefined) || 'Unnamed Product';
+             return (product ? product.display_name : undefined) || 'Unnamed Product';
          },
          get_product_price: function(){
              var product = this.get_product();
  
          init: function(parent, options){
              this._super(parent, options);
 +            this.partner_cache = new module.DomCache();
          },
  
          show_leftpane: false,
                  self.pos_widget.screen_selector.back();
              });
  
-             var partners = this.pos.db.get_partners_sorted();
+             var partners = this.pos.db.get_partners_sorted(1000);
              this.render_list(partners);
+             
+             this.pos.load_new_partners().then(function(){ 
+                 // will only get called if new partners were reloaded.
+                 self.render_list(self.pos.db.get_partners_sorted(1000));
+             });
  
              if( this.old_client ){
                  this.display_client_details('show',this.old_client,0);
              }
          },
          clear_search: function(){
-             var customers = this.pos.db.get_partners_sorted();
+             var customers = this.pos.db.get_partners_sorted(1000);
              this.render_list(customers);
              this.$('.searchbox input')[0].value = '';
              this.$('.searchbox input').focus();
          },
          render_list: function(partners){
              var contents = this.$el[0].querySelector('.client-list-contents');
-             contents.innerHtml = "";
-             for(var i = 0, len = partners.length; i < len; i++){
+             contents.innerHTML = "";
+             for(var i = 0, len = Math.min(partners.length,1000); i < len; i++){
                  var partner    = partners[i];
 -                var clientline_html = QWeb.render('ClientLine',{widget: this, partner:partners[i]});
 -                var clientline = document.createElement('tbody');
 -                clientline.innerHTML = clientline_html;
 -                clientline = clientline.childNodes[1];
 -
 +                var clientline = this.partner_cache.get_node(partner.id);
 +                if(!clientline){
-                     var clientline_html = QWeb.render('ClientLine',{partner:partners[i]});
++                    var clientline_html = QWeb.render('ClientLine',{widget: this, partner:partners[i]});
 +                    var clientline = document.createElement('tbody');
 +                    clientline.innerHTML = clientline_html;
 +                    clientline = clientline.childNodes[1];
 +                    this.partner_cache.cache_node(partner.id,clientline);
 +                }
                  if( partners === this.new_client ){
                      clientline.classList.add('highlight');
                  }else{
                      clientline.classList.remove('highlight');
                  }
 -
                  contents.appendChild(clientline);
              }
          },
  
      module.ReceiptScreenWidget = module.ScreenWidget.extend({
          template: 'ReceiptScreenWidget',
 -
 -        show_numpad:     true,
 -        show_leftpane:   true,
 +        show_numpad:     false,
 +        show_leftpane:   false,
  
          show: function(){
              this._super();
              var self = this;
  
              this.refresh();
              this.print();
  
 -            //
              // The problem is that in chrome the print() is asynchronous and doesn't
              // execute until all rpc are finished. So it conflicts with the rpc used
              // to send the orders to the backend, and the user is able to go to the next 
              // 2 seconds is the same as the default timeout for sending orders and so the dialog
              // should have appeared before the timeout... so yeah that's not ultra reliable. 
  
 -            finish_button.set_disabled(true);   
 +            this.lock_screen(true);  
              setTimeout(function(){
 -                finish_button.set_disabled(false);
 +                self.lock_screen(false);  
              }, 2000);
          },
 +        lock_screen: function(locked) {
 +            this._locked = locked;
 +            if (locked) {
 +                this.$('.next').removeClass('highlight');
 +            } else {
 +                this.$('.next').addClass('highlight');
 +            }
 +        },
          print: function() {
              window.print();
          },
 -        finishOrder: function() {
 -            this.pos.get('selectedOrder').destroy();
 +        finish_order: function() {
 +            if (!this._locked) {
 +                this.pos.get_order().finalize();
 +            }
 +        },
 +        renderElement: function() {
 +            var self = this;
 +            this._super();
 +            this.$('.next').click(function(){
 +                self.finish_order();
 +            });
 +            this.$('.button.print').click(function(){
 +                self.print();
 +            });
          },
          refresh: function() {
 -            var order = this.pos.get('selectedOrder');
 -            $('.pos-receipt-container', this.$el).html(QWeb.render('PosTicket',{
 +            var order = this.pos.get_order();
 +            this.$('.pos-receipt-container').html(QWeb.render('PosTicket',{
                      widget:this,
                      order: order,
                      orderlines: order.get('orderLines').models,
                      paymentlines: order.get('paymentLines').models,
                  }));
          },
 -        close: function(){
 -            this._super();
 -        }
      });
  
 -
      module.PaymentScreenWidget = module.ScreenWidget.extend({
 -        template: 'PaymentScreenWidget',
 -        back_screen: 'products',
 -        next_screen: 'receipt',
 +        template:      'PaymentScreenWidget',
 +        back_screen:   'product',
 +        next_screen:   'receipt',
 +        show_leftpane: false,
 +        show_numpad:   false,
          init: function(parent, options) {
              var self = this;
 -            this._super(parent,options);
 +            this._super(parent, options);
  
              this.pos.bind('change:selectedOrder',function(){
 -                    this.bind_events();
                      this.renderElement();
 +                    this.watch_order_changes();
                  },this);
 +            this.watch_order_changes();
  
 -            this.bind_events();
 -
 -            this.line_delete_handler = function(event){
 -                var node = this;
 -                while(node && !node.classList.contains('paymentline')){
 -                    node = node.parentNode;
 -                }
 -                if(node){
 -                    self.pos.get('selectedOrder').removePaymentline(node.line)   
 +            this.inputbuffer = "";
 +            this.firstinput  = true;
 +            this.keyboard_handler = function(event){
 +                var key = '';
 +                if ( event.keyCode === 13 ) {         // Enter
 +                    self.validate_order();
 +                } else if ( event.keyCode === 190 ) { // Dot
 +                    key = '.';
 +                } else if ( event.keyCode === 46 ) {  // Delete
 +                    key = 'CLEAR';
 +                } else if ( event.keyCode === 8 ) {   // Backspace 
 +                    key = 'BACKSPACE';
 +                    event.preventDefault(); // Prevents history back nav
 +                } else if ( event.keyCode >= 48 && event.keyCode <= 57 ){       // Numbers
 +                    key = '' + (event.keyCode - 48);
 +                } else if ( event.keyCode >= 96 && event.keyCode <= 105 ){      // Numpad Numbers
 +                    key = '' + (event.keyCode - 96);
 +                } else if ( event.keyCode === 189 || event.keyCode === 109 ) {  // Minus
 +                    key = '-';
 +                } else if ( event.keyCode === 107 ) { // Plus
 +                    key = '+';
                  }
 -                event.stopPropagation();
 -            };
  
 -            this.line_change_handler = function(event){
 -                var node = this;
 -                while(node && !node.classList.contains('paymentline')){
 -                    node = node.parentNode;
 -                }
 -                if(node){
 -                    node.line.set_amount(this.value);
 -                }
 -            };
 +                self.payment_input(key);
  
 -            this.line_click_handler = function(event){
 -                var node = this;
 -                while(node && !node.classList.contains('paymentline')){
 -                    node = node.parentNode;
 -                }
 -                if(node){
 -                    self.pos.get('selectedOrder').selectPaymentline(node.line);
 -                }
              };
 -
 -            this.hotkey_handler = function(event){
 -                if(event.which === 13){
 -                    self.validate_order();
 -                }else if(event.which === 27){
 -                    self.back();
 -                }
 -            };
 -
          },
 -        show: function(){
 -            this._super();
 -            var self = this;
 -            
 -            this.enable_numpad();
 -            this.focus_selected_line();
 -            
 -            document.body.addEventListener('keyup', this.hotkey_handler);
 -
 -            this.add_action_button({
 -                    label: _t('Back'),
 -                    icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
 -                    click: function(){  
 -                        self.back();
 -                    },
 -                });
 -
 -            this.add_action_button({
 -                    label: _t('Validate'),
 -                    name: 'validation',
 -                    icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
 -                    click: function(){
 -                        self.validate_order();
 -                    },
 -                });
 -           
 -            if( this.pos.config.iface_invoicing ){
 -                this.add_action_button({
 -                        label: 'Invoice',
 -                        name: 'invoice',
 -                        icon: '/point_of_sale/static/src/img/icons/png48/invoice.png',
 -                        click: function(){
 -                            self.validate_order({invoice: true});
 -                        },
 -                    });
 +        // resets the current input buffer
 +        reset_input: function(){
 +            var line = this.pos.get_order().selected_paymentline;
 +            this.firstinput  = true;
 +            if (line) {
 +                this.inputbuffer = this.format_currency_no_symbol(line.get_amount());
 +            } else {
 +                this.inputbuffer = "";
              }
 -
 -            if( this.pos.config.iface_cashdrawer ){
 -                this.add_action_button({
 -                        label: _t('Cash'),
 -                        name: 'cashbox',
 -                        icon: '/point_of_sale/static/src/img/open-cashbox.png',
 -                        click: function(){
 -                            self.pos.proxy.open_cashbox();
 -                        },
 -                    });
 +        },
 +        // handle both keyboard and numpad input. Accepts
 +        // a string that represents the key pressed.
 +        payment_input: function(input) {
 +            var oldbuf = this.inputbuffer.slice(0);
 +
 +            if (input === '.') {
 +                if (this.firstinput) {
 +                    this.inputbuffer = "0.";
 +                }else if (!this.inputbuffer.length || this.inputbuffer === '-') {
 +                    this.inputbuffer += "0.";
 +                } else if (this.inputbuffer.indexOf('.') < 0){
 +                    this.inputbuffer = this.inputbuffer + '.';
 +                }
 +            } else if (input === 'CLEAR') {
 +                this.inputbuffer = ""; 
 +            } else if (input === 'BACKSPACE') { 
 +                this.inputbuffer = this.inputbuffer.substring(0,this.inputbuffer.length - 1);
 +            } else if (input === '+') {
 +                if ( this.inputbuffer[0] === '-' ) {
 +                    this.inputbuffer = this.inputbuffer.substring(1,this.inputbuffer.length);
 +                }
 +            } else if (input === '-') {
 +                if ( this.inputbuffer[0] === '-' ) {
 +                    this.inputbuffer = this.inputbuffer.substring(1,this.inputbuffer.length);
 +                } else {
 +                    this.inputbuffer = '-' + this.inputbuffer;
 +                }
 +            } else if (input[0] === '+' && !isNaN(parseFloat(input))) {
 +                this.inputbuffer = '' + ((parseFloat(this.inputbuffer) || 0) + parseFloat(input));
 +            } else if (!isNaN(parseInt(input))) {
 +                if (this.firstinput) {
 +                    this.inputbuffer = '' + input;
 +                } else {
 +                    this.inputbuffer += input;
 +                }
              }
  
 -            this.update_payment_summary();
 +            this.firstinput = false;
  
 -        },
 -        close: function(){
 -            this._super();
 -            this.disable_numpad();
 -            document.body.removeEventListener('keyup',this.hotkey_handler);
 -        },
 -        remove_empty_lines: function(){
 -            var order = this.pos.get('selectedOrder');
 -            var lines = order.get('paymentLines').models.slice(0);
 -            for(var i = 0; i < lines.length; i++){ 
 -                var line = lines[i];
 -                if(line.get_amount() === 0){
 -                    order.removePaymentline(line);
 +            if (this.inputbuffer !== oldbuf) {
 +                var order = this.pos.get_order();
 +                if (order.selected_paymentline) {
 +                    order.selected_paymentline.set_amount(parseFloat(this.inputbuffer));
 +                    this.order_changes();
 +                    this.render_paymentlines();
 +                    this.$('.paymentline.selected .edit').text(this.inputbuffer);
                  }
              }
          },
 -        back: function() {
 -            this.remove_empty_lines();
 -            this.pos_widget.screen_selector.set_current_screen(this.back_screen);
 +        click_numpad: function(button) {
 +            this.payment_input(button.data('action'));
          },
 -        bind_events: function() {
 -            if(this.old_order){
 -                this.old_order.unbind(null,null,this);
 -            }
 -            var order = this.pos.get('selectedOrder');
 -                order.bind('change:selected_paymentline',this.focus_selected_line,this);
 -
 -            this.old_order = order;
 -
 -            if(this.old_paymentlines){
 -                this.old_paymentlines.unbind(null,null,this);
 -            }
 -            var paymentlines = order.get('paymentLines');
 -                paymentlines.bind('add', this.add_paymentline, this);
 -                paymentlines.bind('change:selected', this.rerender_paymentline, this);
 -                paymentlines.bind('change:amount', function(line){
 -                        if(!line.selected && line.node){
 -                            line.node.value = line.amount.toFixed(2);
 -                        }
 -                        this.update_payment_summary();
 -                    },this);
 -                paymentlines.bind('remove', this.remove_paymentline, this);
 -                paymentlines.bind('all', this.update_payment_summary, this);
 -
 -            this.old_paymentlines = paymentlines;
 -
 -            if(this.old_orderlines){
 -                this.old_orderlines.unbind(null,null,this);
 +        render_numpad: function() {
 +            var self = this;
 +            var numpad = $(QWeb.render('PaymentScreen-Numpad', { widget:this }));
 +            numpad.on('click','button',function(){
 +                self.click_numpad($(this));
 +            });
 +            return numpad;
 +        },
 +        click_delete_paymentline: function(cid){
 +            var lines = this.pos.get_order().get('paymentLines').models;
 +            for ( var i = 0; i < lines.length; i++ ) {
 +                if (lines[i].cid === cid) {
 +                    this.pos.get_order().removePaymentline(lines[i]);
 +                    this.reset_input();
 +                    this.render_paymentlines();
 +                    return;
 +                }
              }
 -            var orderlines = order.get('orderLines');
 -                orderlines.bind('all', this.update_payment_summary, this);
 -
 -            this.old_orderlines = orderlines;
          },
 -        focus_selected_line: function(){
 -            var line = this.pos.get('selectedOrder').selected_paymentline;
 -            if(line){
 -                var input = line.node.querySelector('input');
 -                if(!input){
 +        click_paymentline: function(cid){
 +            var lines = this.pos.get_order().get('paymentLines').models;
 +            for ( var i = 0; i < lines.length; i++ ) {
 +                if (lines[i].cid === cid) {
 +                    this.pos.get_order().selectPaymentline(lines[i]);
 +                    this.reset_input();
 +                    this.render_paymentlines();
                      return;
                  }
 -                var value = input.value;
 -                input.focus();
 +            }
 +        },
 +        render_paymentlines: function() {
 +            var self  = this;
 +            var order = this.pos.get_order();
 +            var lines = order.get('paymentLines').models;
  
 -                if(this.numpad_state){
 -                    this.numpad_state.reset();
 -                }
 +            this.$('.paymentlines-container').empty();
 +            var lines = $(QWeb.render('PaymentScreen-Paymentlines', { 
 +                widget: this, 
 +                order: order,
 +                paymentlines: lines,
 +            }));
  
 -                if(Number(value) === 0){
 -                    input.value = '';
 -                }else{
 -                    input.value = value;
 -                    input.select();
 +            lines.on('click','.delete-button',function(){
 +                self.click_delete_paymentline($(this).data('cid'));
 +            });
 +
 +            lines.on('click','.paymentline',function(){
 +                self.click_paymentline($(this).data('cid'));
 +            });
 +                
 +            lines.appendTo(this.$('.paymentlines-container'));
 +        },
 +        click_paymentmethods: function(id) {
 +            var cashregister = null;
 +            for ( var i = 0; i < this.pos.cashregisters.length; i++ ) {
 +                if ( this.pos.cashregisters[i].journal_id[0] === id ){
 +                    cashregister = this.pos.cashregisters[i];
 +                    break;
                  }
              }
 +            this.pos.get_order().addPaymentline( cashregister );
 +            this.reset_input();
 +            this.render_paymentlines();
          },
 -        add_paymentline: function(line) {
 -            var list_container = this.el.querySelector('.payment-lines');
 -                list_container.appendChild(this.render_paymentline(line));
 -            
 -            if(this.numpad_state){
 -                this.numpad_state.reset();
 +        render_paymentmethods: function() {
 +            var self = this;
 +            var methods = $(QWeb.render('PaymentScreen-Paymentmethods', { widget:this }));
 +                methods.on('click','.paymentmethod',function(){
 +                    self.click_paymentmethods($(this).data('id'));
 +                });
 +            return methods;
 +        },
 +        click_invoice: function(){
 +            var order = this.pos.get_order();
 +            order.set_to_invoice(!order.is_to_invoice());
 +            if (order.is_to_invoice()) {
 +                this.$('.js_invoice').addClass('highlight');
 +            } else {
 +                this.$('.js_invoice').removeClass('highlight');
              }
          },
 -        render_paymentline: function(line){
 -            var el_html  = openerp.qweb.render('Paymentline',{widget: this, line: line});
 -                el_html  = _.str.trim(el_html);
 +        renderElement: function() {
 +            var self = this;
 +            this._super();
  
 -            var el_node  = document.createElement('tbody');
 -                el_node.innerHTML = el_html;
 -                el_node = el_node.childNodes[0];
 -                el_node.line = line;
 -                el_node.querySelector('.paymentline-delete')
 -                    .addEventListener('click', this.line_delete_handler);
 -                el_node.addEventListener('click', this.line_click_handler);
 -                el_node.querySelector('input')
 -                    .addEventListener('keyup', this.line_change_handler);
 +            var numpad = this.render_numpad();
 +            numpad.appendTo(this.$('.payment-numpad'));
  
 -            line.node = el_node;
 +            var methods = this.render_paymentmethods();
 +            methods.appendTo(this.$('.paymentmethods-container'));
 +
 +            this.render_paymentlines();
 +
 +            this.$('.back').click(function(){
 +                self.pos_widget.screen_selector.back();
 +            });
 +
 +            this.$('.next').click(function(){
 +                self.validate_order();
 +            });
 +
 +            this.$('.js_invoice').click(function(){
 +                self.click_invoice();
 +            });
  
 -            return el_node;
 -        },
 -        rerender_paymentline: function(line){
 -            var old_node = line.node;
 -            var new_node = this.render_paymentline(line);
 -            
 -            old_node.parentNode.replaceChild(new_node,old_node);
          },
 -        remove_paymentline: function(line){
 -            line.node.parentNode.removeChild(line.node);
 -            line.node = undefined;
 +        show: function(){
 +            this.pos.get_order().clean_empty_paymentlines();
 +            this.reset_input();
 +            this.render_paymentlines();
 +            this.order_changes();
 +            window.document.body.addEventListener('keydown',this.keyboard_handler);
 +            this._super();
          },
 -        renderElement: function(){
 +        hide: function(){
 +            window.document.body.removeEventListener('keydown',this.keyboard_handler);
              this._super();
 -
 -            var paymentlines   = this.pos.get('selectedOrder').get('paymentLines').models;
 -            var list_container = this.el.querySelector('.payment-lines');
 -
 -            for(var i = 0; i < paymentlines.length; i++){
 -                list_container.appendChild(this.render_paymentline(paymentlines[i]));
 -            }
 -            
 -            this.update_payment_summary();
 -        },
 -        update_payment_summary: function() {
 -            var currentOrder = this.pos.get('selectedOrder');
 -            var paidTotal = currentOrder.getPaidTotal();
 -            var dueTotal = currentOrder.getTotalTaxIncluded();
 -            var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0;
 -            var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0;
 -
 -            this.$('.payment-due-total').html(this.format_currency(dueTotal));
 -            this.$('.payment-paid-total').html(this.format_currency(paidTotal));
 -            this.$('.payment-remaining').html(this.format_currency(remaining));
 -            this.$('.payment-change').html(this.format_currency(change));
 -            if(currentOrder.selected_orderline === undefined){
 -                remaining = 1;  // What is this ? 
 -            }
 -                
 -            if(this.pos_widget.action_bar){
 -                this.pos_widget.action_bar.set_button_disabled('validation', !this.is_paid());
 -                this.pos_widget.action_bar.set_button_disabled('invoice', !this.is_paid());
 +        },
 +        // sets up listeners to watch for order changes
 +        watch_order_changes: function() {
 +            var self = this;
 +            var order = this.pos.get_order();
 +            if(this.old_order){
 +                this.old_order.unbind(null,null,this);
              }
 +            order.bind('all',function(){
 +                self.order_changes();
 +            });
 +            this.old_order = order;
          },
 -        is_paid: function(){
 -            var currentOrder = this.pos.get('selectedOrder');
 -            return (currentOrder.getTotalTaxIncluded() < 0.000001 
 -                   || currentOrder.getPaidTotal() + 0.000001 >= currentOrder.getTotalTaxIncluded());
 -
 +        // called when the order is changed, used to show if
 +        // the order is paid or not
 +        order_changes: function(){
 +            var self = this;
 +            var order = this.pos.get_order();
 +            if (order.isPaid()) {
 +                self.$('.next').addClass('highlight');
 +            }else{
 +                self.$('.next').removeClass('highlight');
 +            }
          },
 -        validate_order: function(options) {
 +        // Check if the order is paid, then sends it to the backend,
 +        // and complete the sale process
 +        validate_order: function() {
              var self = this;
 -            options = options || {};
  
 -            var currentOrder = this.pos.get('selectedOrder');
 +            var order = this.pos.get_order();
  
 -            if(!this.is_paid()){
 +            if (!order.isPaid() || this.invoicing) {
                  return;
              }
  
 -            if(    this.pos.config.iface_cashdrawer 
 -                && this.pos.get('selectedOrder').get('paymentLines').find( function(pl){ 
 -                           return pl.cashregister.journal.type === 'cash'; 
 -                   })){
 +            if (order.isPaidWithCash() && this.pos.config.iface_cashdrawer) { 
                      this.pos.proxy.open_cashbox();
              }
  
 -            if(options.invoice){
 -                // deactivate the validation button while we try to send the order
 -                this.pos_widget.action_bar.set_button_disabled('validation',true);
 -                this.pos_widget.action_bar.set_button_disabled('invoice',true);
 -
 -                var invoiced = this.pos.push_and_invoice_order(currentOrder);
 +            if (order.is_to_invoice()) {
 +                var invoiced = this.pos.push_and_invoice_order(order);
 +                this.invoicing = true;
  
                  invoiced.fail(function(error){
 -                    if(error === 'error-no-client'){
 -                        self.pos_widget.screen_selector.show_popup('error-no-client');
 -                    }else{
 +                    self.invoicing = false;
 +                    if (error === 'error-no-client') {
 +                        self.pos_widget.screen_selector.show_popup('confirm',{
 +                            message: _t('Please select the Customer'),
 +                            comment: _t('You need to select the customer before you can invoice an order.'),
 +                            confirm: function(){
 +                                self.pos_widget.screen_selector.set_current_screen('clientlist');
 +                            },
 +                        });
 +                    } else {
                          self.pos_widget.screen_selector.show_popup('error-invoice-transfer');
                      }
                  });
  
                  invoiced.done(function(){
 -                    self.pos_widget.action_bar.set_button_disabled('validation',false);
 -                    self.pos_widget.action_bar.set_button_disabled('invoice',false);
 -                    self.pos.get('selectedOrder').destroy();
 +                    self.invoicing = false;
 +                    order.finalize();
                  });
 -
 -            }else{
 -                this.pos.push_order(currentOrder) 
 -                if(this.pos.config.iface_print_via_proxy){
 +            } else {
 +                this.pos.push_order(order) 
 +                if (this.pos.config.iface_print_via_proxy) {
                      var receipt = currentOrder.export_for_printing();
                      this.pos.proxy.print_receipt(QWeb.render('XmlReceipt',{
                          receipt: receipt, widget: self,
                      }));
 -                    this.pos.get('selectedOrder').destroy();    //finish order and go back to scan screen
 -                }else{
 +                    order.finalize();    //finish order and go back to scan screen
 +                } else {
                      this.pos_widget.screen_selector.set_current_screen(this.next_screen);
                  }
              }
 -
 -            // hide onscreen (iOS) keyboard 
 -            setTimeout(function(){
 -                document.activeElement.blur();
 -                $("input").blur();
 -            },250);
 -        },
 -        enable_numpad: function(){
 -            this.disable_numpad();  //ensure we don't register the callbacks twice
 -            this.numpad_state = this.pos_widget.numpad.state;
 -            if(this.numpad_state){
 -                this.numpad_state.reset();
 -                this.numpad_state.changeMode('payment');
 -                this.numpad_state.bind('set_value',   this.set_value, this);
 -                this.numpad_state.bind('change:mode', this.set_mode_back_to_payment, this);
 -            }
 -                    
 -        },
 -        disable_numpad: function(){
 -            if(this.numpad_state){
 -                this.numpad_state.unbind('set_value',  this.set_value);
 -                this.numpad_state.unbind('change:mode',this.set_mode_back_to_payment);
 -            }
 -        },
 -      set_mode_back_to_payment: function() {
 -              this.numpad_state.set({mode: 'payment'});
 -      },
 -        set_value: function(val) {
 -            var selected_line =this.pos.get('selectedOrder').selected_paymentline;
 -            if(selected_line){
 -                selected_line.set_amount(val);
 -                selected_line.node.querySelector('input').value = selected_line.amount.toFixed(2);
 -            }
          },
      });
 +
  }
@@@ -1,7 -1,5 +1,7 @@@
  function openerp_pos_basewidget(instance, module){ //module is instance.point_of_sale
  
 +    var round_pr = instance.web.round_precision
 +
      // This is a base class for all Widgets in the POS. It exposes relevant data to the 
      // templates : 
      // - widget.currency : { symbol: '$' | '€' | ..., position: 'before' | 'after }
  
              var decimals = Math.max(0,Math.ceil(Math.log(1.0 / this.currency.rounding) / Math.log(10)));
  
 +            this.format_currency_no_symbol = function(amount){
 +                amount = round_pr(amount,this.currency.rounding);
 +                amount = amount.toFixed(decimals);
 +                return amount;
 +            };
 +
              this.format_currency = function(amount){
                  if(typeof amount === 'number'){
                      amount = Math.round(amount*100)/100;
@@@ -45,7 -37,7 +45,7 @@@
                  }else{
                      return this.currency.symbol + ' ' + amount;
                  }
 -            }
 +            };
  
          },
          show: function(){
          hide: function(){
              this.$el.addClass('oe_hidden');
          },
+         format_pr: function(value,precision){
+             var decimals = precision > 0 ? Math.max(0,Math.ceil(Math.log(1.0/precision) / Math.log(10))) : 0;
+             return value.toFixed(decimals);
+         },
      });
  
  }
@@@ -27,7 -27,7 +27,7 @@@ function openerp_pos_widgets(instance, 
                              oldest_key  = key;
                          }
                      }
-                     if(oldestKey){
+                     if(oldest_key){
                          delete this.cache[oldest_key];
                          delete this.access_time[oldest_key];
                      }
          },
      });
  
 -    // The paypad allows to select the payment method (cashregisters) 
 -    // used to pay the order.
 -    module.PaypadWidget = module.PosBaseWidget.extend({
 -        template: 'PaypadWidget',
 +    // The action pads contains the payment button and the customer selection button.
 +    module.ActionpadWidget = module.PosBaseWidget.extend({
 +        template: 'ActionpadWidget',
          renderElement: function() {
              var self = this;
              this._super();
 -
 -            _.each(this.pos.cashregisters,function(cashregister) {
 -                var button = new module.PaypadButtonWidget(self,{
 -                    pos: self.pos,
 -                    pos_widget : self.pos_widget,
 -                    cashregister: cashregister,
 -                });
 -                button.appendTo(self.$el);
 +            this.$('.pay').click(function(){
 +                self.pos.pos_widget.screen_selector.set_current_screen('payment');
              });
 -        }
 -    });
 -
 -    module.PaypadButtonWidget = module.PosBaseWidget.extend({
 -        template: 'PaypadButtonWidget',
 -        init: function(parent, options){
 -            this._super(parent, options);
 -            this.cashregister = options.cashregister;
 -        },
 -        renderElement: function() {
 -            var self = this;
 -            this._super();
 -
 -            this.$el.click(function(){
 -                if (self.pos.get('selectedOrder').get('screen') === 'receipt'){  //TODO Why ?
 -                    console.warn('TODO should not get there...?');
 -                    return;
 -                }
 -                self.pos.get('selectedOrder').addPaymentline(self.cashregister);
 -                self.pos_widget.screen_selector.set_current_screen('payment');
 +            this.$('.set-customer').click(function(){
 +                self.pos.pos_widget.screen_selector.set_current_screen('clientlist');
              });
 -        },
 +        }
      });
  
      module.OrderWidget = module.PosBaseWidget.extend({
                      var ss = self.pos.pos_widget.screen_selector;
                      if(ss.get_current_screen() === 'clientlist'){
                          ss.back();
-                     }else{
+                     }else if (ss.get_current_screen() !== 'receipt'){
                          ss.set_current_screen('clientlist');
                      }
                  }else{
          },
      });
  
 -    module.ActionButtonWidget = instance.web.Widget.extend({
 -        template:'ActionButtonWidget',
 -        icon_template:'ActionButtonWidgetWithIcon',
 -        init: function(parent, options){
 -            this._super(parent, options);
 -            this.label = options.label || 'button';
 -            this.rightalign = options.rightalign || false;
 -            this.click_action = options.click;
 -            this.disabled = options.disabled || false;
 -            if(options.icon){
 -                this.icon = options.icon;
 -                this.template = this.icon_template;
 -            }
 -        },
 -        set_disabled: function(disabled){
 -            if(this.disabled != disabled){
 -                this.disabled = !!disabled;
 -                this.renderElement();
 -            }
 -        },
 -        renderElement: function(){
 -            this._super();
 -            if(this.click_action && !this.disabled){
 -                this.$el.click(_.bind(this.click_action, this));
 -            }
 -        },
 -    });
 -
 -    module.ActionBarWidget = instance.web.Widget.extend({
 -        template:'ActionBarWidget',
 -        init: function(parent, options){
 -            this._super(parent,options);
 -            this.button_list = [];
 -            this.buttons = {};
 -            this.visibility = {};
 -        },
 -        set_element_visible: function(element, visible, action){
 -            if(visible != this.visibility[element]){
 -                this.visibility[element] = !!visible;
 -                if(visible){
 -                    this.$('.'+element).removeClass('oe_hidden');
 -                }else{
 -                    this.$('.'+element).addClass('oe_hidden');
 -                }
 -            }
 -            if(visible && action){
 -                this.action[element] = action;
 -                this.$('.'+element).off('click').click(action);
 -            }
 -        },
 -        set_button_disabled: function(name, disabled){
 -            var b = this.buttons[name];
 -            if(b){
 -                b.set_disabled(disabled);
 -            }
 -        },
 -        destroy_buttons:function(){
 -            for(var i = 0; i < this.button_list.length; i++){
 -                this.button_list[i].destroy();
 -            }
 -            this.button_list = [];
 -            this.buttons = {};
 -            return this;
 -        },
 -        get_button_count: function(){
 -            return this.button_list.length;
 -        },
 -        add_new_button: function(button_options){
 -            var button = new module.ActionButtonWidget(this,button_options);
 -            this.button_list.push(button);
 -            if(button_options.name){
 -                this.buttons[button_options.name] = button;
 -            }
 -            button.appendTo(this.$('.pos-actionbar-button-list'));
 -            return button;
 -        },
 -        show:function(){
 -            this.$el.removeClass('oe_hidden');
 -        },
 -        hide:function(){
 -            this.$el.addClass('oe_hidden');
 -        },
 -    });
 -
      module.ProductCategoriesWidget = module.PosBaseWidget.extend({
          template: 'ProductCategoriesWidget',
          init: function(parent, options){
              }
  
              var list_container = el_node.querySelector('.category-list');
-             for(var i = 0, len = this.subcategories.length; i < len; i++){
-                 list_container.appendChild(this.render_category(this.subcategories[i],hasimages));
-             };
+             if (list_container) { 
+                 if (!hasimages) {
+                     list_container.classList.add('simple');
+                 } else {
+                     list_container.classList.remove('simple');
+                 }
+                 for(var i = 0, len = this.subcategories.length; i < len; i++){
+                     list_container.appendChild(this.render_category(this.subcategories[i],hasimages));
+                 };
+             }
  
              var buttons = el_node.querySelectorAll('.js-category-switch');
              for(var i = 0; i < buttons.length; i++){
      // - a header, containing the list of orders
      // - a leftpane, containing the list of bought products (orderlines) 
      // - a rightpane, containing the screens (see pos_screens.js)
 -    // - an actionbar on the bottom, containing various action buttons
      // - popups
      // - an onscreen keyboard
      // a screen_selector which controls the switching between screens and the showing/closing of popups
                      self.screen_selector.show_popup('error', 'Sorry, we could not create a user session');
                  }else if(!self.pos.config){
                      self.screen_selector.show_popup('error', 'Sorry, we could not find any PoS Configuration for this session');
 +                }else if(self.pos.config.iface_fullscreen && document.body.webkitRequestFullscreen && (
 +                    window.screen.availWidth  > window.innerWidth ||
 +                    window.screen.availHeight > window.innerHeight    )){
 +                    self.screen_selector.show_popup('fullscreen');
                  }
              
                  self.$('.loader').animate({opacity:0},1500,'swing',function(){self.$('.loader').addClass('oe_hidden');});
              this.choose_receipt_popup = new module.ChooseReceiptPopupWidget(this, {});
              this.choose_receipt_popup.appendTo(this.$el);
  
 -            this.error_no_client_popup = new module.ErrorNoClientPopupWidget(this, {});
 -            this.error_no_client_popup.appendTo(this.$el);
 -
              this.error_invoice_transfer_popup = new module.ErrorInvoiceTransferPopupWidget(this, {});
              this.error_invoice_transfer_popup.appendTo(this.$el);
  
              this.confirm_popup = new module.ConfirmPopupWidget(this,{});
              this.confirm_popup.appendTo(this.$el);
  
 +            this.fullscreen_popup = new module.FullscreenPopup(this,{});
 +            this.fullscreen_popup.appendTo(this.$el);
 +
              this.unsent_orders_popup = new module.UnsentOrdersPopupWidget(this,{});
              this.unsent_orders_popup.appendTo(this.$el);
  
  
              this.close_button = new module.HeaderButtonWidget(this,{
                  label: _t('Close'),
-                 action: function(){ self.close(); },
+                 action: function(){ 
+                     var self = this;
+                     if (!this.confirmed) {
+                         this.$el.addClass('confirm');
+                         this.$el.text(_t('Confirm'));
+                         this.confirmed = setTimeout(function(){
+                             self.$el.removeClass('confirm');
+                             self.$el.text(_t('Close'));
+                             self.confirmed = false;
+                         },2000);
+                     } else {
+                         clearTimeout(this.confirmed);
+                         this.pos_widget.close();
+                     }
+                 },
              });
              this.close_button.appendTo(this.$('.pos-rightheader'));
  
              this.username   = new module.UsernameWidget(this,{});
              this.username.replace(this.$('.placeholder-UsernameWidget'));
  
 -            this.action_bar = new module.ActionBarWidget(this);
 -            this.action_bar.replace(this.$(".placeholder-RightActionBar"));
 -
 -            this.paypad = new module.PaypadWidget(this, {});
 -            this.paypad.replace(this.$('.placeholder-PaypadWidget'));
 +            this.actionpad = new module.ActionpadWidget(this, {});
 +            this.actionpad.replace(this.$('.placeholder-ActionpadWidget'));
  
              this.numpad = new module.NumpadWidget(this);
              this.numpad.replace(this.$('.placeholder-NumpadWidget'));
                      'error-barcode': this.error_barcode_popup,
                      'error-session': this.error_session_popup,
                      'choose-receipt': this.choose_receipt_popup,
                      'error-invoice-transfer': this.error_invoice_transfer_popup,
                      'error-traceback': this.error_traceback_popup,
                      'confirm': this.confirm_popup,
 +                    'fullscreen': this.fullscreen_popup,
                      'unsent-orders': this.unsent_orders_popup,
                  },
                  default_screen: 'products',
                  this.numpad_visible = visible;
                  if(visible){
                      this.numpad.show();
 -                    this.paypad.show();
 +                    this.actionpad.show();
                  }else{
                      this.numpad.hide();
 -                    this.paypad.hide();
 +                    this.actionpad.hide();
                  }
              }
          },
                              <div class='subwindow-container'>
                                  <div class='subwindow-container-fix pads'>
                                      <div class="control-buttons oe_hidden"></div>
 -                                    <div class="placeholder-PaypadWidget"></div>
 +                                    <div class="placeholder-ActionpadWidget"></div>
                                      <div class="placeholder-NumpadWidget"></div>
                                  </div>
                              </div>
                          </div>
  
 -                        <div class='subwindow collapsed'>
 -                            <div class='subwindow-container'>
 -                                <div class='subwindow-container-fix'>
 -                                    <div class='placeholder-LeftActionBar'></div>
 -                                </div>
 -                            </div>
 -                        </div>
                      </div>
                  </div>
  
                                  </div>
                              </div>
                          </div>
 -
 -                        <div class='subwindow collapsed'>
 -                            <div class='subwindow-container'>
 -                                <div class='subwindow-container-fix'>
 -                                    <div class='placeholder-RightActionBar'></div>
 -                                </div>
 -                            </div>
 -                        </div>
                      </div>
                  </div>
  
          <div>There are pending operations that could not be saved into the database, are you sure you want to exit?</div>
      </t>
  
 -    <t t-name="PaypadWidget">
 -        <div class="paypad touch-scrollable">
 +    <t t-name="ActionpadWidget">
 +        <div class='actionpad'>
 +            <button class='button set-customer'>
 +                <i class='fa fa-user' /> Set Customer
 +            </button>
 +            <button class="button pay">
 +                <i class='fa fa-chevron-right' /> Payment
 +            </button>
          </div>
      </t>
  
          <tr class='client-line' t-att-data-id='partner.id'>
              <td><t t-esc='partner.name' /></td>
              <td><t t-esc='partner.address' /></td>
+             <td><t t-esc='partner.phone or partner.mobile or ""' /></td>
          </tr>
      </t>
  
                                              <tr>
                                                  <th>Name</th>
                                                  <th>Address</th>
+                                                 <th>Phone</th>
                                              </tr>
                                          </thead>
                                          <tbody class='client-list-contents'>
          </div>
      </t>
  
 -        
 +    <t t-name="PaymentScreen-Paymentlines">
 +        <t t-if="!paymentlines.length">
 +            <div class='paymentlines-empty'>
 +                <div class='total'>
 +                    <t t-esc="widget.format_currency(order.getTotalTaxIncluded())"/>
 +                </div>
 +                <div class='message'>
 +                    Please select a payment method. 
 +                </div>
 +            </div>
 +        </t>
 +
 +        <t t-if="paymentlines.length">
 +            <table class='paymentlines'>
 +                <colgroup>
 +                    <col class='due' />
 +                    <col class='tendered' />
 +                    <col class='change' />
 +                    <col class='method' />
 +                    <col class='controls' />
 +                </colgroup>
 +                <thead>
 +                    <tr class='label'>
 +                        <th>Due</th>
 +                        <th>Tendered</th>
 +                        <th>Change</th>
 +                        <th>Method</th>
 +                        <th></th>
 +                    </tr>
 +                </thead>
 +                <tbody>
 +                    <t t-foreach='paymentlines' t-as='line'>
 +                        <t t-if='line.selected'>
 +                            <tr class='paymentline selected'>
 +                                <td class='col-due'> <t t-esc='widget.format_currency_no_symbol(order.getDueLeft(line))' /> </td>
 +                                <td class='col-tendered edit'> 
 +                                    <t t-esc='widget.inputbuffer' />
 +                                    <!-- <t t-esc='line.get_amount()' /> -->
 +                                </td>
 +                                <t t-if='order.getChange(line)'>
 +                                    <td class='col-change highlight' > 
 +                                        <t t-esc='widget.format_currency_no_symbol(order.getChange(line))' />
 +                                    </td>
 +                                </t>
 +                                <t t-if='!order.getChange(line)'>
 +                                    <td class='col-change' ></td>
 +                                </t>
 +                                    
 +                                <td class='col-name' > <t t-esc='line.name' /> </td>
 +                                <td class='delete-button' t-att-data-cid='line.cid'> <i class='fa fa-times-circle' /> </td>
 +                            </tr>
 +                        </t>
 +                        <t t-if='!line.selected'>
 +                            <tr class='paymentline' t-att-data-cid='line.cid'>
 +                                <td class='col-due'> <t t-esc='widget.format_currency_no_symbol(order.getDueLeft(line))' /> </td>
 +                                <td class='col-tendered'> <t t-esc='widget.format_currency_no_symbol(line.get_amount())' /> </td>
 +                                <td class='col-change'> 
 +                                    <t t-if='order.getChange(line)'>
 +                                        <t t-esc='widget.format_currency_no_symbol(order.getChange(line))' />
 +                                     </t>
 +                                </td>
 +                                <td class='col-name'> <t t-esc='line.name' /> </td>
 +                                <td class='delete-button' t-att-data-cid='line.cid'> <i class='fa fa-times-circle' /> </td>
 +                            </tr>
 +                        </t>
 +                    </t>
 +                </tbody>
 +            </table>
 +        </t>
  
 +    </t>
 +
 +    <t t-name="PaymentScreen-Numpad">
 +        <div class="numpad">
 +            <button class="input-button number-char" data-action='1'>1</button>
 +            <button class="input-button number-char" data-action='2'>2</button>
 +            <button class="input-button number-char" data-action='3'>3</button>
 +            <button class="mode-button" data-action='+10'>+10</button>
 +            <br />
 +            <button class="input-button number-char" data-action='4'>4</button>
 +            <button class="input-button number-char" data-action='5'>5</button>
 +            <button class="input-button number-char" data-action='6'>6</button>
 +            <button class="mode-button" data-action='+20'>+20</button>
 +            <br />
 +            <button class="input-button number-char" data-action='7'>7</button>
 +            <button class="input-button number-char" data-action='8'>8</button>
 +            <button class="input-button number-char" data-action='9'>9</button>
 +            <button class="mode-button" data-action='+50'>+50</button>
 +            <br />
 +            <button class="input-button numpad-char" data-action='CLEAR' >C</button>
 +            <button class="input-button number-char" data-action='0'>0</button>
 +            <button class="input-button number-char" data-action='.'>.</button>
 +            <button class="input-button numpad-backspace" data-action='BACKSPACE' >
 +                <img src="/point_of_sale/static/src/img/backspace.png" width="24" height="21" />
 +            </button>
 +            <br />
 +        </div>
 +    </t>
 +
 +    <t t-name="PaymentScreen-Paymentmethods">
 +        <div class='paymentmethods'>
 +            <t t-foreach="widget.pos.cashregisters" t-as="cashregister">
 +                <div class="paymentmethod" t-att-data-id="cashregister.journal_id[0]">
 +                    <t t-esc="cashregister.journal_id[1]" />
 +                </div>
 +            </t>
 +        </div>
 +    </t>
 +        
      <t t-name="PaymentScreenWidget">
 -        <div class="payment-screen screen touch-scrollable">
 -            <div class="pos-payment-container">
 -                <div class='payment-due-total'></div>
 -                <div class='payment-lines'></div>
 -                <div class='payment-info'>
 -                    <div class="infoline">
 -                        <span class='left-block'>
 -                            Paid:
 -                        </span>
 -                        <span class="right-block payment-paid-total"></span>
 -                    </div>
 -                    <div class="infoline">
 -                        <span class='left-block'>
 -                            Remaining:
 -                        </span>
 -                        <span class="right-block payment-remaining"></span>
 +        <div class='payment-screen screen'>
 +            <div class='screen-content'>
 +                <div class='top-content'>
 +                    <span class='button back'>
 +                        <i class='fa fa-angle-double-left'></i>
 +                        Back
 +                    </span>
 +                    <h1>Payment</h1>
 +                    <span class='button next'>
 +                        Validate
 +                        <i class='fa fa-angle-double-right'></i>
 +                    </span>
 +                </div>
 +                <div class='left-content pc40 touch-scrollable scrollable-y'>
 +
 +                    <div class='paymentmethods-container'>
                      </div>
 -                    <div class="infoline bigger" >
 -                        <span class='left-block'>
 -                            Change:
 -                        </span>
 -                        <span class="right-block payment-change"></span>
 +
 +                </div>
 +                <div class='right-content pc60 touch-scrollable scrollable-y'>
 +
 +                    <section class='paymentlines-container'>
 +                    </section>
 +
 +                    <section class='payment-numpad'>
 +                    </section>
 +
 +                    <div class='payment-buttons'>
 +                        <t t-if='widget.pos.config.iface_invoicing'>
 +                            <div t-attf-class='button js_invoice #{ widget.pos.get_order().is_to_invoice() ? "highlight" : ""} '>
 +                                <i class='fa fa-file-text-o' /> Invoice
 +                            </div>
 +                        </t>
 +                        <t t-if='widget.pos.config.iface_cashdrawer'>
 +                            <div class='button js_invoice'>
 +                                <i class='fa fa-archive' /> Open Cashbox
 +                            </div>
 +                        </t>
                      </div>
 +
 +
                  </div>
              </div>
          </div>
 +
      </t>
  
      <t t-name="ReceiptScreenWidget">
 -        <div class="receipt-screen screen touch-scrollable" >
 -            <div class="pos-step-container">
 -                <div class="pos-receipt-container">
 +        <div class='receipt-screen screen'>
 +            <div class='screen-content'>
 +                <div class='top-content'>
 +                    <h1>Receipt</h1>
 +                    <span class='button next'>
 +                        Next Order
 +                        <i class='fa fa-angle-double-right'></i>
 +                    </span>
 +                </div>
 +                <div class="centered-content">
 +                    <div class="button print">
 +                        <i class='fa fa-print'></i> Print
 +                    </div>
 +                    <div class="pos-receipt-container">
 +                    </div>
                  </div>
              </div>
          </div>
          </div>
      </t>
  
 +    <t t-name="FullscreenPopupWidget">
 +        <div class="modal-dialog">
 +            <div class="popup popup-fullscreen">
 +                <p class="message">Fullscreen Setup</p>
 +
 +                <t t-if='widget.ismobile()'>
 +                    <p class="comment">
 +                        The best way to make the point of sale fullscreen on mobile
 +                        devices is to add the point of sale to your home screen. On 
 +                        iPhone and iPad this is done by tapping <img src='/point_of_sale/static/src/img/ios-share-icon.png' />
 +                        and then <i>Add to Homescreen</i>
 +                    </p>
 +                    <p class='comment'>
 +                        This also works on Android with the Chrome Beta Browser, using the <i>Add to Homescreen</i> option 
 +                        in the browser's menu.
 +                    </p>
 +                    <p class='comment'> 
 +                        If you want to work in fullscreen just this time tap the <i>Go Fullscreen</i> button.
 +                    </p>
 +                </t>
 +
 +                <t t-if='!widget.ismobile()'>
 +                    <p class="comment">
 +                        The best way to make the point of sale fullscreen on desktop
 +                        and laptops is to launch your browser in kiosk mode. Please
 +                        refer to your browser's documentation for the specific 
 +                        instructions.
 +                    </p>
 +                    <p class="comment">
 +                        If you want to work in fullscreen just this time, click the <i> Go Fullscreen</i> button.
 +                    </p>
 +                </t>
 +
 +                <div class="footer">
 +                    <div class="button fullscreen">
 +                        Go Fullscreen
 +                    </div>
 +                    <div class="button cancel">
 +                        Cancel
 +                    </div>
 +                </div>
 +            </div>
 +        </div>
 +    </t>
 +
      <t t-name="ErrorSessionPopupWidget">
          <div class="modal-dialog">
              <div class="popup popup-nosession">
          </div>
      </t>
  
 -    <t t-name="ErrorNoClientPopupWidget">
 +    <t t-name="ErrorInvoiceTransferPopupWidget">
          <div class="modal-dialog">
 -            <div class="popup popup-noclient">
 -                <p class="message">An anonymous order cannot be invoiced</p>
 +            <div class="popup popup-invoice">
 +                <p class="message">The Order could not be sent to the server for invoicing. Invoices cannot be generated
 +                    in offline mode. Please check your internet connection and try again.</p>
                  <div class="footer">
                      <div class="button">
                          Ok
          </div>
      </t>
  
 -    <t t-name="ErrorInvoiceTransferPopupWidget">
 +    <t t-name="ErrorPopupWidget">
          <div class="modal-dialog">
 -            <div class="popup popup-invoice">
 -                <p class="message">The Order could not be sent to the server for invoicing. Invoices cannot be generated
 -                    in offline mode. Please check your internet connection and try again.</p>
 +            <div class="popup popup-error">
 +                <p class="message"><t t-esc=" widget.message || 'Error' " /></p>
 +                <p class="comment"><t t-esc=" widget.comment || '' "/></p>
                  <div class="footer">
                      <div class="button">
                          Ok
          </div>
      </t>
  
 -    <t t-name="ErrorPopupWidget">
 +    <t t-name="ErrorTracebackPopupWidget">
          <div class="modal-dialog">
              <div class="popup popup-error">
                  <p class="message"><t t-esc=" widget.message || 'Error' " /></p>
 -                <p class="comment"><t t-esc=" widget.comment || '' "/></p>
 +                <p class="comment traceback"><t t-esc=" widget.comment || '' "/></p>
                  <div class="footer">
                      <div class="button">
                          Ok
          </div>
      </t>
  
+     <t t-name="ErrorTracebackPopupWidget">
+         <div class="modal-dialog">
+             <div class="popup popup-error">
+                 <p class="message"><t t-esc=" widget.message || 'Error' " /></p>
+                 <p class="comment traceback"><t t-esc=" widget.comment || '' "/></p>
+                 <div class="footer">
+                     <div class="button">
+                         Ok
+                     </div>
+                 </div>
+             </div>
+         </div>
+     </t>
      <t t-name="ErrorBarcodePopupWidget">
          <div class="modal-dialog">
              <div class="popup popup-barcode">
                  </t>
              </div>
              <div class="product-name">
-                 <t t-esc="product.name"/>
+                 <t t-esc="product.display_name"/>
              </div>
          </span>
      </t>
      <t t-name="Orderline">
          <li t-attf-class="orderline #{ line.selected ? 'selected' : '' }">
              <span class="product-name">
-                 <t t-esc="line.get_product().name"/>
+                 <t t-esc="line.get_product().display_name"/>
              </span>
              <span class="price">
                  <t t-esc="widget.format_currency(line.get_display_price())"/>
          </tr>
      </t>
  
 -    <t t-name="PaypadButtonWidget">
 -        <button class="paypad-button" t-att-cash-register-id="widget.cashregister.id">
 -            <t t-esc="widget.cashregister.journal.name"/>
 -        </button>
 -    </t>
 -
      <t t-name="OrderButtonWidget">
          <span class="order-button select-order">
              <t t-if='widget.selected'>
              <br />
              <t t-esc="widget.pos.company.name"/><br />
              Phone: <t t-esc="widget.pos.company.phone || ''"/><br />
-             User: <t t-esc="widget.pos.user.name"/><br />
+             User: <t t-esc="widget.pos.cashier ? widget.pos.cashier.name : widget.pos.user.name"/><br />
              Shop: <t t-esc="widget.pos.shop.name"/><br />
              <br />
              <t t-if="widget.pos.config.receipt_header">
                  </colgroup>
                  <tr t-foreach="orderlines" t-as="orderline">
                      <td>
-                         <t t-esc="orderline.get_product().name"/>
+                         <t t-esc="orderline.get_product().display_name"/>
                           <t t-if="orderline.get_discount() > 0">
                              <div class="pos-disc-font">
                                  With a <t t-esc="orderline.get_discount()"/>% discount
          </div>
      </t>
  
 -    <t t-name="ActionBarWidget">
 -        <div class="pos-actionbar">
 -            <ul class="pos-actionbar-button-list">
 -            </ul>
 -        </div>
 -    </t>
 -
 -    <t t-name="ActionButtonWidget">
 -        <li t-att-class=" 'button '+ (widget.rightalign  ? 'rightalign ' : '') + (widget.disabled ? 'disabled ' : '')">
 -            <div class='label'>
 -                <t t-esc="widget.label" />
 -            </div>
 -        </li>
 -    </t>
 -
 -    <t t-name="ActionButtonWidgetWithIcon">
 -        <li t-att-class=" 'button '+ (widget.rightalign  ? 'rightalign ' : '') + (widget.disabled ? 'disabled ' : '')">
 -            <div class='icon'>
 -                <img t-att-src="widget.icon" />
 -                <div class='iconlabel'><t t-esc="widget.label" /></div>
 -            </div>
 -        </li>
 -    </t>
 -
      <!-- Onscreen Keyboard : 
           http://net.tutsplus.com/tutorials/javascript-ajax/creating-a-keyboard-with-css-and-jquery/ -->
      <t t-name="OnscreenKeyboardFull">
@@@ -7,13 -7,14 +7,14 @@@
          <record id="email_template_edi_sale" model="email.template">
              <field name="name">Sales Order - Send by Email (Portal)</field>
              <field name="email_from">${(object.user_id.email or '')|safe}</field>
-             <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
+             <field name="subject">${object.company_id.name|safe} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
              <field name="partner_to">${object.partner_invoice_id.id}</field>
              <field name="model_id" ref="sale.model_sale_order"/>
              <field name="auto_delete" eval="True"/>
              <field name="report_template" ref="sale.report_sale_order"/>
              <field name="report_name">${(object.name or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''}</field>
              <field name="lang">${object.partner_id.lang}</field>
+             <field name="user_signature" eval="True"/>
              <field name="body_html"><![CDATA[
  <div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); ">
  
@@@ -26,9 -27,6 +27,9 @@@
         &nbsp;&nbsp;Order number: <strong>${object.name}</strong><br />
         &nbsp;&nbsp;Order total: <strong>${object.amount_total} ${object.pricelist_id.currency_id.name}</strong><br />
         &nbsp;&nbsp;Order date: ${object.date_order}<br />
 +       % if object.validity_date and object.state in ('draft', 'sent'):
 +       &nbsp;&nbsp;Expiration date: ${object.validity_date}<br />
 +       % endif
         % if object.origin:
         &nbsp;&nbsp;Order reference: ${object.origin}<br />
         % endif
          <record id="email_template_edi_invoice" model="email.template">
              <field name="name">Invoice - Send by Email (Portal)</field>
              <field name="email_from">${(object.user_id.email or object.company_id.email or 'noreply@localhost')|safe}</field>
-             <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })</field>
+             <field name="subject">${object.company_id.name|safe} Invoice (Ref ${object.number or 'n/a' })</field>
              <field name="partner_to">${object.partner_id.id}</field>
              <field name="model_id" ref="account.model_account_invoice"/>
              <field name="auto_delete" eval="True"/>
                      <filter string="Consumable" name="consumable" icon="terp-accessories-archiver" domain="[('type','=','consu')]" help="Consumable products"/>
                      <separator/>
                      <filter string="Can be Sold" name="filter_to_sell" icon="terp-accessories-archiver-minus" domain="[('sale_ok','=',1)]"/>
+                     <field name="categ_id"/>
                      <field string="Product Variant" name="product_variant_ids" filter_domain="['|', ('product_variant_ids.name','ilike',self), ('product_variant_ids.attribute_value_ids.name','ilike',self)]"/>
                      <field name="company_id"/>
                      <field name="pricelist_id" widget="selection" context="{'pricelist': self}" filter_domain="[]" groups="product.group_sale_pricelist"/> <!-- Keep widget=selection on this field to pass numeric `self` value, which is not the case for regular m2o widgets! -->
                      <group  expand='0' string='Group by...'>
+                        <filter string='Category' domain="[]" context="{'group_by' : 'categ_id'}"/>
                         <filter string='Default Unit of Measure' icon="terp-mrp" domain="[]" context="{'group_by' : 'uom_id'}"/>
                         <filter string='Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}"/>
                      </group>
                                          <field name="state"/>
                                          <field name="product_manager"/>
                                      </group>
-                                     <group name="weight" string="Weights">
-                                         <field digits="(14, 3)" name="volume" attrs="{'readonly':[('type','=','service')]}"/>
-                                         <field digits="(14, 3)" name="weight" attrs="{'readonly':[('type','=','service')]}"/>
-                                         <field digits="(14, 3)" name="weight_net" attrs="{'readonly':[('type','=','service')]}"/>
+                                     <group name="weight" string="Weights" attrs="{'invisible':[('type','=','service')]}">
+                                         <field digits="(14, 3)" name="volume"/>
+                                         <field digits="(14, 3)" name="weight"/>
+                                         <field digits="(14, 3)" name="weight_net"/>
                                      </group>
                                  </group>
                              </page>
              <field name="res_model">product.attribute.value</field>
              <field name="view_mode">tree</field>
              <field name="view_type">form</field>
+             <field name="view_id" ref="variants_template_tree_view"/>
              <field name="domain">[('product_ids.product_tmpl_id', '=', active_id)]</field>
              <field name="context">{'default_product_tmpl_id': active_id}</field>
          </record>
                          <field name="attribute_line_ids" widget="one2many_list">
                              <tree string="Variants" editable="bottom">
                                  <field name="attribute_id"/>
-                                 <field name="value_ids" widget="many2many_tags" domain="[('attribute_id', '=', attribute_id)]" context="{'default_attribute_id': attribute_id}"/>
+                                 <field name="value_ids" widget="many2many_tags" options="{'no_create_edit': True}" domain="[('attribute_id', '=', attribute_id)]" context="{'default_attribute_id': attribute_id}"/>
                              </tree>
                          </field>
                      </page>
              <field name="model">product.category</field>
              <field name="arch" type="xml">
                  <form string="Product Categories">
 -                    <sheet>
 -                        <div class="oe_title">
 -                            <label for="name" class="oe_edit_only"/>
 -                            <h1>
 -                                <field name="name"/>
 -                            </h1>
 -                        </div>
 -                        <group>
 -                            <group name="parent" col="4">
 -                                <field name="parent_id"/>
 -                                <field name="type"/>
 -                            </group>
 +                    <div class="oe_title">
 +                        <label for="name" class="oe_edit_only"/>
 +                        <h1>
 +                            <field name="name"/>
 +                        </h1>
 +                    </div>
 +                    <group>
 +                        <group name="parent" col="4">
 +                            <field name="parent_id"/>
 +                            <field name="type"/>
                          </group>
 -                    </sheet>
 +                    </group>
                  </form>
              </field>
          </record>
@@@ -166,18 -166,14 +166,18 @@@ class project(osv.osv)
      def unlink(self, cr, uid, ids, context=None):
          alias_ids = []
          mail_alias = self.pool.get('mail.alias')
 +        analytic_account_to_delete = set()
          for proj in self.browse(cr, uid, ids, context=context):
              if proj.tasks:
                  raise osv.except_osv(_('Invalid Action!'),
                                       _('You cannot delete a project containing tasks. You can either delete all the project\'s tasks and then delete the project or simply deactivate the project.'))
              elif proj.alias_id:
                  alias_ids.append(proj.alias_id.id)
 +            if proj.analytic_account_id and not proj.analytic_account_id.line_ids:
 +                analytic_account_to_delete.add(proj.analytic_account_id.id)
          res = super(project, self).unlink(cr, uid, ids, context=context)
          mail_alias.unlink(cr, uid, alias_ids, context=context)
 +        self.pool['account.analytic.account'].unlink(cr, uid, list(analytic_account_to_delete), context=context)
          return res
  
      def _get_attached_docs(self, cr, uid, ids, field_name, arg, context):
      _columns = {
          'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the project without removing it."),
          'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of Projects."),
-         'analytic_account_id': fields.many2one('account.analytic.account', 'Contract/Analytic', help="Link this project to an analytic account if you need financial management on projects. It enables you to connect projects with budgets, planning, cost and revenue analysis, timesheets on projects, etc.", ondelete="cascade", required=True),
+         'analytic_account_id': fields.many2one(
+             'account.analytic.account', 'Contract/Analytic',
+             help="Link this project to an analytic account if you need financial management on projects. "
+                  "It enables you to connect projects with budgets, planning, cost and revenue analysis, timesheets on projects, etc.",
+             ondelete="cascade", required=True, auto_join=True),
          'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members',
              help="Project's members are users who can have an access to the tasks related to this project.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
          'tasks': fields.one2many('project.task', 'project_id', "Task Activities"),
@@@ -535,11 -535,7 +539,11 @@@ def Project()
  
          project_id = super(project, self).create(cr, uid, vals, context=create_context)
          project_rec = self.browse(cr, uid, project_id, context=context)
 -        self.pool.get('mail.alias').write(cr, uid, [project_rec.alias_id.id], {'alias_parent_thread_id': project_id, 'alias_defaults': {'project_id': project_id}}, context)
 +        ir_values = self.pool.get('ir.values').get_default( cr, uid, 'project.config.settings', 'generate_project_alias' )
 +        values = { 'alias_parent_thread_id': project_id, 'alias_defaults': {'project_id': project_id}}
 +        if ir_values:
 +            values = dict(values, alias_name=vals['name'])
 +        self.pool.get('mail.alias').write(cr, uid, [project_rec.alias_id.id], values, context=context)
          return project_id
  
      def write(self, cr, uid, ids, vals, context=None):
@@@ -703,10 -699,10 +707,10 @@@ class task(osv.osv)
          if default is None:
              default = {}
          if not default.get('name'):
-             current = self.browse(cr, uid, id, context=context)       
+             current = self.browse(cr, uid, id, context=context)
              default['name'] = _("%s (copy)") % current.name
          return super(task, self).copy_data(cr, uid, id, default, context)
-     
      def _is_template(self, cr, uid, ids, field_name, arg, context=None):
          res = {}
          for task in self.browse(cr, uid, ids, context=context):
          'project_id': _get_default_project_id,
          'date_last_stage_update': fields.datetime.now,
          'kanban_state': 'normal',
-         'priority': '1',
+         'priority': '0',
          'progress': 0,
          'sequence': 10,
          'active': True,
          'company_id': lambda self, cr, uid, ctx=None: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=ctx),
          'partner_id': lambda self, cr, uid, ctx=None: self._get_default_partner(cr, uid, context=ctx),
      }
-     _order = "priority, sequence, date_start, name, id"
-     
+     _order = "priority desc, sequence, date_start, name, id"
      def _check_recursion(self, cr, uid, ids, context=None):
          for id in ids:
              visited_branch = set()
              'planned_hours': 0.0,
          }
          defaults.update(custom_values)
 -        return super(task, self).message_new(cr, uid, msg, custom_values=defaults, context=context)
 +        res = super(task, self).message_new(cr, uid, msg, custom_values=defaults, context=context)
 +        email_list = tools.email_split(msg.get('to', '') + ',' + msg.get('cc', ''))
 +        new_task = self.browse(cr, uid, res, context=context)
 +        if new_task.project_id and new_task.project_id.alias_name:  # check left-part is not already an alias
 +            email_list = filter(lambda x: x.split('@')[0] != new_task.project_id.alias_name, email_list)
 +        partner_ids = filter(lambda x: x, self._find_partner_from_emails(cr, uid, None, email_list, context=context, check_followers=False))
 +        self.message_subscribe(cr, uid, [res], partner_ids, context=context)
 +        return res
  
      def message_update(self, cr, uid, ids, msg, update_vals=None, context=None):
          """ Override to update the task according to the email. """
@@@ -1232,12 -1221,12 +1236,12 @@@ class account_analytic_account(osv.osv)
              self.project_create(cr, uid, account.id, vals_for_project, context=context)
          return super(account_analytic_account, self).write(cr, uid, ids, vals, context=context)
  
 -    def unlink(self, cr, uid, ids, *args, **kwargs):
 -        project_obj = self.pool.get('project.project')
 -        analytic_ids = project_obj.search(cr, uid, [('analytic_account_id','in',ids)])
 -        if analytic_ids:
 -            raise osv.except_osv(_('Warning!'), _('Please delete the project linked with this account first.'))
 -        return super(account_analytic_account, self).unlink(cr, uid, ids, *args, **kwargs)
 +    def unlink(self, cr, uid, ids, context=None):
 +        proj_ids = self.pool['project.project'].search(cr, uid, [('analytic_account_id', 'in', ids)])
 +        has_tasks = self.pool['project.task'].search(cr, uid, [('project_id', 'in', proj_ids)], count=True, context=context)
 +        if has_tasks:
 +            raise osv.except_osv(_('Warning!'), _('Please remove existing tasks in the project linked to the accounts you want to delete.'))
 +        return super(account_analytic_account, self).unlink(cr, uid, ids, context=context)
  
      def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
          if args is None:
@@@ -1324,6 -1313,7 +1328,7 @@@ class project_task_history_cumulative(o
  
      _columns = {
          'end_date': fields.date('End Date'),
+         'nbr_tasks': fields.integer('# of Tasks', readonly=True),
          'project_id': fields.many2one('project.project', 'Project'),
      }
  
                      h.id AS history_id,
                      h.date+generate_series(0, CAST((coalesce(h.end_date, DATE 'tomorrow')::date - h.date) AS integer)-1) AS date,
                      h.task_id, h.type_id, h.user_id, h.kanban_state,
+                     count(h.task_id) as nbr_tasks,
                      greatest(h.remaining_hours, 1) AS remaining_hours, greatest(h.planned_hours, 1) AS planned_hours,
                      t.project_id
                  FROM
                      project_task_history AS h
                      JOIN project_task AS t ON (h.task_id = t.id)
+                 GROUP BY
+                   h.id,
+                   h.task_id,
+                   t.project_id
  
              ) AS history
          )
@@@ -25,9 -25,11 +25,11 @@@ from openerp import ap
  from openerp import SUPERUSER_ID
  from openerp import tools
  from openerp.osv import fields, osv, orm
+ from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
  from openerp.tools import html2plaintext
  from openerp.tools.translate import _
  
  class project_issue_version(osv.Model):
      _name = "project.issue.version"
      _order = "name desc"
@@@ -42,7 -44,7 +44,7 @@@
  class project_issue(osv.Model):
      _name = "project.issue"
      _description = "Project Issue"
-     _order = "priority, create_date desc"
+     _order = "priority desc, create_date desc"
      _inherit = ['mail.thread', 'ir.needaction_mixin']
  
      _mail_post_access = 'read'
          @return: difference between current date and log date
          @param context: A standard dictionary for contextual values
          """
-         cal_obj = self.pool.get('resource.calendar')
-         res_obj = self.pool.get('resource.resource')
+         Calendar = self.pool['resource.calendar']
  
-         res = {}
+         res = dict.fromkeys(ids, dict())
          for issue in self.browse(cr, uid, ids, context=context):
+             values = {
+                 'day_open': 0.0, 'day_close': 0.0,
+                 'working_hours_open': 0.0, 'working_hours_close': 0.0,
+                 'days_since_creation': 0.0, 'inactivity_days': 0.0,
+             }
              # if the working hours on the project are not defined, use default ones (8 -> 12 and 13 -> 17 * 5), represented by None
-             if not issue.project_id or not issue.project_id.resource_calendar_id:
-                 working_hours = None
+             calendar_id = None
+             if issue.project_id and issue.project_id.resource_calendar_id:
+                 calendar_id = issue.project_id.resource_calendar_id.id
+             dt_create_date = datetime.strptime(issue.create_date, DEFAULT_SERVER_DATETIME_FORMAT)
+             if issue.date_open:
+                 dt_date_open = datetime.strptime(issue.date_open, DEFAULT_SERVER_DATETIME_FORMAT)
+                 values['day_open'] = (dt_date_open - dt_create_date).total_seconds() / (24.0 * 3600)
+                 values['working_hours_open'] = Calendar._interval_hours_get(
+                     cr, uid, calendar_id, dt_create_date, dt_date_open,
+                     timezone_from_uid=issue.user_id.id or uid,
+                     exclude_leaves=False, context=context)
+             if issue.date_closed:
+                 dt_date_closed = datetime.strptime(issue.date_closed, DEFAULT_SERVER_DATETIME_FORMAT)
+                 values['day_close'] = (dt_date_closed - dt_create_date).total_seconds() / (24.0 * 3600)
+                 values['working_hours_close'] = Calendar._interval_hours_get(
+                     cr, uid, calendar_id, dt_create_date, dt_date_closed,
+                     timezone_from_uid=issue.user_id.id or uid,
+                     exclude_leaves=False, context=context)
+             days_since_creation = datetime.today() - dt_create_date
+             values['days_since_creation'] = days_since_creation.days
+             if issue.date_action_last:
+                 inactive_days = datetime.today() - datetime.strptime(issue.date_action_last, DEFAULT_SERVER_DATETIME_FORMAT)
+             elif issue.date_last_stage_update:
+                 inactive_days = datetime.today() - datetime.strptime(issue.date_last_stage_update, DEFAULT_SERVER_DATETIME_FORMAT)
              else:
-                 working_hours = issue.project_id.resource_calendar_id.id
+                 inactive_days = datetime.today() - datetime.strptime(issue.create_date, DEFAULT_SERVER_DATETIME_FORMAT)
+             values['inactivity_days'] = inactive_days.days
  
-             res[issue.id] = {}
+             # filter only required values
              for field in fields:
-                 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_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, working_hours,
-                                                            date_create,
-                                                            date_open,
-                                                            timezone_from_uid=issue.user_id.id or uid,
-                                                            exclude_leaves=False,
-                                                            context=context)
-                 elif field in ['working_hours_close','day_close']:
-                     if issue.date_closed:
-                         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, working_hours,
-                                                            date_create,
-                                                            date_close,
-                                                            timezone_from_uid=issue.user_id.id or uid,
-                                                            exclude_leaves=False,
-                                                            context=context)
-                 elif field in ['days_since_creation']:
-                     if issue.create_date:
-                         days_since_creation = datetime.today() - datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
-                         res[issue.id][field] = days_since_creation.days
-                     continue
-                 elif field in ['inactivity_days']:
-                     res[issue.id][field] = 0
-                     if issue.date_action_last:
-                         inactive_days = datetime.today() - datetime.strptime(issue.date_action_last, '%Y-%m-%d %H:%M:%S')
-                         res[issue.id][field] = inactive_days.days
-                     continue
-                 if ans:
-                     resource_id = False
-                     if issue.user_id:
-                         resource_ids = res_obj.search(cr, uid, [('user_id','=',issue.user_id.id)])
-                         if resource_ids and len(resource_ids):
-                             resource_id = resource_ids[0]
-                     duration = float(ans.days) + float(ans.seconds)/(24*3600)
-                 if field in ['working_hours_open','working_hours_close']:
-                     res[issue.id][field] = hours
-                 elif field in ['day_open','day_close']:
-                     res[issue.id][field] = duration
+                 res[issue.id][field] = values[field]
  
          return res
  
              if work.task_id:
                  issues += issue_pool.search(cr, uid, [('task_id','=',work.task_id.id)])
          return issues
      _columns = {
          'id': fields.integer('ID', readonly=True),
          'name': fields.char('Issue', required=True),
          'active': fields.boolean('Active', required=False),
-         'create_date': fields.datetime('Creation Date', readonly=True,select=True),
+         'create_date': fields.datetime('Creation Date', readonly=True, select=True),
          'write_date': fields.datetime('Update Date', readonly=True),
          'days_since_creation': fields.function(_compute_day, string='Days since creation date', \
                                                 multi='compute_day', type="integer", help="Difference in days between creation date and current date"),
                                           required=False),
          'email_from': fields.char('Email', size=128, help="These people will receive email.", select=1),
          'email_cc': fields.char('Watchers Emails', size=256, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
-         'date_open': fields.datetime('Opened', readonly=True,select=True),
+         'date_open': fields.datetime('Assigned', readonly=True, select=True),
          # Project Issue fields
-         'date_closed': fields.datetime('Closed', readonly=True,select=True),
+         'date_closed': fields.datetime('Closed', readonly=True, select=True),
          'date': fields.datetime('Date'),
          'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
          'channel': fields.char('Channel', help="Communication channel."),
          'project_id': fields.many2one('project.project', 'Project', track_visibility='onchange', select=True),
          'duration': fields.float('Duration'),
          'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]"),
-         'day_open': fields.function(_compute_day, string='Days to Open', \
-                                 multi='compute_day', type="float", store=True),
-         'day_close': fields.function(_compute_day, string='Days to Close', \
-                                 multi='compute_day', type="float", store=True),
+         'day_open': fields.function(_compute_day, string='Days to Assign',
+                                     multi='compute_day', type="float",
+                                     store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
+         'day_close': fields.function(_compute_day, string='Days to Close',
+                                      multi='compute_day', type="float",
+                                      store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
          'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1, track_visibility='onchange'),
-         'working_hours_open': fields.function(_compute_day, string='Working Hours to Open the Issue', \
-                                 multi='compute_day', type="float", store=True),
-         'working_hours_close': fields.function(_compute_day, string='Working Hours to Close the Issue', \
-                                 multi='compute_day', type="float", store=True),
-         'inactivity_days': fields.function(_compute_day, string='Days since last action', \
-                                 multi='compute_day', type="integer", help="Difference in days between last action and current date"),
+         'working_hours_open': fields.function(_compute_day, string='Working Hours to assign the Issue',
+                                               multi='compute_day', type="float",
+                                               store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
+         'working_hours_close': fields.function(_compute_day, string='Working Hours to close the Issue',
+                                                multi='compute_day', type="float",
+                                                store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
+         'inactivity_days': fields.function(_compute_day, string='Days since last action',
+                                            multi='compute_day', type="integer", help="Difference in days between last action and current date"),
          'color': fields.integer('Color Index'),
          'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
          'date_action_last': fields.datetime('Last Action', readonly=1),
          'active': 1,
          'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
          'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
-         'priority': '1',
+         'priority': '0',
          'kanban_state': 'normal',
          'date_last_stage_update': fields.datetime.now,
          'user_id': lambda obj, cr, uid, context: uid,
              default = {}
          default = default.copy()
          default.update(name=_('%s (copy)') % (issue['name']))
-         return super(project_issue, self).copy(cr, uid, id, default=default,
-                 context=context)
+         return super(project_issue, self).copy(cr, uid, id, default=default, context=context)
  
      def create(self, cr, uid, vals, context=None):
          context = dict(context or {})
          if vals.get('project_id') and not context.get('default_project_id'):
              context['default_project_id'] = vals.get('project_id')
+         if vals.get('user_id'):
+             vals['date_open'] = fields.datetime.now()
+         if 'stage_id' in vals:
+             vals.update(self.onchange_stage_id(cr, uid, None, vals.get('stage_id'), context=context)['value'])
  
          # context: no_log, because subtype already handle this
          create_context = dict(context, mail_create_nolog=True)
      def write(self, cr, uid, ids, vals, context=None):
          # stage change: update date_last_stage_update
          if 'stage_id' in vals:
+             vals.update(self.onchange_stage_id(cr, uid, ids, vals.get('stage_id'), context=context)['value'])
              vals['date_last_stage_update'] = fields.datetime.now()
              if 'kanban_state' not in vals:
                  vals['kanban_state'] = 'normal'
          # user_id change: update date_start
          if vals.get('user_id'):
-             vals['date_start'] = fields.datetime.now()
+             vals['date_open'] = fields.datetime.now()
  
          return super(project_issue, self).write(cr, uid, ids, vals, context)
  
      # Stage management
      # -------------------------------------------------------
  
+     def onchange_stage_id(self, cr, uid, ids, stage_id, context=None):
+         if not stage_id:
+             return {'value': {}}
+         stage = self.pool['project.task.type'].browse(cr, uid, stage_id, context=context)
+         if stage.fold:
+             return {'value': {'date_closed': fields.datetime.now()}}
+         return {'value': {'date_closed': False}}
      def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
          """ Override of the base.stage method
              Parameter of the stage search taken from the issue:
@@@ -517,13 -514,6 +514,13 @@@ class account_analytic_account(osv.Mode
          res = super(account_analytic_account, self)._trigger_project_creation(cr, uid, vals, context=context)
          return res or (vals.get('use_issues') and not 'project_creation_in_progress' in context)
  
 +    def unlink(self, cr, uid, ids, context=None):
 +        proj_ids = self.pool['project.project'].search(cr, uid, [('analytic_account_id', 'in', ids)])
 +        has_issues = self.pool['project.issue'].search(cr, uid, [('project_id', 'in', proj_ids)], count=True, context=context)
 +        if has_issues:
 +            raise osv.except_osv(_('Warning!'), _('Please remove existing issues in the project linked to the accounts you want to delete.'))
 +        return super(account_analytic_account, self).unlink(cr, uid, ids, context=context)
 +
  
  class project_project(osv.Model):
      _inherit = 'project.project'
@@@ -19,8 -19,8 +19,8 @@@
          <!--Email template -->
          <record id="email_template_edi_purchase" model="email.template">
              <field name="name">RFQ - Send by Email</field>
-             <field name="email_from">${object.validator.email or ''}</field>
-             <field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
+             <field name="email_from">${(object.validator.email or '')|safe}</field>
+             <field name="subject">${object.company_id.name|safe} Order (Ref ${object.name or 'n/a' })</field>
              <field name="partner_to">${object.partner_id.id}</field>
              <field name="model_id" ref="purchase.model_purchase_order"/>
              <field name="auto_delete" eval="True"/>
@@@ -35,7 -35,7 +35,7 @@@
      <p>Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}: </p>
      
      <p style="border-left: 1px solid #8e0000; margin-left: 30px;">
 -       &nbsp;&nbsp;<strong>REFERENCES</strong><br />
 +       &nbsp;&nbsp;<strong>OUR REFERENCES</strong><br />
         &nbsp;&nbsp;RFQ number: <strong>${object.name}</strong><br />
         &nbsp;&nbsp;RFQ date: ${object.date_order}<br />
         % if object.origin:
      <p>Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}: </p>
      
      <p style="border-left: 1px solid #8e0000; margin-left: 30px;">
 -       &nbsp;&nbsp;<strong>REFERENCES</strong><br />
 +       &nbsp;&nbsp;<strong>OUR REFERENCES</strong><br />
         &nbsp;&nbsp;Order number: <strong>${object.name}</strong><br />
         &nbsp;&nbsp;Order total: <strong>${object.amount_total} ${object.currency_id.name}</strong><br />
         &nbsp;&nbsp;Order date: ${object.date_order}<br />
@@@ -252,7 -252,7 +252,7 @@@ class purchase_order(osv.osv)
                                      help="It indicates that an invoice has been validated"),
          'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'),
          'invoice_method': fields.selection([('manual','Based on Purchase Order lines'),('order','Based on generated draft invoice'),('picking','Based on incoming shipments')], 'Invoicing Control', required=True,
 -            readonly=True, states={'draft':[('readonly',False)], 'sent':[('readonly',False)]},
 +            readonly=True, states={'draft':[('readonly',False)], 'sent':[('readonly',False)],'bid':[('readonly',False)]},
              help="Based on Purchase Order lines: place individual lines in 'Invoice Control / On Purchase Order lines' from where you can selectively create an invoice.\n" \
                  "Based on generated invoice: create a draft invoice you can validate later.\n" \
                  "Based on incoming shipments: let you create an invoice when receipts are validated."
                  'move_dest_id': procurement.move_dest_id.id,  #move destination is same as procurement destination
                  'group_id': procurement.group_id.id or group_id,  #move group is same as group of procurements if it exists, otherwise take another group
                  'procurement_id': procurement.id,
-                 'invoice_state': procurement.rule_id.invoice_state or (procurement.location_id and procurement.location_id.usage == 'customer' and procurement.invoice_state) or (order.invoice_method == 'picking' and '2binvoiced') or 'none', #dropship case takes from sale
+                 'invoice_state': procurement.rule_id.invoice_state or (procurement.location_id and procurement.location_id.usage == 'customer' and procurement.invoice_state=='picking' and '2binvoiced') or (order.invoice_method == 'picking' and '2binvoiced') or 'none', #dropship case takes from sale
              })
              diff_quantity -= min(procurement_qty, diff_quantity)
              res.append(tmp)
@@@ -47,7 -47,7 +47,7 @@@
          <menuitem
              action="purchase_pricelist_version_action" id="menu_purchase_pricelist_version_action"
              parent="menu_purchase_config_pricelist" sequence="2" groups="product.group_purchase_pricelist"/>
 -        
 +
          <menuitem
              action="product.product_price_type_action" id="menu_product_pricelist_action2_purchase_type"
              parent="menu_purchase_config_pricelist" sequence="60" />
                          </group>
                          <group>
                              <field name="date_order"/>
-                             <field name="origin" attr="{'invisible': [('origin','=',False)]}"/>
+                             <field name="origin" attrs="{'invisible': [('origin','=',False)]}"/>
                              <field name="company_id" groups="base.group_multi_company" widget="selection"/>
                              <field name="picking_type_id" on_change="onchange_picking_type_id(picking_type_id, context)" domain="[('code','=','incoming')]" widget="selection" context="{'special_shortened_wh_name': True}" groups="stock.group_locations"/>
                              <field name="related_location_id" invisible="1"/>
                                      <field name="name"/>
                                      <field name="date_planned"/>
                                      <field name="company_id" groups="base.group_multi_company" widget="selection"/>
 -                                    <field name="account_analytic_id" groups="purchase.group_analytic_accounting" domain="[('type','not in',('view','template'))]"/>
 +                                    <field name="account_analytic_id" context="{'default_partner_id':parent.partner_id}" groups="purchase.group_analytic_accounting" domain="[('type','not in',('view','template'))]"/>
                                      <field name="product_qty" on_change="onchange_product_id(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id,parent.date_order,parent.fiscal_position,date_planned,name,price_unit,parent.state,context)"/>
                                      <field name="product_uom" groups="product.group_uom" on_change="onchange_product_uom(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,parent.state,context)"/>
                                      <field name="price_unit"/>
              <field name="arch" type="xml">
                  <div name="options" position="inside">
                      <div>
-                         <field name="purchase_ok" attrs="{'readonly': [('is_product_variant', '=', False)]}"/>
+                         <field name="purchase_ok"/>
                          <label for="purchase_ok"/>
                      </div>
                  </div>
              <field name="name">product.template.purchase.button.inherit</field>
              <field name="model">product.template</field>
              <field name="inherit_id" ref="product.product_template_only_form_view"/>
+             <field name="groups_id" eval="[(4, ref('purchase.group_purchase_user'))]"/>
              <field name="arch" type="xml">
                  <xpath expr="//div[@name='buttons']" position="inside">
                     <button class="oe_inline oe_stat_button" name="action_view_purchases" type="object" 
-                        groups="purchase.group_purchase_user" icon="fa-shopping-cart">
+                        icon="fa-shopping-cart">
                         <field string="Purchases" name="purchase_count" widget="statinfo"/>
                     </button>
                  </xpath>
              <field name="name">product.product.purchase.button.inherit</field>
              <field name="model">product.product</field>
              <field name="inherit_id" ref="product.product_normal_form_view"/>
+             <field name="groups_id" eval="[(4, ref('purchase.group_purchase_user'))]"/>
              <field name="arch" type="xml">
                  <xpath expr="//div[@name='buttons']" position="inside">
                     <button class="oe_inline oe_stat_button" name="%(action_purchase_line_product_tree)d" type="action" 
-                        groups="purchase.group_purchase_user" icon="fa-shopping-cart">
+                        icon="fa-shopping-cart">
                         <field string="Purchases" name="purchase_count" widget="statinfo"/>
                     </button>
                  </xpath>
index dbf735b,0000000..fbcfb30
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,56 @@@
 +# -*- coding: utf-8 -*-
 +##############################################################################
 +#
 +#    OpenERP, Open Source Management Solution
 +#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
 +#
 +#    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': 'Restaurant',
 +    'version': '1.0',
 +    'category': 'Point of Sale',
 +    'sequence': 6,
 +    'summary': 'Restaurant extensions for the Point of Sale ',
 +    'description': """
 +
 +=======================
 +
 +This module adds several restaurant features to the Point of Sale:
 +- Bill Printing: Allows you to print a receipt before the order is paid
 +- Bill Splitting: Allows you to split an order into different orders
 +- Kitchen Order Printing: allows you to print orders updates to kitchen or bar printers
 +
 +""",
 +    'author': 'OpenERP SA',
++    'website': 'https://www.odoo.com',
 +    'depends': ['point_of_sale'],
 +    'data': [
 +        'restaurant_view.xml',
 +        'security/ir.model.access.csv',
 +        'views/templates.xml',
 +    ],
 +    'qweb':[
 +        'static/src/xml/multiprint.xml',
 +        'static/src/xml/splitbill.xml',
 +        'static/src/xml/printbill.xml',
 +    ],
 +    'installable': True,
 +    'auto_install': False,
 +}
 +
 +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@@ -21,7 -21,7 +21,7 @@@
          <record id="email_template_edi_sale" model="email.template">
              <field name="name">Sales Order - Send by Email</field>
              <field name="email_from">${(object.user_id.email or '')|safe}</field>
-             <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
+             <field name="subject">${object.company_id.name|safe} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
              <field name="partner_to">${object.partner_invoice_id.id}</field>
              <field name="model_id" ref="sale.model_sale_order"/>
              <field name="auto_delete" eval="True"/>
@@@ -40,9 -40,6 +40,9 @@@
         &nbsp;&nbsp;Order number: <strong>${object.name}</strong><br />
         &nbsp;&nbsp;Order total: <strong>${object.amount_total} ${object.pricelist_id.currency_id.name}</strong><br />
         &nbsp;&nbsp;Order date: ${object.date_order}<br />
 +       % if object.validity_date and object.state in ('draft', 'sent'):
 +       &nbsp;&nbsp;Expiration date: ${object.validity_date}<br />
 +       % endif
         % if object.origin:
         &nbsp;&nbsp;Order reference: ${object.origin}<br />
         % endif
              <field name="model">res.partner</field>
              <field name="inherit_id" ref="base.view_partner_form" />
              <field name="priority" eval="20"/>
+             <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
              <field name="arch" type="xml">
                  <xpath expr="//div[@name='buttons']" position="inside">
                      <button class="oe_inline oe_stat_button" type="action" name="%(sale.act_res_partner_2_sale_order)d" 
                          attrs="{'invisible': [('customer', '=', False)]}"
-                         icon="fa-strikethrough" groups="base.group_sale_salesman">
+                         icon="fa-strikethrough">
                          <field string="Sales" name="sale_order_count" widget="statinfo"/>
                      </button>     
                  </xpath>
              </field>
          </record>
  
 -        <record id="res_partner_address_type" model="ir.ui.view">
 -            <field name="name">res.partner.view.address_type</field>
 -            <field name="model">res.partner</field>
 -            <field name="inherit_id" ref="base.view_partner_form" />
 -            <field name="arch" type="xml">
 -                <xpath expr="//label[@for='type']" position="attributes">
 -                    <attribute name="groups">sale.group_delivery_invoice_address</attribute>
 -                    <attribute name="invisible">False</attribute>
 -                </xpath>
 -                <xpath expr="//div[@name='div_type']" position="attributes">
 -                    <attribute name="invisible">False</attribute>
 -                    <attribute name="groups">sale.group_delivery_invoice_address</attribute>
 -                </xpath>
 -                <!--  Version-specific hacks to avoid breaking view inheritance when the sale module
 -                      is installed on top of an older 7.0 version of the base module
 -                      (as the second embedded div_type was added later in the 7.0 release)
 -                      TODO: remove the hacks in trunk -->
 -                <xpath expr="//div[@name='div_type'][field[@name='use_parent_address']] | //field[@name='child_ids']//div[@name='div_type']" position="attributes">
 -                    <attribute name="invisible">False</attribute>
 -                    <attribute name="groups">sale.group_delivery_invoice_address</attribute>
 -                </xpath>
 -                <xpath expr="//label[@for='type'][following-sibling::div[@name='div_type']/field[@name='use_parent_address']] | //field[@name='child_ids']//label[@for='type']" position="attributes">
 -                    <attribute name="invisible">False</attribute>
 -                    <attribute name="groups">sale.group_delivery_invoice_address</attribute>
 -                </xpath>
 -            </field>
 -        </record>
 -
      </data>
  </openerp>
  
diff --combined addons/sale/sale.py
@@@ -27,6 -27,12 +27,12 @@@ from openerp.tools import DEFAULT_SERVE
  import openerp.addons.decimal_precision as dp
  from openerp import workflow
  
+ class res_company(osv.Model):
+     _inherit = "res.company"
+     _columns = {
+         'sale_note': fields.text('Default Terms and Conditions', translate=True, help="Default terms and conditions for quotations."),
+     }
  class sale_order(osv.osv):
      _name = "sale.order"
      _inherit = ['mail.thread', 'ir.needaction_mixin']
          'name': fields.char('Order Reference', required=True, copy=False,
              readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True),
          'origin': fields.char('Source Document', help="Reference of the document that generated this sales order request."),
 -        'client_order_ref': fields.char('Reference/Description', copy=False),
 +        'client_order_ref': fields.char('Customer Reference', copy=False),
          'state': fields.selection([
              ('draft', 'Draft Quotation'),
              ('sent', 'Quotation Sent'),
                in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' status is set when the invoice is confirmed\
                 but waiting for the scheduler to run on the order date.", select=True),
          'date_order': fields.datetime('Date', required=True, readonly=True, select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=False),
 +        'validity_date': fields.date('Expiration Date', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
          'create_date': fields.datetime('Creation Date', readonly=True, select=True, help="Date on which sales order is created."),
          'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed.", copy=False),
          'user_id': fields.many2one('res.users', 'Salesperson', states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True, track_visibility='onchange'),
          'payment_term': fields.many2one('account.payment.term', 'Payment Term'),
          'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position'),
          'company_id': fields.many2one('res.company', 'Company'),
 -        'section_id': fields.many2one('crm.case.section', 'Sales Team'),
 +        'section_id': fields.many2one('crm.case.section', 'Sales Team', change_default=True),
          'procurement_group_id': fields.many2one('procurement.group', 'Procurement group', copy=False),
  
      }
      def action_button_confirm(self, cr, uid, ids, context=None):
          assert len(ids) == 1, 'This option should only be used for a single id at a time.'
          self.signal_workflow(cr, uid, ids, 'order_confirm')
-         # redisplay the record as a sales order
-         view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'view_order_form')
-         view_id = view_ref and view_ref[1] or False,
-         return {
-             'type': 'ir.actions.act_window',
-             'name': _('Sales Order'),
-             'res_model': 'sale.order',
-             'res_id': ids[0],
-             'view_type': 'form',
-             'view_mode': 'form',
-             'view_id': view_id,
-             'target': 'current',
-             'nodestroy': True,
-         }
+         return True
+         
      def action_wait(self, cr, uid, ids, context=None):
          context = context or {}
          for o in self.browse(cr, uid, ids):
@@@ -810,6 -801,10 +802,10 @@@ class sale_order_line(osv.osv)
      def need_procurement(self, cr, uid, ids, context=None):
          #when sale is installed only, there is no need to create procurements, that's only
          #further installed modules (sale_service, sale_stock) that will change this.
+         prod_obj = self.pool.get('product.product')
+         for line in self.browse(cr, uid, ids, context=context):
+             if prod_obj.need_procurement(cr, uid, [line.product_id.id], context=context):
+                 return True
          return False
  
      def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
                  flag=False,  # Force name update
                  context=context
              )['value']
+             if defaults.get('tax_id'):
+                 defaults['tax_id'] = [[6, 0, defaults['tax_id']]]
              values = dict(defaults, **values)
          return super(sale_order_line, self).create(cr, uid, values, context=context)
  
      def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
              uom=False, qty_uos=0, uos=False, name='', partner_id=False,
              lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
 -        context = context or {}
 -        lang = lang or context.get('lang', False)
 -        if not partner_id:
 -            raise osv.except_osv(_('No Customer Defined!'), _('Before choosing a product,\n select a customer in the sales form.'))
 -        warning = False
 -        product_uom_obj = self.pool.get('product.uom')
 -        partner_obj = self.pool.get('res.partner')
 -        product_obj = self.pool.get('product.product')
 -        context = {'lang': lang, 'partner_id': partner_id}
 -        partner = partner_obj.browse(cr, uid, partner_id)
 -        lang = partner.lang
 -        context_partner = {'lang': lang, 'partner_id': partner_id}
 +        if context is None:
 +            context = {}
 +        Partner = self.pool['res.partner']
 +        ProductUom = self.pool['product.uom']
 +        Product = self.pool['product.product']
 +        ctx_product = dict(context)
 +        partner = False
 +        if partner_id:
 +            partner = Partner.browse(cr, uid, partner_id, context=context)
 +            ctx_product['lang'] = partner.lang
 +            ctx_product['partner_id'] = partner_id
 +        elif lang:
 +            ctx_product['lang'] = lang
  
          if not product:
              return {'value': {'th_weight': 0,
 -                'product_uos_qty': qty}, 'domain': {'product_uom': [],
 -                   'product_uos': []}}
 +                    'product_uos_qty': qty}, 'domain': {'product_uom': [],
 +                    'product_uos': []}}
          if not date_order:
              date_order = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
  
          result = {}
 -        warning_msgs = ''
 -        product_obj = product_obj.browse(cr, uid, product, context=context_partner)
 +        product_obj = Product.browse(cr, uid, product, context=ctx_product)
  
          uom2 = False
          if uom:
 -            uom2 = product_uom_obj.browse(cr, uid, uom)
 +            uom2 = ProductUom.browse(cr, uid, uom, context=context)
              if product_obj.uom_id.category_id.id != uom2.category_id.id:
                  uom = False
          if uos:
              if product_obj.uos_id:
 -                uos2 = product_uom_obj.browse(cr, uid, uos)
 +                uos2 = ProductUom.browse(cr, uid, uos, context=context)
                  if product_obj.uos_id.category_id.id != uos2.category_id.id:
                      uos = False
              else:
  
          fpos = False
          if not fiscal_position:
 -            fpos = partner.property_account_position or False
 +            fpos = partner and partner.property_account_position or False
          else:
 -            fpos = self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position)
 -        if update_tax: #The quantity only have changed
 -            result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, product_obj.taxes_id)
 +            fpos = self.pool['account.fiscal.position'].browse(cr, uid, fiscal_position)
 +        if update_tax:  # The quantity only have changed
 +            result['tax_id'] = self.pool['account.fiscal.position'].map_tax(cr, uid, fpos, product_obj.taxes_id)
  
          if not flag:
 -            result['name'] = self.pool.get('product.product').name_get(cr, uid, [product_obj.id], context=context_partner)[0][1]
 +            result['name'] = Product.name_get(cr, uid, [product_obj.id], context=ctx_product)[0][1]
              if product_obj.description_sale:
                  result['name'] += '\n'+product_obj.description_sale
          domain = {}
                          [('category_id', '=', product_obj.uom_id.category_id.id)],
                          'product_uos':
                          [('category_id', '=', uos_category_id)]}
 -        elif uos and not uom: # only happens if uom is False
 +        elif uos and not uom:  # only happens if uom is False
              result['product_uom'] = product_obj.uom_id and product_obj.uom_id.id
              result['product_uom_qty'] = qty_uos / product_obj.uos_coeff
              result['th_weight'] = result['product_uom_qty'] * product_obj.weight
 -        elif uom: # whether uos is set or not
 +        elif uom:  # whether uos is set or not
              default_uom = product_obj.uom_id and product_obj.uom_id.id
 -            q = product_uom_obj._compute_qty(cr, uid, uom, qty, default_uom)
 +            q = ProductUom._compute_qty(cr, uid, uom, qty, default_uom)
              if product_obj.uos_id:
                  result['product_uos'] = product_obj.uos_id.id
                  result['product_uos_qty'] = qty * product_obj.uos_coeff
  
          if not uom2:
              uom2 = product_obj.uom_id
 -        # get unit price
  
 -        if not pricelist:
 -            warn_msg = _('You have to select a pricelist or a customer in the sales form !\n'
 -                    'Please set one before choosing a product.')
 -            warning_msgs += _("No Pricelist ! : ") + warn_msg +"\n\n"
 -        else:
 -            price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist],
 +        if pricelist and partner_id:
 +            price = self.pool['product.pricelist'].price_get(cr, uid, [pricelist],
                      product, qty or 1.0, partner_id, {
                          'uom': uom or result.get('product_uom'),
                          'date': date_order,
                          })[pricelist]
 -            if price is False:
 -                warn_msg = _("Cannot find a pricelist line matching this product and quantity.\n"
 -                        "You have to change either the product, the quantity or the pricelist.")
 -
 -                warning_msgs += _("No valid pricelist line found ! :") + warn_msg +"\n\n"
 -            else:
 -                result.update({'price_unit': price})
 -        if warning_msgs:
 -            warning = {
 -                       'title': _('Configuration Error!'),
 -                       'message' : warning_msgs
 -                    }
 -        return {'value': result, 'domain': domain, 'warning': warning}
 +        else:
 +            price = Product.price_get(cr, uid, [product], ptype='list_price', context=ctx_product)[product] or False
 +        result.update({'price_unit': price})
 +        return {'value': result, 'domain': domain}
  
      def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0,
              uom=False, qty_uos=0, uos=False, name='', partner_id=False,
                  raise osv.except_osv(_('Invalid Action!'), _('Cannot delete a sales order line which is in state \'%s\'.') %(rec.state,))
          return super(sale_order_line, self).unlink(cr, uid, ids, context=context)
  
- class res_company(osv.Model):
-     _inherit = "res.company"
-     _columns = {
-         'sale_note': fields.text('Default Terms and Conditions', translate=True, help="Default terms and conditions for quotations."),
-     }
  
  class mail_compose_message(osv.Model):
      _inherit = 'mail.compose.message'
@@@ -1236,7 -1241,7 +1228,7 @@@ class procurement_order(osv.osv)
          from openerp import workflow
          if vals.get('state') in ['done', 'cancel', 'exception']:
              for proc in self.browse(cr, uid, ids, context=context):
-                 if proc.sale_line_id and proc.sale_line_id.order_id and proc.move_ids:
+                 if proc.sale_line_id and proc.sale_line_id.order_id:
                      order_id = proc.sale_line_id.order_id.id
                      if self.pool.get('sale.order').test_procurements_done(cr, uid, [order_id], context=context):
                          workflow.trg_validate(uid, 'sale.order', order_id, 'ship_end', cr)
                      <header>
                          <button name="invoice_recreate" states="invoice_except" string="Recreate Invoice" groups="base.group_user"/>
                          <button name="invoice_corrected" states="invoice_except" string="Ignore Exception" groups="base.group_user"/>
 +                        <button name="action_button_confirm" states="sent" string="Confirm Sale" class="oe_highlight" type="object" groups="base.group_user"/>
                          <button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
                          <button name="action_quotation_send" string="Send by Email" type="object" states="sent,progress,manual" groups="base.group_user"/>
                          <button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
                          <button name="print_quotation" string="Print" type="object" states="sent,progress,manual" groups="base.group_user"/>
                          <button name="action_button_confirm" states="draft" string="Confirm Sale" type="object" groups="base.group_user"/>
 -                        <button name="action_button_confirm" states="sent" string="Confirm Sale" class="oe_highlight" type="object" groups="base.group_user"/>
                          <button name="action_view_invoice" string="View Invoice" type="object" class="oe_highlight"
                              attrs="{'invisible': [('invoice_exists', '=', False)]}" groups="base.group_user"/>
                          <button name="%(action_view_sale_advance_payment_inv)d" string="Create Invoice"
                              type="action" states="manual" class="oe_highlight" groups="base.group_user"/>
                          <button name="copy_quotation" states="cancel" string="New Copy of Quotation" type="object"/>
 -                        <button name="cancel" states="draft,sent" string="Cancel Quotation" groups="base.group_user"/>
 +                        <button name="cancel" states="draft,sent" string="Cancel" groups="base.group_user"/>
                          <button name="action_cancel" states="manual,progress" string="Cancel Order" type="object" groups="base.group_user"/>
                          <button name="invoice_cancel" states="invoice_except" string="Cancel Order" groups="base.group_user"/>
                          <field name="state" widget="statusbar" statusbar_visible="draft,sent,progress,done" statusbar_colors='{"invoice_except":"red","waiting_date":"blue"}'/>
                          </group>
                          <group>
                              <field name="date_order"/>
 -                            <field name="client_order_ref"/>
                              <field domain="[('type','=','sale')]" name="pricelist_id" groups="product.group_sale_pricelist" on_change="onchange_pricelist_id(pricelist_id,order_line)"/>
                              <field name="currency_id" invisible="1"/>
                          </group>
                                      <field name="th_weight" invisible="1"/>
                                      <field name="product_id"
                                          context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}"
-                                         groups="base.group_user" 
+                                         groups="base.group_user"
                                          on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, False, product_uos_qty, False, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/>
                                      <field name="name"/>
                                      <field name="product_uom_qty"
                                      <field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'account.group_account_invoice', 'base.group_sale_salesman_all_leads']}"/>
                                      <field name="section_id" options="{'no_create': True}" groups="base.group_multi_salesteams"/>
                                      <field groups="base.group_no_one" name="origin"/>
 +                                    <field name="client_order_ref"/>
 +                                    <field name="validity_date"/>
                                  </group>
                                  <group name="sale_pay">
                                      <field name="payment_term" options="{'no_create': True}"/>
              <field name="name">product.product.sale.order</field>
              <field name="model">product.product</field>
              <field name="inherit_id" ref="product.product_normal_form_view"/>
+             <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
              <field name="arch" type="xml">
                  <xpath expr="//div[@name='buttons']" position="inside">
                      <button class="oe_inline oe_stat_button" name="%(action_order_line_product_tree)d" 
-                         type="action" groups="base.group_sale_salesman" icon="fa-strikethrough">
+                         type="action" icon="fa-strikethrough">
                          <field string="Sales" name="sales_count" widget="statinfo" />
                      </button>
                  </xpath>
              <field name="name">product.template.sale.order.button</field>
              <field name="model">product.template</field>
              <field name="inherit_id" ref="product.product_template_only_form_view"/>
+             <field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/>
              <field name="arch" type="xml">
                  <xpath expr="//div[@name='buttons']" position="inside">
                      <button class="oe_inline oe_stat_button" name="action_view_sales" 
-                         type="object" groups="base.group_sale_salesman" icon="fa-strikethrough">
+                         type="object" icon="fa-strikethrough">
                          <field string="Sales" name="sales_count" widget="statinfo" />
                      </button>
                  </xpath>
                  </xpath>
              </field>
          </record>
-         
         <!-- Update account invoice list view!-->
          <record model="ir.ui.view" id="account_invoice_tree">
              <field name="name">Account Invoice</field>
                  <xpath expr="//field[@name='user_id']" position="after">
                      <field name="section_id" groups="base.group_multi_salesteams"/>
                  </xpath>
-                 <xpath expr="//group/filter[@string='Due Month']" position="after">
+                 <xpath expr="//group/filter[@string='Status']" position="after">
                      <filter string="Sales Team" domain="[]" context="{'group_by':'section_id'}" groups="base.group_multi_salesteams"/>
                  </xpath>
              </field>
@@@ -28,6 -28,11 +28,6 @@@ function openerp_picking_widgets(instan
              var self = this;
              this.rows = [];
              this.search_filter = "";
 -            jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
 -                return function( elem ) {
 -                    return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
 -                };
 -            });
          },
          get_header: function(){
              return this.getParent().get_header();
              });
              return locations;
          },
+         get_logisticunit: function(){
+             var model = this.getParent();
+             var ul = [];
+             var self = this;
+             _.each(model.uls, function(ulog){
+                 ul.push({name: ulog.name, id: ulog.id,});
+             });
+             return ul;
+         },
          get_rows: function(){
              var model = this.getParent();
              this.rows = [];
@@@ -54,6 -68,7 +63,7 @@@
                      if (packopline.product_qty < packopline.qty_done){ color = "danger "; }
                      //also check that we don't have a line already existing for that package
                      if (packopline.result_package_id[1] !== undefined && $.inArray(packopline.result_package_id[0], pack_created) === -1){
+                         var myPackage = $.grep(model.packages, function(e){ return e.id == packopline.result_package_id[0]; })[0];
                          self.rows.push({
                              cols: { product: packopline.result_package_id[1],
                                      qty: '',
@@@ -70,6 -85,8 +80,8 @@@
                                      can_scan: false,
                                      head_container: true,
                                      processed: packopline.processed,
+                                     package_id: myPackage.id,
+                                     ul_id: myPackage.ul_id[0],
                              },
                              classes: ('success container_head ') + (packopline.processed === "true" ? 'processed hidden ':''),
                          });
                                  can_scan: packopline.result_package_id[1] === undefined ? true : false,
                                  head_container: false,
                                  processed: packopline.processed,
+                                 package_id: undefined,
+                                 ul_id: -1,
                          },
                          classes: color + (packopline.result_package_id[1] !== undefined ? 'in_container_hidden ' : '') + (packopline.processed === "true" ? 'processed hidden ':''),
                      });
              this.$('.oe_searchbox').keyup(function(event){
                  self.on_searchbox($(this).val());
              });
-             this.$('.js_pick_pack').click(function(){ self.getParent().pack(); });
+             this.$('.js_putinpack').click(function(){ self.getParent().pack(); });
              this.$('.js_drop_down').click(function(){ self.getParent().drop_down();});
-             this.$('.js_clear_search').click(function(){ 
-                 self.on_searchbox(''); 
+             this.$('.js_clear_search').click(function(){
+                 self.on_searchbox('');
                  self.$('.oe_searchbox').val('');
              });
              this.$('.oe_searchbox').focus(function(){
                      //we unfold
                      line.addClass('warning');
                      select.removeClass('in_container_hidden');
-                     select.addClass('in_container'); 
+                     select.addClass('in_container');
                  }
                  else{
                      //we fold
                      line.removeClass('warning');
                      select = self.$('.js_pack_op_line.in_container[data-container-id='+op_id+']')
                      select.removeClass('in_container');
-                     select.addClass('in_container_hidden'); 
+                     select.addClass('in_container_hidden');
                  }
              });
              this.$('.js_create_lot').click(function(){
                  $lot_modal.modal()
                  //focus input
                  $lot_modal.on('shown.bs.modal', function(){
-                     self.$('.js_lot_scan').focus();    
+                     self.$('.js_lot_scan').focus();
                  })
                  //reactivate scanner when dialog close
                  $lot_modal.on('hidden.bs.modal', function(){
                  self.getParent().barcode_scanner.disconnect();
              });
              this.$('.js_qty').blur(function(){
-                 this.value = "";
+                 var op_id = $(this).parents("[data-id]:first").data('id');
+                 var value = parseFloat($(this).val());
+                 if (value>=0){
+                     self.getParent().set_operation_quantity(value, op_id);
+                 }
+                 
                  self.getParent().barcode_scanner.connect(function(ean){
                      self.getParent().scan(ean);
                  });
  
                  }
              });
+             this.$('.js_pack_configure').click(function(){
+                 var pack_id = $(this).parents(".js_pack_op_line:first").data('package-id');
+                 var ul_id = $(this).parents(".js_pack_op_line:first").data('ulid');
+                 self.$('#js_packconf_select').val(ul_id);
+                 self.$('#js_packconf_select').data('pack-id',pack_id);
+                 self.$el.siblings('#js_PackConfModal').modal();
+             });
+             this.$('.js_validate_pack').click(function(){
+                 //get current selection
+                 var select_dom_element = self.$('#js_packconf_select');
+                 var ul_id = self.$('#js_packconf_select option:selected').data('ul-id');
+                 var pack_id = select_dom_element.data('pack-id');
+                 self.$el.siblings('#js_PackConfModal').modal('hide');
+                 if (pack_id){
+                     self.getParent().set_package_pack(pack_id, ul_id);
+                     $('.container_head[data-package-id="'+pack_id+'"]').data('ulid', ul_id);
+                 }
+             });
+             
              //remove navigtion bar from default openerp GUI
              $('td.navbar').html('<div></div>');
          },
              //get all visible element and if none has positive qty, disable put in pack and process button
              var self = this;
              var processed = this.$('.js_pack_op_line.processed');
-             var qties = this.$('.js_pack_op_line:not(.processed):not(.hidden) .js_qty').map(function(){return $(this).attr('placeholder')});
+             var qties = this.$('.js_pack_op_line:not(.processed):not(.hidden) .js_qty').map(function(){return $(this).val()});
              var container = this.$('.js_pack_op_line.container_head:not(.processed):not(.hidden)')
              var disabled = true;
              $.each(qties,function(index, value){
                      self.pickings_by_type[0] = [];
  
                      return new instance.web.Model('stock.picking').call('search_read',[ [['state','in', ['assigned', 'partially_available']]], [] ], {context: new instance.web.CompoundContext()});
-                                                                   
                  }).then(function(pickings){
                      self.pickings = pickings;
                      for(var i = 0; i < pickings.length; i++){
                  //avoid crash if a not supported char is given (like '\' or ')')
                return [];
              }
-             
              var results = [];
              for(var i = 0; i < 100; i++){
                  r = re.exec(this.picking_search_string);
              this.packages = null;
              this.barcode_scanner = new module.BarcodeScanner();
              this.locations = [];
+             this.uls = [];
              if(this.picking_id){
                  this.loaded =  this.load(this.picking_id);
              }else{
          load: function(picking_id){
              var self = this;
  
-        
              function load_picking_list(type_id){
                  var pickings = new $.Deferred();
                  new instance.web.Model('stock.picking')
                          if(!picking_ids || picking_ids.length === 0){
                              (new instance.web.Dialog(self,{
                                  title: _t('No Picking Available'),
-                                 buttons: [{ 
-                                     text:_t('Ok'), 
+                                 buttons: [{
+                                     text:_t('Ok'),
                                      click: function(){
                                          self.menu();
                                      }
                          return load_picking_list(self.picking.picking_type_id[0]);
                      });
              }else{
-                 // if we don't have a specified picking id, we load the pickings belong to the specified type, and then we take 
+                 // if we don't have a specified picking id, we load the pickings belong to the specified type, and then we take
                  // the first one of that list as the active picking
                  var loaded_picking = new $.Deferred();
                  load_picking_list(self.picking_type_id)
                      return new instance.web.Model('stock.quant.package').call('read',[package_ids, [], new instance.web.CompoundContext()])
                  }).then(function(packages){
                      self.packages = packages;
+                 }).then(function(){
+                         return new instance.web.Model('product.ul').call('search',[[]])
+                 }).then(function(uls_ids){
+                         return new instance.web.Model('product.ul').call('read',[uls_ids, []])
+                 }).then(function(uls){
+                     self.uls = uls;
                  });
          },
          start: function(){
              this.barcode_scanner.connect(function(ean){
                  self.scan(ean);
              });
-             
              this.$('.js_pick_quit').click(function(){ self.quit(); });
              this.$('.js_pick_prev').click(function(){ self.picking_prev(); });
              this.$('.js_pick_next').click(function(){ self.picking_next(); });
              this.$('.js_pick_menu').click(function(){ self.menu(); });
              this.$('.js_reload_op').click(function(){ self.reload_pack_operation();});
  
              $.when(this.loaded).done(function(){
                  self.picking_editor = new module.PickingEditorWidget(self);
                  self.picking_editor.replace(self.$('.oe_placeholder_picking_editor'));
-                 
                  if( self.picking.id === self.pickings[0]){
                      self.$('.js_pick_prev').addClass('disabled');
                  }else{
                      self.$('.js_pick_prev').removeClass('disabled');
                  }
-                 
                  if( self.picking.id === self.pickings[self.pickings.length-1] ){
                      self.$('.js_pick_next').addClass('disabled');
                  }else{
              var self = this;
              self.picking_editor.on_searchbox(query.toUpperCase());
          },
-         // reloads the data from the provided picking and refresh the ui. 
+         // reloads the data from the provided picking and refresh the ui.
          // (if no picking_id is provided, gets the first picking in the db)
          refresh_ui: function(picking_id){
              var self = this;
                      }else{
                          self.$('.js_pick_prev').removeClass('disabled');
                      }
-                     
                      if( self.picking.id === self.pickings[self.pickings.length-1] ){
                          self.$('.js_pick_next').addClass('disabled');
                      }else{
              }
  
          },
+         set_package_pack: function(package_id, pack){
+             var self = this;
+                 new instance.web.Model('stock.quant.package')
+                     .call('write',[[package_id],{'ul_id': pack }]);
+             return;
+         },
          reload_pack_operation: function(){
              var self = this;
              new instance.web.Model('stock.picking')
              this._super();
              // this.disconnect_numpad();
              this.barcode_scanner.disconnect();
-             $('body').off('keyup',this.hotkey_handler);
              instance.webclient.set_content_full_screen(false);
          },
      });
diff --combined addons/stock/stock.py
@@@ -785,7 -785,7 +785,7 @@@ class stock_picking(osv.osv)
                   store={'stock.move': (_get_pickings, ['date_expected'], 20)}, type='datetime', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, string='Scheduled Date', select=1, help="Scheduled time for the first part of the shipment to be processed. Setting manually a value here would set it as expected date for all the stock moves.", track_visibility='onchange'),
          'max_date': fields.function(get_min_max_date, multi="min_max_date",
                   store={'stock.move': (_get_pickings, ['date_expected'], 20)}, type='datetime', string='Max. Expected Date', select=2, help="Scheduled time for the last part of the shipment to be processed"),
-         'date': fields.datetime('Commitment Date', help="Date promised for the completion of the transfer order, usually set the time of the order and revised later on.", select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, track_visibility='onchange'),
+         'date': fields.datetime('Creation Date', help="Creation Date, usually the time of the order", select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, track_visibility='onchange'),
          'date_done': fields.datetime('Date of Transfer', help="Date of Completion", states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=False),
          'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=True),
          'quant_reserved_exist': fields.function(_get_quant_reserved_exist, type='boolean', string='Quant already reserved ?', help='technical field used to know if there is already at least one quant reserved on moves of a given picking'),
  
      @api.cr_uid_ids_context
      def open_barcode_interface(self, cr, uid, picking_ids, context=None):
 -        final_url="/barcode/web/#action=stock.ui&picking_id="+str(picking_ids[0])
 +        final_url="/stock/barcode/#action=stock.ui&picking_id="+str(picking_ids[0])
          return {'type': 'ir.actions.act_url', 'url':final_url, 'target': 'self',}
  
      @api.cr_uid_ids_context
  
                          #check if the quant is matching the operation details
                          if ops.package_id:
-                             flag = quant.package_id and bool(package_obj.search(cr, uid, [('id', 'child_of', [ops.package_id.id]), ('id', '=', quant.package_id.id)], context=context)) or False
+                             flag = quant.package_id and bool(package_obj.search(cr, uid, [('id', 'child_of', [ops.package_id.id])], context=context)) or False
                          else:
                              flag = not quant.package_id.id
                          flag = flag and ((ops.lot_id and ops.lot_id.id == quant.lot_id.id) or not ops.lot_id)
              move_obj.action_confirm(cr, uid, moves, context=context)
          return moves
  
      def rereserve_quants(self, cr, uid, picking, move_ids=[], context=None):
          """ Unreserve quants then try to reassign quants."""
          stock_move_obj = self.pool.get('stock.move')
              stock_move_obj.action_assign(cr, uid, move_ids, context=context)
  
      @api.cr_uid_ids_context
+     def do_enter_transfer_details(self, cr, uid, picking, context=None):
+         if not context:
+             context = {}
+         context.update({
+             'active_model': self._name,
+             'active_ids': picking,
+             'active_id': len(picking) and picking[0] or False
+         })
+         created_id = self.pool['stock.transfer_details'].create(cr, uid, {'picking_id': len(picking) and picking[0] or False}, context)
+         return self.pool['stock.transfer_details'].wizard_view(cr, uid, created_id, context)
+     @api.cr_uid_ids_context
      def do_transfer(self, cr, uid, picking_ids, context=None):
          """
              If no pack operation, we do simple action_done of the picking
                  todo_move_ids = []
                  if not all_op_processed:
                      todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context)
-                     
                  picking.refresh()
                  #split move lines eventually
-                 
                  toassign_move_ids = []
                  for move in picking.move_lines:
                      remaining_qty = move.remaining_qty
                          todo_move_ids.append(move.id)
                          #Assign move as it was assigned before
                          toassign_move_ids.append(new_move)
-                 if (need_rereserve or not all_op_processed) and not picking.location_id.usage in ("supplier", "production", "inventory"):
-                     self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context)
+                 if need_rereserve or not all_op_processed: 
+                     if not picking.location_id.usage in ("supplier", "production", "inventory"):
+                         self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context)
                      self.do_recompute_remaining_quantities(cr, uid, [picking.id], context=context)
                  if todo_move_ids and not context.get('do_only_split'):
                      self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context)
                          stock_operation_obj.write(cr, uid, operation.id, {'product_qty': operation.product_qty - operation.qty_done,'qty_done': 0, 'lot_id': False}, context=context)
                          op = stock_operation_obj.browse(cr, uid, new_operation, context=context)
                      pack_operation_ids.append(op.id)
-                     for record in op.linked_move_operation_ids:
-                         stock_move_obj.check_tracking(cr, uid, record.move_id, op.package_id.id or op.lot_id.id, context=context)
+                     if op.product_id and op.location_id and op.location_dest_id:
+                         stock_move_obj.check_tracking_product(cr, uid, op.product_id, op.lot_id.id, op.location_id, op.location_dest_id, context=context)
                  package_id = package_obj.create(cr, uid, {}, context=context)
                  stock_operation_obj.write(cr, uid, pack_operation_ids, {'result_package_id': package_id}, context=context)
          return True
@@@ -1804,8 -1821,8 +1821,8 @@@ class stock_move(osv.osv)
                      if wh_route_ids:
                          rules = push_obj.search(cr, uid, domain + [('route_id', 'in', wh_route_ids)], order='route_sequence, sequence', context=context)
                      if not rules:
-                         #if no specialized push rule has been found yet, we try to find a general one
-                         rules = push_obj.search(cr, uid, domain, order='route_sequence, sequence', context=context)
+                         #if no specialized push rule has been found yet, we try to find a general one (without route)
+                         rules = push_obj.search(cr, uid, domain + [('route_id', '=', False)], order='sequence', context=context)
                  if rules:
                      rule = push_obj.browse(cr, uid, rules[0], context=context)
                      push_obj._apply(cr, uid, rule, move, context=context)
          product = self.pool.get('product.product').browse(cr, uid, [prod_id], context=ctx)[0]
          uos_id = product.uos_id and product.uos_id.id or False
          result = {
+             'name': product.partner_ref,
              'product_uom': product.uom_id.id,
              'product_uos': uos_id,
              'product_uom_qty': 1.00,
              'product_uos_qty': self.pool.get('stock.move').onchange_quantity(cr, uid, ids, prod_id, 1.00, product.uom_id.id, uos_id)['value']['product_uos_qty'],
          }
-         if not ids:
-             result['name'] = product.partner_ref
          if loc_id:
              result['location_id'] = loc_id
          if loc_dest_id:
          """
          return self.write(cr, uid, ids, {'state': 'assigned'}, context=context)
  
-     def check_tracking(self, cr, uid, move, lot_id, context=None):
-         """ Checks if serial number is assigned to stock move or not and raise an error if it had to.
-         """
+     def check_tracking_product(self, cr, uid, product, lot_id, location, location_dest, context=None):
          check = False
-         if move.product_id.track_all and not move.location_dest_id.usage == 'inventory':
+         if product.track_all and not location_dest.usage == 'inventory':
              check = True
-         elif move.product_id.track_incoming and move.location_id.usage in ('supplier', 'transit', 'inventory') and move.location_dest_id.usage == 'internal':
+         elif product.track_incoming and location.usage in ('supplier', 'transit', 'inventory') and location_dest.usage == 'internal':
              check = True
-         elif move.product_id.track_outgoing and move.location_dest_id.usage in ('customer', 'transit') and move.location_id.usage == 'internal':
+         elif product.track_outgoing and location_dest.usage in ('customer', 'transit') and location.usage == 'internal':
              check = True
          if check and not lot_id:
-             raise osv.except_osv(_('Warning!'), _('You must assign a serial number for the product %s') % (move.product_id.name))
+             raise osv.except_osv(_('Warning!'), _('You must assign a serial number for the product %s') % (product.name))
+     def check_tracking(self, cr, uid, move, lot_id, context=None):
+         """ Checks if serial number is assigned to stock move or not and raise an error if it had to.
+         """
+         self.check_tracking_product(cr, uid, move.product_id, lot_id, move.location_id, move.location_dest_id, context=context)
+         
  
      def action_assign(self, cr, uid, ids, context=None):
          """ Checks the product type and accordingly writes the state.
              main_domain = [('qty', '>', 0)]
              for record in ops.linked_move_operation_ids:
                  move = record.move_id
-                 self.check_tracking(cr, uid, move, ops.package_id.id or ops.lot_id.id, context=context)
+                 self.check_tracking(cr, uid, move, not ops.product_id and ops.package_id.id or ops.lot_id.id, context=context)
                  prefered_domain = [('reservation_id', '=', move.id)]
                  fallback_domain = [('reservation_id', '=', False)]
                  fallback_domain2 = ['&', ('reservation_id', '!=', move.id), ('reservation_id', '!=', False)]
          defaults = {
              'product_uom_qty': uom_qty,
              'product_uos_qty': uos_qty,
-             'state': move.state,
              'procure_method': 'make_to_stock',
              'restrict_lot_id': restrict_lot_id,
              'restrict_partner_id': restrict_partner_id,
              'product_uos_qty': move.product_uos_qty - uos_qty,
          }, context=ctx)
  
-         if move.move_dest_id and move.propagate:
+         if move.move_dest_id and move.propagate and move.move_dest_id.state not in ('done', 'cancel'):
              new_move_prop = self.split(cr, uid, move.move_dest_id, qty, context=context)
              self.write(cr, uid, [new_move], {'move_dest_id': new_move_prop}, context=context)
          #returning the first element of list returned by action_confirm is ok because we checked it wouldn't be exploded (and
@@@ -2598,7 -2618,7 +2618,7 @@@ class stock_inventory(osv.osv)
              domain += ' and lot_id = %s'
              args += (inventory.lot_id.id,)
          if inventory.product_id:
-             domain += 'and product_id = %s'
+             domain += ' and product_id = %s'
              args += (inventory.product_id.id,)
          if inventory.package_id:
              domain += ' and package_id = %s'
@@@ -2745,7 -2765,7 +2765,7 @@@ class stock_warehouse(osv.osv)
          'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, select=True),
          'partner_id': fields.many2one('res.partner', 'Address'),
          'view_location_id': fields.many2one('stock.location', 'View Location', required=True, domain=[('usage', '=', 'view')]),
-         'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True, domain=[('usage', '=', 'internal')]),
+         'lot_stock_id': fields.many2one('stock.location', 'Location Stock', domain=[('usage', '=', 'internal')], required=True),
          'code': fields.char('Short Name', size=5, required=True, help="Short name used to identify your warehouse"),
          'route_ids': fields.many2many('stock.location.route', 'stock_route_warehouse', 'warehouse_id', 'route_id', 'Routes', domain="[('warehouse_selectable', '=', True)]", help='Defaults routes through the warehouse'),
          'reception_steps': fields.selection([
                  if wh.delivery_steps == 'ship_only':
                      output_loc = wh.lot_stock_id
                      # Create extra MTO rule (only for 'ship only' because in the other cases MTO rules already exists)
-                     mto_pull_vals = self._get_mto_pull_rule(cr, uid, wh, [(output_loc, transit_location, wh.out_type_id.id)], context=context)
+                     mto_pull_vals = self._get_mto_pull_rule(cr, uid, wh, [(output_loc, transit_location, wh.out_type_id.id)], context=context)[0]
                      pull_obj.create(cr, uid, mto_pull_vals, context=context)
                  inter_wh_route_vals = self._get_inter_wh_route(cr, uid, warehouse, wh, context=context)
                  inter_wh_route_id = route_obj.create(cr, uid, vals=inter_wh_route_vals, context=context)
                  if default_resupply_wh and default_resupply_wh.id == wh.id:
                      self.write(cr, uid, [warehouse.id], {'route_ids': [(4, inter_wh_route_id)]}, context=context)
  
-     def _default_stock_id(self, cr, uid, context=None):
-         #lot_input_stock = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock')
-         try:
-             warehouse = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'warehouse0')
-             return warehouse.lot_stock_id.id
-         except:
-             return False
      _defaults = {
          'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c),
-         'lot_stock_id': _default_stock_id,
          'reception_steps': 'one_step',
          'delivery_steps': 'ship_only',
      }
  
      def _get_mto_pull_rule(self, cr, uid, warehouse, values, context=None):
          mto_route_id = self._get_mto_route(cr, uid, context=context)
-         from_loc, dest_loc, pick_type_id = values[0]
-         return {
+         res = []
+         for value in values:
+             from_loc, dest_loc, pick_type_id = value
+             res += [{
              'name': self._format_rulename(cr, uid, warehouse, from_loc, dest_loc, context=context) + _(' MTO'),
              'location_src_id': from_loc.id,
              'location_id': dest_loc.id,
              'procure_method': 'make_to_order',
              'active': True,
              'warehouse_id': warehouse.id,
-         }
+             }]
+         return res
  
      def _get_crossdock_route(self, cr, uid, warehouse, route_name, context=None):
          return {
          for pull_rule in pull_rules_list:
              pull_obj.create(cr, uid, vals=pull_rule, context=context)
          #create MTO pull rule and link it to the generic MTO route
-         mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, values, context=context)
+         mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, values, context=context)[0]
          mto_pull_id = pull_obj.create(cr, uid, mto_pull_vals, context=context)
  
          #create a route for cross dock operations, that can be set on products and product categories
  
          #change MTO rule
          dummy, values = routes_dict[new_delivery_step]
-         mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, values, context=context)
+         mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, values, context=context)[0]
          pull_obj.write(cr, uid, warehouse.mto_pull_id.id, mto_pull_vals, context=context)
          return True
  
          route_obj = self.pool.get("stock.location.route")
          pull_obj = self.pool.get("procurement.rule")
          routes = route_obj.search(cr, uid, [('supplier_wh_id','=', warehouse.id)], context=context)
-         pulls= pull_obj.search(cr, uid, ['&', ('route_id', 'in', routes), ('location_id.usage', '=', 'transit')], context=context)
+         pulls = pull_obj.search(cr, uid, ['&', ('route_id', 'in', routes), ('location_id.usage', '=', 'transit')], context=context)
          if pulls:
              pull_obj.write(cr, uid, pulls, {'location_src_id': new_location, 'procure_method': change_to_multiple and "make_to_order" or "make_to_stock"}, context=context)
          # Create or clean MTO rules
              transfer_locs = list(set([x.location_id for x in pull_recs]))
              vals = [(warehouse.lot_stock_id , x, warehouse.out_type_id.id) for x in transfer_locs]
              mto_pull_vals = self._get_mto_pull_rule(cr, uid, warehouse, vals, context=context)
-             pull_obj.create(cr, uid, mto_pull_vals, context=context)
+             for mto_pull_val in mto_pull_vals:
+                 pull_obj.create(cr, uid, mto_pull_val, context=context)
          else:
              # We need to delete all the MTO pull rules, otherwise they risk to be used in the system
              pulls = pull_obj.search(cr, uid, ['&', ('route_id', '=', mto_route_id), ('location_id.usage', '=', 'transit'), ('location_src_id', '=', warehouse.lot_stock_id.id)], context=context)
@@@ -3621,7 -3636,7 +3636,7 @@@ class stock_package(osv.osv)
  
      def action_print(self, cr, uid, ids, context=None):
          context = dict(context or {}, active_ids=ids)
-         return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_package_barcode', context=context)
+         return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_package_barcode_small', context=context)
      
      
      def unpack(self, cr, uid, ids, context=None):
@@@ -3750,9 -3765,9 +3765,9 @@@ class stock_pack_operation(osv.osv)
          'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'),
          'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
          'qty_done': fields.float('Quantity Processed', digits_compute=dp.get_precision('Product Unit of Measure')),
-         'package_id': fields.many2one('stock.quant.package', 'Package'),  # 2
+         'package_id': fields.many2one('stock.quant.package', 'Source Package'),  # 2
          'lot_id': fields.many2one('stock.production.lot', 'Lot/Serial Number'),
-         'result_package_id': fields.many2one('stock.quant.package', 'Container Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
+         'result_package_id': fields.many2one('stock.quant.package', 'Destination Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
          'date': fields.datetime('Date', required=True),
          'owner_id': fields.many2one('res.partner', 'Owner', help="Owner of the quants"),
          #'update_cost': fields.boolean('Need cost update'),
          'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'),
          'linked_move_operation_ids': fields.one2many('stock.move.operation.link', 'operation_id', string='Linked Moves', readonly=True, help='Moves impacted by this operation for the computation of the remaining quantities'),
          'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Qty'),
-         'location_id': fields.many2one('stock.location', 'Location From', required=True),
-         'location_dest_id': fields.many2one('stock.location', 'Location To', required=True),
+         'location_id': fields.many2one('stock.location', 'Source Location', required=True),
+         'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True),
          'processed': fields.selection([('true','Yes'), ('false','No')],'Has been processed?', required=True),
      }
  
              operation in two to process the one with the qty moved
          '''
          processed_ids = []
+         move_obj = self.pool.get("stock.move")
          for pack_op in self.browse(cr, uid, ids, context=None):
+             if pack_op.product_id and pack_op.location_id and pack_op.location_dest_id:
+                 move_obj.check_tracking_product(cr, uid, pack_op.product_id, pack_op.lot_id.id, pack_op.location_id, pack_op.location_dest_id, context=context)
              op = pack_op.id
              if pack_op.qty_done < pack_op.product_qty:
                  # we split the operation in two
                  new_lot_id = lots[0]
              val.update({'name': name})
  
-         if not obj.lot_id:
-             if not new_lot_id:
-                 new_lot_id = self.pool.get('stock.production.lot').create(cr, uid, val, context=context)
-             self.write(cr, uid, id, {'lot_id': new_lot_id}, context=context)
+         if not new_lot_id:
+             new_lot_id = self.pool.get('stock.production.lot').create(cr, uid, val, context=context)
+         self.write(cr, uid, id, {'lot_id': new_lot_id}, context=context)
  
      def _search_and_increment(self, cr, uid, picking_id, domain, filter_visible=False, visible_op_ids=False, increment=True, context=None):
          '''Search for an operation with given 'domain' in a picking, if it exists increment the qty (+1) otherwise create it
@@@ -3983,7 -4000,7 +4000,7 @@@ class stock_warehouse_orderpoint(osv.os
              "a procurement to bring the forecasted quantity to the Quantity specified as Max Quantity."),
          'qty_multiple': fields.float('Qty Multiple', required=True,
              digits_compute=dp.get_precision('Product Unit of Measure'),
-             help="The procurement quantity will be rounded up to this multiple."),
+             help="The procurement quantity will be rounded up to this multiple.  If it is 0, the exact quantity will be used.  "),
          'procurement_ids': fields.one2many('procurement.order', 'orderpoint_id', 'Created Procurements'),
          'group_id': fields.many2one('procurement.group', 'Procurement Group', help="Moves created through this orderpoint will be put in this procurement group. If none is given, the moves generated by procurement rules will be grouped into one big picking.", copy=False),
          'company_id': fields.many2one('res.company', 'Company', required=True),
          'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.warehouse.orderpoint', context=context)
      }
      _sql_constraints = [
-         ('qty_multiple_check', 'CHECK( qty_multiple > 0 )', 'Qty Multiple must be greater than zero.'),
+         ('qty_multiple_check', 'CHECK( qty_multiple >= 0 )', 'Qty Multiple must be greater than or equal to zero.'),
      ]
      _constraints = [
          (_check_product_uom, 'You have to select a product unit of measure in the same category than the default unit of measure of the product', ['product_id', 'product_uom']),
@@@ -4043,7 -4060,7 +4060,7 @@@ class stock_picking_type(osv.osv)
      _order = 'sequence'
  
      def open_barcode_interface(self, cr, uid, ids, context=None):
 -        final_url = "/barcode/web/#action=stock.ui&picking_type_id=" + str(ids[0]) if len(ids) else '0'
 +        final_url = "/stock/barcode/#action=stock.ui&picking_type_id=" + str(ids[0]) if len(ids) else '0'
          return {'type': 'ir.actions.act_url', 'url': final_url, 'target': 'self'}
  
      def _get_tristate_values(self, cr, uid, ids, field_name, arg, context=None):
index 8c6fac9,0000000..db076e8
mode 100644,000000..100644
--- /dev/null
@@@ -1,206 -1,0 +1,206 @@@
 +<?xml version="1.0" encoding="utf-8"?>\r
 +<openerp>\r
 +<data>\r
 +\r
 +<template id="theme_customize" inherit_id="website.theme_customize" name="theme_customize Bootswatch">\r
 +    <xpath expr="div" position="replace">\r
 +\r
 +      <div id="theme_customize_modal" class="modal fade bs-example-modal-sm">\r
 +        <div class="modal-dialog">\r
 +          <div class="modal-content">\r
 +            <div class="loading_backdrop"></div>\r
 +            <div class="modal-header">\r
 +              <button type="button" class="close">×</button>\r
 +              <h4 class="modal-title">Customize your theme</h4>\r
 +            </div>\r
 +            <div class="modal-body">\r
 +              <table>\r
 +                <tr>\r
 +                  <td>\r
 +                    <label class="checked">\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/img/bootswatch_default_thumbnail.png" alt="Default Theme"/>\r
 +                      <input name="theme" type="radio" data-xmlid="" checked="checked"/>\r
 +                    </label>\r
 +                  </td>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/amelia/thumbnail.png" alt="Amelia"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_amelia"/>\r
 +                    </label>\r
 +                  </td>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/cerulean/thumbnail.png" alt="Cerulean"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_cerulean"/>\r
 +                    </label>\r
 +                  </td>\r
 +                </tr>\r
 +                <tr>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/cosmo/thumbnail.png" alt="Cosmo"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_cosmo" data-disable="ee"/>\r
 +                    </label>\r
 +                  </td>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/cyborg/thumbnail.png" alt="Cyborg"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_cyborg"/>\r
 +                    </label>\r
 +                  </td>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/flatly/thumbnail.png" alt="Flatly"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_flatly"/>\r
 +                    </label>\r
 +                  </td>\r
 +                </tr>\r
 +                <tr>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/journal/thumbnail.png" alt="Journal"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_journal"/>\r
 +                    </label>\r
 +                  </td>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/readable/thumbnail.png" alt="Readable"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_readable"/>\r
 +                    </label>\r
 +                  </td>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/simplex/thumbnail.png" alt="Simplex"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_simplex"/>\r
 +                    </label>\r
 +                  </td>\r
 +                </tr>\r
 +                <tr>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/slate/thumbnail.png" alt="Slate"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_slate"/>\r
 +                    </label>\r
 +                  </td>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/spacelab/thumbnail.png" alt="Spacelab"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_spacelab"/>\r
 +                    </label>\r
 +                  </td>\r
 +                  <td>\r
 +                    <label>\r
 +                      <img style="width: 120px; height: 70px;" src="/theme_bootswatch/static/src/less/united/thumbnail.png" alt="United"/>\r
 +                      <input name="theme" type="radio" data-xmlid="website.theme_united"/>\r
 +                    </label>\r
 +                  </td>\r
 +                </tr>\r
 +              </table>\r
 +            </div>\r
 +          </div>\r
 +        </div>\r
 +      </div>\r
 +\r
 +    </xpath>\r
 +</template>\r
 +\r
 +<!--\r
 +     Apply Bootstrap less files\r
 +-->\r
 +\r
 +    <record id="website.option_bootstrap_less" model="ir.ui.view">\r
 +        <field name="application">always</field>\r
 +    </record>\r
 +\r
 +<!--\r
 +     All Default Themes\r
 +-->\r
 +\r
-     <template id="website.theme_amelia" name="Amelia" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_amelia" name="Amelia" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/amelia/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/amelia/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/amelia/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_cerulean" name="Cerulean" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_cerulean" name="Cerulean" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/cerulean/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/cerulean/variables.less' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_cosmo" name="Cosmo" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_cosmo" name="Cosmo" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/cosmo/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/cosmo/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/cosmo/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_cyborg" name="Cyborg" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_cyborg" name="Cyborg" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/cyborg/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/cyborg/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/cyborg/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_flatly" name="Flatly" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_flatly" name="Flatly" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/flatly/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/flatly/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/flatly/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_journal" name="Journal" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_journal" name="Journal" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/journal/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/journal/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/journal/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_readable" name="Readable" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_readable" name="Readable" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/readable/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/readable/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/readable/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_simplex" name="Simplex" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_simplex" name="Simplex" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/simplex/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/simplex/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/simplex/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_slate" name="Slate" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_slate" name="Slate" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/slate/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/slate/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/slate/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_spacelab" name="Spacelab" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_spacelab" name="Spacelab" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/spacelab/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/spacelab/variables.less' t-ignore="true"/>\r
 +            <link rel='stylesheet' href='/theme_bootswatch/static/src/less/spacelab/fix.css' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
-     <template id="website.theme_united" name="United" inherit_id="website.theme" optional="disabled">\r
++    <template id="website.theme_united" name="United" inherit_id="website.theme" active="False" customize_show="False">\r
 +        <xpath expr="//link[last()]" position="after">\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/united/bootswatch.less' t-ignore="true"/>\r
 +            <link rel="stylesheet" type="text/less" href='/theme_bootswatch/static/src/less/united/variables.less' t-ignore="true"/>\r
 +        </xpath>\r
 +    </template>\r
 +\r
 +</data>\r
 +</openerp>\r
@@@ -32,6 -32,7 +32,7 @@@ except ImportError
  import openerp
  import openerp.modules.registry
  from openerp.addons.base.ir.ir_qweb import AssetsBundle, QWebTemplateNotFound
+ from openerp.tools import topological_sort
  from openerp.tools.translate import _
  from openerp import http
  
@@@ -141,50 -142,6 +142,6 @@@ def ensure_db(redirect='/web/database/s
  
      request.session.db = db
  
- def module_topological_sort(modules):
-     """ Return a list of module names sorted so that their dependencies of the
-     modules are listed before the module itself
-     modules is a dict of {module_name: dependencies}
-     :param modules: modules to sort
-     :type modules: dict
-     :returns: list(str)
-     """
-     dependencies = set(itertools.chain.from_iterable(modules.itervalues()))
-     # incoming edge: dependency on other module (if a depends on b, a has an
-     # incoming edge from b, aka there's an edge from b to a)
-     # outgoing edge: other module depending on this one
-     # [Tarjan 1976], http://en.wikipedia.org/wiki/Topological_sorting#Algorithms
-     #L ← Empty list that will contain the sorted nodes
-     L = []
-     #S ← Set of all nodes with no outgoing edges (modules on which no other
-     #    module depends)
-     S = set(module for module in modules if module not in dependencies)
-     visited = set()
-     #function visit(node n)
-     def visit(n):
-         #if n has not been visited yet then
-         if n not in visited:
-             #mark n as visited
-             visited.add(n)
-             #change: n not web module, can not be resolved, ignore
-             if n not in modules: return
-             #for each node m with an edge from m to n do (dependencies of n)
-             for m in modules[n]:
-                 #visit(m)
-                 visit(m)
-             #add n to L
-             L.append(n)
-     #for each node n in S do
-     for n in S:
-         #visit(n)
-         visit(n)
-     return L
  def module_installed():
      # Candidates module the current heuristic is the /static dir
      loadable = http.addons_manifest.keys()
              dependencies = [i['name'] for i in deps_read]
              modules[module['name']] = dependencies
  
-     sorted_modules = module_topological_sort(modules)
+     sorted_modules = topological_sort(modules)
      return sorted_modules
  
  def module_installed_bypass_session(dbname):
                      modules[module['name']] = dependencies
      except Exception,e:
          pass
-     sorted_modules = module_topological_sort(modules)
+     sorted_modules = topological_sort(modules)
      return sorted_modules
  
  def module_boot(db=None):
@@@ -590,34 -547,6 +547,34 @@@ class WebClient(http.Controller)
      def jslist(self, mods=None):
          return manifest_list('js', mods=mods)
  
 +    @http.route('/web/webclient/locale/<string:lang>', type='http', auth="none")
 +    def load_locale(self, lang):
 +        magic_file_finding = [lang.replace("_",'-').lower(), lang.split('_')[0]]
 +        addons_path = http.addons_manifest['web']['addons_path']
 +        #load datejs locale
 +        datejs_locale = ""
 +        try:
 +            with open(os.path.join(addons_path, 'web', 'static', 'lib', 'datejs', 'globalization', lang.replace('_', '-') + '.js'), 'r') as f:
 +                datejs_locale = f.read()
 +        except IOError:
 +            pass
 +
 +        #load momentjs locale
 +        momentjs_locale_file = False
 +        momentjs_locale = ""
 +        for code in magic_file_finding:
 +            try:
 +                with open(os.path.join(addons_path, 'web', 'static', 'lib', 'moment', 'locale', code + '.js'), 'r') as f:
 +                    momentjs_locale = f.read()
 +                #we found a locale matching so we can exit
 +                break
 +            except IOError:
 +                continue
 +
 +        #return the content of the locale
 +        headers = [('Content-Type', 'application/javascript'), ('Cache-Control', 'max-age=%s' % (36000))]
 +        return request.make_response(datejs_locale + "\n"+ momentjs_locale, headers)
 +
      @http.route('/web/webclient/qweb', type='http', auth="none")
      def qweb(self, mods=None, db=None):
          files = [f[0] for f in manifest_glob('qweb', addons=mods, db=db)]
@@@ -710,7 -639,8 +667,8 @@@ class Proxy(http.Controller)
          from werkzeug.test import Client
          from werkzeug.wrappers import BaseResponse
  
-         return Client(request.httprequest.app, BaseResponse).get(path).data
+         base_url = request.httprequest.base_url
+         return Client(request.httprequest.app, BaseResponse).get(path, base_url=base_url).data
  
  class Database(http.Controller):
  
          password, db = operator.itemgetter(
              'drop_pwd', 'drop_db')(
                  dict(map(operator.itemgetter('name', 'value'), fields)))
-         
          try:
              if request.session.proxy("db").drop(password, db):
                  return True
@@@ -1289,7 -1219,7 +1247,7 @@@ class Binary(http.Controller)
  class Action(http.Controller):
  
      @http.route('/web/action/load', type='json', auth="user")
-     def load(self, action_id, do_not_eval=False):
+     def load(self, action_id, do_not_eval=False, additional_context=None):
          Actions = request.session.model('ir.actions.actions')
          value = False
          try:
  
          base_action = Actions.read([action_id], ['type'], request.context)
          if base_action:
-             ctx = {}
+             ctx = request.context
              action_type = base_action[0]['type']
              if action_type == 'ir.actions.report.xml':
                  ctx.update({'bin_size': True})
-             ctx.update(request.context)
+             if additional_context:
+                 ctx.update(additional_context)
              action = request.session.model(action_type).read([action_id], False, ctx)
              if action:
                  value = clean_action(action[0])
@@@ -1489,16 -1420,18 +1448,18 @@@ class ExportFormat(object)
          raise NotImplementedError()
  
      def base(self, data, token):
+         params = simplejson.loads(data)
          model, fields, ids, domain, import_compat = \
              operator.itemgetter('model', 'fields', 'ids', 'domain',
                                  'import_compat')(
-                 simplejson.loads(data))
+                 params)
  
          Model = request.session.model(model)
-         ids = ids or Model.search(domain, 0, False, False, request.context)
+         context = dict(request.context or {}, **params.get('context', {}))
+         ids = ids or Model.search(domain, 0, False, False, context)
  
          field_names = map(operator.itemgetter('name'), fields)
-         import_data = Model.export_data(ids, field_names, self.raw_data, context=request.context).get('datas',[])
+         import_data = Model.export_data(ids, field_names, self.raw_data, context=context).get('datas',[])
  
          if import_compat:
              columns_headers = field_names
@@@ -1686,7 -1619,7 +1647,7 @@@ class Apps(http.Controller)
          sakey = Session().save_session_action(action)
          debug = '?debug' if req.debug else ''
          return werkzeug.utils.redirect('/web{0}#sa={1}'.format(debug, sakey))
-         
  
  
  # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
    padding-top: 8px;
    margin-right: 4px !important;
  }
- .openerp .btn-group.kanban_state .dropdown-menu {
-   min-width: 100%;
-   padding-right: 10px !important;
- }
- .openerp .btn-group.kanban_state .dropdown-menu li a, .openerp .btn-group.kanban_state .dropdown-menu li a:hover, .openerp .btn-group.kanban_state .dropdown-menu li a:focus {
-   padding-left: 5px;
-   padding-right: 0px;
- }
  .openerp .btn-group.kanban_state a {
    color: #333333;
  }
    height: 16px;
  }
  .openerp .oe_form_field_radio.oe_vertical label {
 -  margin-left: 4px;
 +  display: inline-block;
 +  margin-left: 20px;
  }
 -.openerp .oe_form_field_radio.oe_form_required .oe_radio_input {
 +.openerp .oe_form_field_radio .oe_radio_input {
    border: 2px solid transparent;
    display: inline-block;
    height: 12px;
    vertical-align: top;
    border-radius: 10px;
    margin: 1px 0;
 +  position: absolute;
  }
  .openerp .oe_form_field_radio.oe_form_required.oe_form_invalid .oe_radio_input {
    border-color: red;
  .openerp .oe_view_manager_inline > .oe_view_manager_header, .openerp .oe_view_manager_inlineview > .oe_view_manager_header {
    display: none;
  }
+ .openerp .oe_popup_form {
+   display: table;
+ }
  .openerp .oe_popup_form .oe_formview .oe_form_pager {
    display: none !important;
  }
  .openerp .oe_form .oe_form_field_boolean {
    width: auto;
  }
 -.openerp .oe_form .oe_datepicker_container {
 -  display: none;
 -}
  .openerp .oe_form .oe_datepicker_root {
    display: inline-block;
  }
@@@ -3390,3 -3386,9 +3385,9 @@@ div.tour-backdrop 
  body {
    overflow: auto;
  }
+ @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+   .oe_secondary_menus_container {
+     position: static !important;
+   }
+ }
@@@ -498,12 -498,6 +498,6 @@@ $sheet-padding: 16p
      .btn-group.kanban_state
          padding-top: 8px
          margin-right: 4px !important
-         .dropdown-menu
-             min-width: 100%
-             padding-right: 10px !important
-         .dropdown-menu li a, .dropdown-menu li a:hover, .dropdown-menu li a:focus
-             padding-left: 5px
-             padding-right: 0px
          a
              color: #333333
      // }}}
                  height: 16px
          &.oe_vertical
              label
 -                margin-left: 4px
 -        &.oe_form_required
 -            .oe_radio_input
 -                border: 2px solid transparent
                  display: inline-block
 -                height: 12px
 -                width: 12px
 -                vertical-align: top
 -                border-radius: 10px
 -                margin: 1px 0
 -            &.oe_form_invalid
 -                .oe_radio_input
 -                    border-color: red
 +                margin-left: 20px
 +        .oe_radio_input
 +            border: 2px solid transparent
 +            display: inline-block
 +            height: 12px
 +            width: 12px
 +            vertical-align: top
 +            border-radius: 10px
 +            margin: 1px 0
 +            position: absolute
 +        &.oe_form_required.oe_form_invalid .oe_radio_input
 +            border-color: red
      .oe_tags
          &.oe_inline
              min-width: 250px
      // }}}
      // FormPopup {{{
      .oe_popup_form 
+         display: table
          .oe_formview .oe_form_pager
              display: none !important
          // Customize label weight for popup wizard appear from another wizard according bootstrap3
          font-size: 20px
          margin: 15px 0px 10px 0px
          color: $section-title-color
-     .oe_horizontal_separator:empty
-         height: 5px
+         &:empty
+             height: 5px
      .oe_vertical_separator
          border-left: 1px solid #666
          padding: 0 4px 0 4px
              white-space: nowrap
          .oe_form_field_boolean
              width: auto
 -        .oe_datepicker_container
 -            display: none
          .oe_datepicker_root
              display: inline-block
          .oe_form_required
@@@ -2766,5 -2763,13 +2761,13 @@@ div.tour-backdro
  
  body
      overflow: auto
+ // hack to make IE11 work
+ @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) 
+     .oe_secondary_menus_container 
+         position: static !important
  // au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
  // vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
@@@ -1077,7 -1077,10 +1077,10 @@@ instance.web.UserMenu =  instance.web.W
                      state: JSON.stringify(state),
                      scope: 'userinfo',
                  };
-                 instance.web.redirect('https://accounts.openerp.com/oauth2/auth?'+$.param(params));
+                 instance.web.redirect('https://accounts.odoo.com/oauth2/auth?'+$.param(params));
+             }).fail(function(result, ev){
+                 ev.preventDefault();
+                 instance.web.redirect('https://accounts.openerp.com/web');
              });
          }
      },
@@@ -1166,13 -1169,6 +1169,13 @@@ instance.web.Client = instance.web.Widg
              }
          });
      },
 +    on_logo_click: function(ev){
 +        if (!this.has_uncommitted_changes()) {
 +            return;
 +        } else {
 +            ev.preventDefault();
 +        }
 +    },
      show_common: function() {
          var self = this;
          this.crashmanager =  new instance.web.CrashManager();
@@@ -1277,9 -1273,6 +1280,9 @@@ instance.web.WebClient = instance.web.C
              self.logo_edit(ev);
          });
  
 +        this.$('.oe_logo img').click(function(ev) {
 +              self.on_logo_click(ev);
 +          });
          // Menu is rendered server-side thus we don't want the widget to create any dom
          self.menu = new instance.web.Menu(self);
          self.menu.setElement(this.$el.parents().find('.oe_application_menu_placeholder'));
@@@ -311,8 -311,9 +311,8 @@@ instance.web.Session.include( /** @lend
              self.module_list = all_modules;
  
              var loaded = self.load_translations();
 -            var datejs_locale = "/web/static/lib/datejs/globalization/" + self.user_context.lang.replace("_", "-") + ".js";
 -
 -            var file_list = [ datejs_locale ];
 +            var locale = "/web/webclient/locale/" + self.user_context.lang || 'en_US';
 +            var file_list = [ locale ];
              if(to_load.length) {
                  loaded = $.when(
                      loaded,
@@@ -772,6 -773,7 +772,7 @@@ instance.web.unblockUI = function() 
  $.fn.tooltip.Constructor.DEFAULTS.placement = 'auto top';
  $.fn.tooltip.Constructor.DEFAULTS.html = true;
  $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover focus click';
+ $.fn.tooltip.Constructor.DEFAULTS.container = 'body';
  //overwrite bootstrap tooltip method to prevent showing 2 tooltip at the same time
  var bootstrap_show_function = $.fn.tooltip.Constructor.prototype.show;
  $.fn.tooltip.Constructor.prototype.show = function () {
      if (e.isDefaultPrevented() || !inDom) return;
      return bootstrap_show_function.call(this);
  };
- //overwrite bootstrap tooltip init method in order to check if tooltip is in a modal or not and
- //if so it needs to have a container body in order to be visible
- var bootstrap_init_tooltip_fnct = $.fn.tooltip.Constructor.prototype.init;
- $.fn.tooltip.Constructor.prototype.init = function (type, element, options) {
-     options = options || {}
-     if ($('.modal[aria-hidden="false"]').length !== 0){
-         if (options && !options.container){
-             options = _.extend({container: 'body'},options);
-         }
-     }
-     return bootstrap_init_tooltip_fnct.call(this, type, element, options);
- }
  
  /**
   * Registry for all the client actions key: tag value: widget
@@@ -27,8 -27,6 +27,8 @@@ instance.web.DataExport = instance.web.
              this.$('#fields_list').empty();
          },
          'click #export_new_list': 'on_show_save_list',
 +        'click #move_up':'on_click_move_up',
 +        'click #move_down':'on_click_move_down',
      },
      init: function(parent, dataset) {
          var self = this;
              this.rpc('/web/export/formats', {}).done(this.do_setup_export_formats),
              this.show_exports_list());
      },
 +    on_click_move_up: function () {
 +        var prev_row = this.$el.find('#fields_list option:selected').first().prev();
 +        if(prev_row.length){
 +            var selected_rows = self.$('#fields_list option:selected').detach();
 +            prev_row.before(selected_rows);
 +        }
 +    },
 +    on_click_move_down: function () {
 +        var next_row = this.$el.find('#fields_list option:selected').last().next();
 +        if(next_row.length){
 +            var selected_rows = self.$('#fields_list option:selected').detach();
 +            next_row.after(selected_rows);
 +        }
 +    },
      do_setup_export_formats: function (formats) {
          var $fmts = this.$el.find('#export_format');
          _(formats).each(function (format) {
                  fields: exported_fields,
                  ids: this.ids_to_export,
                  domain: this.domain,
+                 context: this.dataset.context,
                  import_compat: !!this.$el.find("#import_compat").val(),
              })},
              complete: instance.web.unblockUI,
@@@ -323,28 -323,24 +323,28 @@@ var Tour = 
      },
      log: function (message, add_user) {
          if (add_user) {
-             var user = $(".navbar .dropdown:has(>.js_usermenu) a:first, .navbar .oe_topbar_name").text();
+             var user = $(".navbar .dropdown:has(>.js_usermenu) a:first, .navbar .oe_topbar_name, .pos .username").text();
              if (!user && $('a[href*="/login"]')) user = 'Public User';
              message += " (" + (user||"").replace(/^\s*|\s*$/g, '') + ")";
          }
          console.log(message);
      },
 -    error: function (step, message) {
 +    logError: function (step, message, all) {
          var state = Tour.getState();
 -        message += '\n tour: ' + state.id
 -            + (step ? '\n step: ' + step.id + ": '" + (step._title || step.title) + "'" : '' )
 -            + '\n href: ' + window.location.href
 -            + '\n referrer: ' + document.referrer
 -            + (step ? '\n element: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) : '' )
 -            + (step ? '\n waitNot: ' + Boolean(!step.waitNot || !$(step.waitNot).size()) : '' )
 -            + (step ? '\n waitFor: ' + Boolean(!step.waitFor || $(step.waitFor).size()) : '' )
 -            + "\n localStorage: " + JSON.stringify(localStorage)
 -            + '\n\n' + $("body").html();
 +        message += '\ntour: ' + state.id
 +            + (step ? '\nstep: ' + step.id + ": '" + (step._title || step.title) + "'" : '' )
 +            + (all ? '\nhref: ' + window.location.href : '' )
 +            + (all ? '\nreferrer: ' + document.referrer : '' )
 +            + (step ? '\nelement: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) : '' )
 +            + (step ? '\nwaitNot: ' + Boolean(!step.waitNot || !$(step.waitNot).size()) : '' )
 +            + (step ? '\nwaitFor: ' + Boolean(!step.waitFor || $(step.waitFor).size()) : '' )
 +            + (all ? "\nlocalStorage: " + JSON.stringify(localStorage) : '' )
 +            + (all ? '\n\n' + $("body").html() : '' );
          Tour.log(message, true);
 +    },
 +    error: function (step, message) {
 +        var state = Tour.getState();
 +        Tour.logError(step, "Error: " + message, true);
          Tour.endTour();
      },
      lists: function () {
          var state = Tour.getState();
          var time = new Date().getTime();
          var timer;
 -        var next = state.tour.steps[state.step.id+1];
 +        var next = state.step.next ? Tour.search_step(state.step.next) : state.tour.steps[state.step.id+1];
          var overlaps = state.mode === "test" ? Tour.errorDelay : 0;
  
          window.onbeforeunload = function () {
  
              clearTimeout(Tour.timer);
              if (Tour.check(next)) {
 +
                  clearTimeout(Tour.currentTimer);
                  // use an other timeout for cke dom loading
                  Tour.saveState(state.id, state.mode, state.step.id, 0);
                  setTimeout(function () {
 +                    if (state.step.onend && Tour._goto(state.step.onend())) return;
                      Tour.nextStep(next);
                  }, Tour.defaultDelay);
 +                return;
 +
              } else if (!overlaps || new Date().getTime() - time < overlaps) {
 +
                  Tour.timer = setTimeout(checkNext, Tour.defaultDelay);
 +                return;
 +
 +            } else if(next.onerror) {
 +                
 +                Tour.logError(next, "Error: Can't reach the next step (call next step onerror)", false);
 +                var id = next.onerror();
 +                if (id) {
 +                    if (Tour._goto(id)) return;
 +                    if (id === true) {
 +                        Tour.nextStep(next);
 +                        return;
 +                    }
 +                }
 +
 +            }
 +            
 +            Tour.error(next, "Can't reach the next step");
 +            return;
 +
 +        }
 +        setTimeout(checkNext, 0);
 +    },
 +    search_step: function (id_or_title) {
 +        var state = Tour.getState();
 +        if (id_or_title !== undefined) {
 +            if (isNaN(id_or_title)) {
 +                for (var k=0; k<state.tour.steps.length; k++) {
 +                    if (state.tour.steps[k].title === id_or_title || state.tour.steps[k]._title === id_or_title) {
 +                        return state.tour.steps[k];
 +                    }
 +                }
              } else {
 -                return Tour.error(next, "Can't reach the next step");
 +                return state.tour.steps[id_or_title];
              }
          }
 -        checkNext();
 +        return undefined;
 +    },
 +    _goto: function (id_or_title) {
 +        var state = Tour.getState();
 +        if (!state) return true;
 +        if (id_or_title === undefined) return false;
 +        var step = Tour.search_step(id_or_title);
 +        Tour.saveState(state.id, state.mode, step.id, 0);
 +        Tour.nextStep(Tour.getState().step);
 +        return true;
      },
      nextStep: function (step) {
          var state = Tour.getState();
          }
  
          step = step || state.step;
 -        var next = state.tour.steps[step.id+1];
 +        var next = state.step.next ? Tour.search_step(state.step.next) : state.tour.steps[step.id+1];
  
          if (state.mode === "test" && state.number > 3) {
              return Tour.error(next, "Cycling. Can't reach the next step");
          
          Tour.saveState(state.id, state.mode, step.id, state.number);
  
 -        if (step.id !== state.step_id) {
 +        if (state.number === 1) {
              Tour.log("Tour '"+state.id+"' Step: '" + (step._title || step.title) + "' (" + (new Date().getTime() - this.time) + "ms)");
          }
  
          Tour.autoTogglePopover(true);
  
 -        if (step.onload) {
 -            step.onload();
 +        // onload a step you can fallback to an other step
 +        if (step.onload && Tour._goto(step.onload())) {
 +            return;
          }
  
 -        if (next) {
 +        if (state.mode === "test") {
              setTimeout(function () {
 -                if (Tour.getState()) {
 +                Tour.autoNextStep(state.tour, step);
 +                if (next && Tour.getState()) {
                      Tour.waitNextStep();
                  }
 -                if (state.mode === "test") {
 -                    setTimeout(function(){
 -                        Tour.autoNextStep(state.tour, step);
 -                    }, Tour.defaultDelay);
 -                }
 -            }, next.wait || 0);
 -        } else {
 -            setTimeout(function(){
 -                Tour.autoNextStep(state.tour, step);
 -            }, Tour.defaultDelay);
 +            }, step.wait || Tour.defaultDelay);
 +        } else if (next) {
 +            setTimeout(Tour.waitNextStep, next.wait || 0);
 +        }
 +        if (!next) {
              Tour.endTour();
          }
      },
              
              }
          }
 -        Tour.testtimer = setTimeout(autoStep, 100);
 +        Tour.testtimer = setTimeout(autoStep, 0);
      },
      autoDragAndDropSnippet: function (selector) {
          var $thumbnail = $(selector).first();
@@@ -103,6 -103,7 +103,7 @@@ instance.web.FormView = instance.web.Vi
          this.fields_order = [];
          this.datarecord = {};
          this._onchange_specs = {};
+         this.onchanges_defs = [];
          this.default_focus_field = null;
          this.default_focus_button = null;
          this.fields_registry = instance.web.form.widgets;
                  opacity: '1',
                  filter: 'alpha(opacity = 100)'
              });
 +            instance.web.bus.trigger('form_view_shown', self);
          });
      },
      do_hide: function () {
                  def = self.alive(new instance.web.Model(self.dataset.model).call(
                      "onchange", [ids, values, trigger_field_name, onchange_specs, context]));
              }
-             return def.then(function(response) {
+             var onchange_def = def.then(function(response) {
                  if (widget && widget.field['change_default']) {
                      var fieldname = widget.name;
                      var value_;
              }).then(function(response) {
                  return self.on_processed_onchange(response);
              });
+             this.onchanges_defs.push(onchange_def);
+             return onchange_def;
          } catch(e) {
              console.error(e);
              instance.webclient.crashmanager.show_message(e);
       * if the current record is not yet saved. It will then stay in create mode.
       */
      to_edit_mode: function() {
+         this.onchanges_defs = [];
          this._actualize_mode("edit");
      },
      /**
                  if (menu) {
                      menu.do_reload_needaction();
                  }
 +                instance.web.bus.trigger('form_view_saved', self);
              });
          }).always(function(){
              $(e.target).attr("disabled", false);
          var self = this;
          var save_obj = {prepend_on_create: prepend_on_create, ret: null};
          this.save_list.push(save_obj);
-         return this._process_operations().then(function() {
-             if (save_obj.error)
-                 return $.Deferred().reject();
-             return $.when.apply($, save_obj.ret);
-         }).done(function() {
-             self.$el.removeClass('oe_form_dirty');
+         return $.when.apply($, self.onchanges_defs).then(function(){
+             return self._process_operations().then(function() {
+                 if (save_obj.error)
+                     return $.Deferred().reject();
+                 return $.when.apply($, save_obj.ret);
+             }).done(function() {
+                 self.$el.removeClass('oe_form_dirty');
+             });
          });
      },
      _process_save: function(save_obj) {
@@@ -2337,26 -2341,31 +2343,31 @@@ instance.web.form.KanbanSelection = ins
      },
      render_value: function() {
          var self = this;
-         this.record_id = self.view.datarecord.id;
-         this.states = self.prepare_dropdown_selection();;
+         this.record_id = this.view.datarecord.id;
+         this.states = this.prepare_dropdown_selection();;
          this.$el.html(QWeb.render("KanbanSelection", {'widget': self}));
-         this.$el.find('.oe_legend').click(self.do_action.bind(self));
+         this.$el.find('li').on('click', this.set_kanban_selection.bind(this));
      },
-     do_action: function(e) {
+     /* setting the value: in view mode, perform an asynchronous call and reload
+     the form view; in edit mode, use set_value to save the new value that will
+     be written when saving the record. */
+     set_kanban_selection: function (ev) {
          var self = this;
-         var li = $(e.target).closest( "li" );
+         var li = $(ev.target).closest('li');
          if (li.length) {
-             var value = {};
-             value[self.name] = String(li.data('value'));
-             self.record_id = self.view.datarecord.id;
-             if (self.record_id) {
-                 return self.view.dataset._model.call('write', [[self.record_id], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self));
-             } else {
-                 return self.view.on_button_save().done(function(result) {
-                     if (result) {
-                         self.view.dataset._model.call('write', [[result], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self));
-                     }
-                 });
+             var value = String(li.data('value'));
+             if (this.view.get('actual_mode') == 'view') {
+                 var write_values = {}
+                 write_values[self.name] = value;
+                 return this.view.dataset._model.call(
+                     'write', [
+                         [self.record_id],
+                         write_values,
+                         self.view.dataset.get_context()
+                     ]).done(self.reload_record.bind(self));
+             }
+             else {
+                 return this.set_value(value);
              }
          }
      },
@@@ -2388,27 -2397,34 +2399,34 @@@ instance.web.form.Priority = instance.w
      },
      render_value: function() {
          var self = this;
-         this.record_id = self.view.datarecord.id;
-         this.priorities = self.prepare_priority();
+         this.record_id = this.view.datarecord.id;
+         this.priorities = this.prepare_priority();
          this.$el.html(QWeb.render("Priority", {'widget': this}));
-         this.$el.find('.oe_legend').click(self.do_action.bind(self));
+         this.$el.find('li').on('click', this.set_priority.bind(this));
      },
-     do_action: function(e) {
+     /* setting the value: in view mode, perform an asynchronous call and reload
+     the form view; in edit mode, use set_value to save the new value that will
+     be written when saving the record. */
+     set_priority: function (ev) {
          var self = this;
-         var li = $(e.target).closest( "li" );
+         var li = $(ev.target).closest('li');
          if (li.length) {
-             var value = {};
-             value[self.name] = String(li.data('value'));
-             if (self.record_id) {
-                 return self.view.dataset._model.call('write', [[self.record_id], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self));
-             } else {
-                 return self.view.on_button_save().done(function(result) {
-                     if (result) {
-                         self.view.dataset._model.call('write', [[result], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self));
-                     }
-                 });
+             var value = String(li.data('value'));
+             if (this.view.get('actual_mode') == 'view') {
+                 var write_values = {}
+                 write_values[self.name] = value;
+                 return this.view.dataset._model.call(
+                     'write', [
+                         [self.record_id],
+                         write_values,
+                         self.view.dataset.get_context()
+                     ]).done(self.reload_record.bind(self));
+             }
+             else {
+                 return this.set_value(value);
              }
          }
      },
      reload_record: function() {
          this.view.reload();
@@@ -2515,14 -2531,10 +2533,10 @@@ instance.web.form.FieldCharDomain = ins
          this._super.apply(this, arguments);
          this.on("change:effective_readonly", this, function () {
              this.display_field();
-             this.render_value();
          });
          this.display_field();
          return this._super();
      },
-     render_value: function() {
-         this.$('button.select_records').css('visibility', this.get('effective_readonly') ? 'hidden': '');
-     },
      set_value: function(value_) {
          var self = this;
          this.set('value', value_ || false);
              var ds = new instance.web.DataSetStatic(self, model, self.build_context());
              ds.call('search_count', [domain]).then(function (results) {
                  $('.oe_domain_count', self.$el).text(results + ' records selected');
-                 $('button span', self.$el).text(' Change selection');
+                 if (self.get('effective_readonly')) {
+                     $('button span', self.$el).text(' See selection');
+                 }
+                 else {
+                     $('button span', self.$el).text(' Change selection');
+                 }
              });
          } else {
              $('.oe_domain_count', this.$el).text('0 record selected');
          var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
          this.pop = new instance.web.form.SelectCreatePopup(this);
          this.pop.select_element(
-             model, {title: 'Select records...'},
-             [], this.build_context());
+             model, {
+                 title: this.get('effective_readonly') ? 'Selected records' : 'Select records...',
+                 readonly: this.get('effective_readonly'),
+                 disable_multiple_selection: this.get('effective_readonly'),
+                 no_create: this.get('effective_readonly'),
+             }, [], this.build_context());
          this.pop.on("elements_selected", self, function(element_ids) {
              if (this.pop.$('input.oe_list_record_selector').prop('checked')) {
                  var search_data = this.pop.searchview.build_search_data();
  
  instance.web.DateTimeWidget = instance.web.Widget.extend({
      template: "web.datepicker",
 -    jqueryui_object: 'datetimepicker',
      type_of_date: "datetime",
      events: {
 -        'change .oe_datepicker_master': 'change_datetime',
 +        'dp.change .oe_datepicker_main': 'change_datetime',
 +        'dp.show .oe_datepicker_main': 'set_datetime_default',
          'keypress .oe_datepicker_master': 'change_datetime',
      },
      init: function(parent) {
      },
      start: function() {
          var self = this;
 +        var l10n = _t.database.parameters;
 +        var options = {
 +            pickTime: true,
 +            useSeconds: true,
 +            startDate: new moment({ y: 1900 }),
 +            endDate: new moment().add(200, "y"),
 +            calendarWeeks: true,
 +            icons : {
 +                time: 'fa fa-clock-o',
 +                date: 'fa fa-calendar',
 +                up: 'fa fa-chevron-up',
 +                down: 'fa fa-chevron-down'
 +               },
 +            language : moment.locale(),
 +            format : instance.web.convert_to_moment_format(l10n.date_format +' '+ l10n.time_format),
 +        };
          this.$input = this.$el.find('input.oe_datepicker_master');
 -        this.$input_picker = this.$el.find('input.oe_datepicker_container');
 -
 -        $.datepicker.setDefaults({
 -            clearText: _t('Clear'),
 -            clearStatus: _t('Erase the current date'),
 -            closeText: _t('Done'),
 -            closeStatus: _t('Close without change'),
 -            prevText: _t('<Prev'),
 -            prevStatus: _t('Show the previous month'),
 -            nextText: _t('Next>'),
 -            nextStatus: _t('Show the next month'),
 -            currentText: _t('Today'),
 -            currentStatus: _t('Show the current month'),
 -            monthNames: Date.CultureInfo.monthNames,
 -            monthNamesShort: Date.CultureInfo.abbreviatedMonthNames,
 -            monthStatus: _t('Show a different month'),
 -            yearStatus: _t('Show a different year'),
 -            weekHeader: _t('Wk'),
 -            weekStatus: _t('Week of the year'),
 -            dayNames: Date.CultureInfo.dayNames,
 -            dayNamesShort: Date.CultureInfo.abbreviatedDayNames,
 -            dayNamesMin: Date.CultureInfo.shortestDayNames,
 -            dayStatus: _t('Set DD as first week day'),
 -            dateStatus: _t('Select D, M d'),
 -            firstDay: Date.CultureInfo.firstDayOfWeek,
 -            initStatus: _t('Select a date'),
 -            isRTL: false
 -        });
 -        $.timepicker.setDefaults({
 -            timeOnlyTitle: _t('Choose Time'),
 -            timeText: _t('Time'),
 -            hourText: _t('Hour'),
 -            minuteText: _t('Minute'),
 -            secondText: _t('Second'),
 -            currentText: _t('Now'),
 -            closeText: _t('Done')
 -        });
 -
 -        this.picker({
 -            onClose: this.on_picker_select,
 -            onSelect: this.on_picker_select,
 -            changeMonth: true,
 -            changeYear: true,
 -            showWeek: true,
 -            showButtonPanel: true,
 -            firstDay: Date.CultureInfo.firstDayOfWeek
 -        });
 -        // Some clicks in the datepicker dialog are not stopped by the
 -        // datepicker and "bubble through", unexpectedly triggering the bus's
 -        // click event. Prevent that.
 -        this.picker('widget').click(function (e) { e.stopPropagation(); });
 -
 -        this.$el.find('img.oe_datepicker_trigger').click(function() {
 -            if (self.get("effective_readonly") || self.picker('widget').is(':visible')) {
 -                self.$input.focus();
 -                return;
 -            }
 -            self.picker('setDate', self.get('value') ? instance.web.auto_str_to_date(self.get('value')) : new Date());
 -            self.$input_picker.show();
 -            self.picker('show');
 -            self.$input_picker.hide();
 -        });
 +        if (this.type_of_date === 'date') {
 +            options['pickTime'] = false;
 +            options['useSeconds'] = false;
 +            options['format'] = instance.web.convert_to_moment_format(l10n.date_format);
 +        }
 +        this.picker = this.$('.oe_datepicker_main').datetimepicker(options);
          this.set_readonly(false);
          this.set({'value': false});
      },
 -    picker: function() {
 -        return $.fn[this.jqueryui_object].apply(this.$input_picker, arguments);
 -    },
 -    on_picker_select: function(text, instance_) {
 -        var date = this.picker('getDate');
 -        this.$input
 -            .val(date ? this.format_client(date) : '')
 -            .change()
 -            .focus();
 -    },
      set_value: function(value_) {
          this.set({'value': value_});
          this.$input.val(value_ ? this.format_client(value_) : '');
      },
      set_value_from_ui_: function() {
          var value_ = this.$input.val() || false;
 -        this.set({'value': this.parse_client(value_)});
 +        this.set_value(this.parse_client(value_));
      },
      set_readonly: function(readonly) {
          this.readonly = readonly;
          this.$input.prop('readonly', this.readonly);
 -        this.$el.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
      },
      is_valid_: function() {
          var value_ = this.$input.val();
      format_client: function(v) {
          return instance.web.format_value(v, {"widget": this.type_of_date});
      },
 +    set_datetime_default: function(){
 +        //when opening datetimepicker the date and time by default should be the one from
 +        //the input field if any or the current day otherwise
 +        if (this.type_of_date === 'datetime') {
 +            value = new moment().second(0);
 +            if (this.$input.val().length !== 0 && this.is_valid_()){
 +                var value = this.$input.val();
 +            }
 +            this.$('.oe_datepicker_main').data('DateTimePicker').setValue(value);
 +        }
 +    },
      change_datetime: function(e) {
          if ((e.type !== "keypress" || e.which === 13) && this.is_valid_()) {
              this.set_value_from_ui_();
  });
  
  instance.web.DateWidget = instance.web.DateTimeWidget.extend({
 -    jqueryui_object: 'datepicker',
      type_of_date: "date"
  });
  
@@@ -2834,7 -2896,7 +2857,7 @@@ instance.web.form.FieldTextHtml = insta
          if (! this.get("effective_readonly")) {
              self._updating_editor = false;
              this.$textarea = this.$el.find('textarea');
-             var width = ((this.node.attrs || {}).editor_width || '100%');
+             var width = ((this.node.attrs || {}).editor_width || 'auto');
              var height = ((this.node.attrs || {}).editor_height || 250);
              this.$textarea.cleditor({
                  width:      width, // width not including margins, borders or padding
@@@ -3121,9 -3183,9 +3144,9 @@@ instance.web.form.FieldRadio = instance
          this._super(field_manager, node);
          this.selection = _.clone(this.field.selection) || [];
          this.domain = false;
 +        this.uniqueId = _.uniqueId("radio");
      },
      initialize_content: function () {
 -        this.uniqueId = _.uniqueId("radio");
          this.on("change:effective_readonly", this, this.render_value);
          this.field_manager.on("view_content_has_changed", this, this.get_selection);
          this.get_selection();
@@@ -3300,7 -3362,7 +3323,7 @@@ instance.web.form.CompletionFieldMixin 
                  });
              }
              // create...
-             if (!(self.options && self.options.no_create)){
+             if (!(self.options && (self.options.no_create || self.options.no_create_edit))){
                  values.push({
                      label: _t("Create and Edit..."),
                      action: function() {
@@@ -3575,7 -3637,7 +3598,7 @@@ instance.web.form.FieldMany2One = insta
                  }
                  self.floating = false;
              }
-             if (used && self.get("value") === false && ! self.no_ed && (self.options.no_create === false || self.options.no_create === undefined)) {
+             if (used && self.get("value") === false && ! self.no_ed && ! (self.options && (self.options.no_create || self.options.no_quick_create))) {
                  self.ed_def.reject();
                  self.uned_def.reject();
                  self.ed_def = $.Deferred();
              focusout: anyoneLoosesFocus,
              focus: function () { self.trigger('focused'); },
              autocompleteopen: function () { ignore_blur = true; },
-             autocompleteclose: function () { ignore_blur = false; },
+             autocompleteclose: function () { setTimeout(function() {ignore_blur = false;},0); },
              blur: function () {
                  // autocomplete open
-                 if (ignore_blur) { return; }
+                 if (ignore_blur) { $(this).focus(); return; }
                  if (_(self.getChildren()).any(function (child) {
                      return child instanceof instance.web.form.AbstractFormPopup;
                  })) { return; }
@@@ -5663,6 -5725,20 +5686,20 @@@ instance.web.form.FieldBinaryImage = in
          this._super.apply(this, arguments);
          this.render_value();
          this.set_filename('');
+     },
+     set_value: function(value_){
+         var changed = value_ !== this.get_value();
+         this._super.apply(this, arguments);
+         // By default, on binary images read, the server returns the binary size
+         // This is possible that two images have the exact same size
+         // Therefore we trigger the change in case the image value hasn't changed
+         // So the image is re-rendered correctly
+         if (!changed){
+             this.trigger("change:value", this, {
+                 oldValue: value_,
+                 newValue: value_
+             });
+         }
      }
  });
  
@@@ -167,7 -167,7 +167,7 @@@ instance.web.ListView = instance.web.Vi
       * @returns {String} CSS style declaration
       */
      style_for: function (record) {
-         var style= '';
+         var len, style= '';
  
          var context = _.extend({}, record.attributes, {
              uid: this.session.uid,
                          self.dataset.index = 0;
                      }
                  } else if (self.dataset.index >= self.records.length) {
-                     self.dataset.index = 0;
+                     self.dataset.index = self.records.length ? 0 : null;
                  }
  
                  self.compute_aggregates();
@@@ -1623,7 -1623,6 +1623,7 @@@ instance.web.ListView.Groups = instance
                      self.setup_resequence_rows(list, dataset);
                  }).always(function() {
                      if (post_render) { post_render(); }
 +                    self.view.trigger('view_list_rendered');
                  });
              });
          return $el;
@@@ -339,13 -339,16 +339,18 @@@ instance.web.ActionManager = instance.w
              return this.do_action(action_client, options);
          } else if (_.isNumber(action) || _.isString(action)) {
              var self = this;
-             return self.rpc("/web/action/load", { action_id: action }).then(function(result) {
+             var additional_context = {
+                 active_id : options.additional_context.active_id,
+                 active_ids : options.additional_context.active_ids,
+                 active_model : options.additional_context.active_model
+             };
+             return self.rpc("/web/action/load", { action_id: action, additional_context : additional_context }).then(function(result) {
                  return self.do_action(result, options);
              });
          }
  
 +        instance.web.bus.trigger('action', action);
 +
          // Ensure context & domain are evaluated and can be manipulated/used
          var ncontext = new instance.web.CompoundContext(options.additional_context, action.context || {});
          action.context = instance.web.pyeval.eval('context', ncontext);
          }
          var widget = executor.widget();
          if (executor.action.target === 'new') {
+             var pre_dialog = this.dialog;
+             if (pre_dialog){
+                 // prevent previous dialog to consider itself closed,
+                 // right now, as we're opening a new one (prevents
+                 // reload of original form view)
+                 pre_dialog.off('closing', null, pre_dialog.on_close);
+             }
              if (this.dialog_widget && !this.dialog_widget.isDestroyed()) {
                  this.dialog_widget.destroy();
              }
+             // explicitly passing a closing action to dialog_stop() prevents
+             // it from reloading the original form view
              this.dialog_stop(executor.action);
              this.dialog = new instance.web.Dialog(this, {
                  title: executor.action.name,
                  dialogClass: executor.klass,
              });
-             this.dialog.on("closing", null, options.on_close);
+             // chain on_close triggers with previous dialog, if any
+             this.dialog.on_close = function(){
+                 options.on_close.apply(null, arguments);
+                 if (pre_dialog && pre_dialog.on_close){
+                     // no parameter passed to on_close as this will
+                     // only be called when the last dialog is truly
+                     // closing, and *should* trigger a reload of the
+                     // underlying form view (see comments above)
+                     pre_dialog.on_close();
+                 }
+             };
+             this.dialog.on("closing", null, this.dialog.on_close);
              if (widget instanceof instance.web.ViewManager) {
                  _.extend(widget.flags, {
                      $buttons: this.dialog.$buttons,
              this.dialog.open();
              return initialized;
          } else  {
+             // explicitly passing a closing action to dialog_stop() prevents
+             // it from reloading the original form view - we're opening a
+             // completely new action anyway
              this.dialog_stop(executor.action);
              this.inner_action = executor.action;
              this.inner_widget = widget;
@@@ -600,7 -627,7 +629,7 @@@ instance.web.ViewManager =  instance.we
                      action_views_ids : views_ids
                  }, self.flags, self.flags[view.view_type] || {}, view.options || {})
              });
-             
              views_ids[view.view_type] = view.view_id;
          });
          if (this.flags.views_switcher === false) {
          }
          // If no default view defined, switch to the first one in sequence
          var default_view = this.flags.default_view || this.views_src[0].view_type;
-   
          return this.switch_mode(default_view, null, this.flags[default_view] && this.flags[default_view].options);
-       
-         
      },
      switch_mode: function(view_type, no_store, view_options) {
          var self = this;
          }
          controller.on('switch_mode', self, this.switch_mode);
          controller.on('previous_view', self, this.prev_view);
-         
          var container = this.$el.find("> div > div > .oe_view_manager_body > .oe_view_manager_view_" + view_type);
          var view_promise = controller.appendTo(container);
          this.views[view_type].controller = controller;
                  }
                  views.push(mode);
              }
 +            instance.web.bus.trigger('view_switch_mode', self, mode);
          });
          var item = _.extend({
              widget: this,
@@@ -1123,7 -1149,7 +1152,7 @@@ instance.web.ViewManagerAction = instan
                      return self.switch_mode(state.view_type, true);
                  })
              );
-         } 
+         }
  
          $.when(this.views[this.active_view] ? this.views[this.active_view].deferred : $.when(), defs).done(function() {
              self.views[self.active_view].controller.do_load_state(state, warm);
@@@ -1209,7 -1235,7 +1238,7 @@@ instance.web.Sidebar = instance.web.Wid
      add_items: function(section_code, items) {
          var self = this;
          if (items) {
 -            this.items[section_code].push.apply(this.items[section_code],items);
 +            this.items[section_code].unshift.apply(this.items[section_code],items);
              this.redraw();
          }
      },
@@@ -1445,12 -1471,12 +1474,12 @@@ instance.web.View = instance.web.Widget
          if (action_data.special === 'cancel') {
              return handler({"type":"ir.actions.act_window_close"});
          } else if (action_data.type=="object") {
-             var args = [[record_id]], additional_args = [];
+             var args = [[record_id]];
              if (action_data.args) {
                  try {
                      // Warning: quotes and double quotes problem due to json and xml clash
                      // Maybe we should force escaping in xml or do a better parse of the args array
-                     additional_args = JSON.parse(action_data.args.replace(/'/g, '"'));
+                     var additional_args = JSON.parse(action_data.args.replace(/'/g, '"'));
                      args = args.concat(additional_args);
                  } catch(e) {
                      console.error("Could not JSON.parse arguments", action_data.args);
      },
      do_show: function () {
          this.$el.show();
 +        instance.web.bus.trigger('view_shown', this);
      },
      do_hide: function () {
          this.$el.hide();
      /**
       * Switches to a specific view type
       */
-     do_switch_view: function() { 
+     do_switch_view: function() {
          this.trigger.apply(this, ['switch_mode'].concat(_.toArray(arguments)));
      },
      /**
      <div class="btn-group kanban_state">
          <t t-foreach="widget.states" t-as="rec">
              <a t-if="widget.get('value') === rec.name">
-                 <a class="oe_legend dropdown-toggle" data-toggle="dropdown">
+                 <a class="dropdown-toggle" data-toggle="dropdown">
                      <span t-att-class="rec.state_class" t-if="widget.get('value') === rec.name" t-att-title="rec.tooltip"/>
                      <span class="sr-only">Toggle Dropdown</span>
                  </a>
          <ul class="dropdown-menu state" role="menu">
              <t t-foreach="widget.states" t-as="rec">
                  <t t-if="widget.get('value') !== rec.name">
-                     <li class="oe_legend" t-att-data-value="rec.name" ><a href="#">
+                     <li t-att-data-value="rec.name" ><a href="#">
                          <span t-att-class="rec.state_class" t-att-title="rec.tooltip"/>
                          <t t-raw="rec.state_name" /></a>
                      </li>
  <t t-name="Priority">
      <ul style="list-style: none; padding-left: 2px; display: inline-block;">
          <t t-foreach="widget.priorities" t-as="rec" >
-             <li t-att-data-value="rec.click_value" class="oe_legend" style="display: inline-block;">
+             <li t-att-data-value="rec.click_value" style="display: inline-block;">
                  <a href="#" t-att-title="rec.name">
                      <span t-att-class="widget.get('value') gte rec.value and 'oe_e oe_star_on' or 'oe_e oe_star_off'">7</span>
                  </a>
  <t t-name="web.datepicker">
      <span>
          <t t-set="placeholder" t-value="widget.getParent().node and widget.getParent().node.attrs.placeholder"/>
 -        <input type="text" class="oe_datepicker_container" disabled="disabled" style="display: none;"/>
 -        <input type="text"
 -            t-att-name="widget.name"
 -            t-att-placeholder="placeholder"
 -            class="oe_datepicker_master"
 -        /><img class="oe_input_icon oe_datepicker_trigger" draggable="false"
 -               t-att-src='_s + "/web/static/src/img/ui/field_calendar.png"'
 -               title="Select date" width="16" height="16" border="0"/>
 +        <div class="oe_datepicker_main input-group">
 +            <input type="text"
 +                t-att-name="widget.name"
 +                t-att-placeholder="placeholder"
 +                class="oe_datepicker_master"
 +            /><span><img class="oe_input_icon oe_datepicker_trigger datepickerbutton" draggable="false"
 +                   t-att-src='_s + "/web/static/src/img/ui/field_calendar.png"'
 +                   title="Select date" width="16" height="16" border="0"/></span>
 +        </div>
      </span>
  </t>
  <t t-name="FieldDate">
  </t>
  <t t-name="FieldBinaryImage">
      <span class="oe_form_field oe_form_field_image" t-att-style="widget.node.attrs.style">
-         <div class="oe_form_field_image_controls oe_edit_only">
-             <i class="fa fa-pencil fa-1g pull-left col-md-offset-1 oe_form_binary_file_edit" title="Edit"/>
-             <i class="fa fa-trash-o fa-1g col-md-offset-5 oe_form_binary_file_clear" title="Clear"/>
-             <div class="oe_form_binary_progress" style="display: none">
-                 <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
-                 <b>Uploading ...</b>
+         <t t-if="!widget.get('effective_readonly')">
+             <div class="oe_form_field_image_controls oe_edit_only">
+               <i class="fa fa-pencil fa-1g pull-left col-md-offset-1 oe_form_binary_file_edit" title="Edit"/>
+               <i class="fa fa-trash-o fa-1g col-md-offset-5 oe_form_binary_file_clear" title="Clear"/>
+                 <div class="oe_form_binary_progress" style="display: none">
+                     <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
+                     <b>Uploading ...</b>
+                 </div>
              </div>
-         </div>
-         <t t-call="HiddenInputFile">
+           <t t-call="HiddenInputFile">
              <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
+           </t>
          </t>
      </span>
  </t>
              <button class="oe_button" id="add_field">Add</button>
              <button class="oe_button" id="remove_field">Remove</button>
              <button class="oe_button" id="remove_all_field">Remove All</button>
 +            <button class="oe_button" id="move_up">Move Up</button>
 +            <button class="oe_button" id="move_down">Move Down</button>
          </td>
          <td class="oe_export_fields_selector_right">
              <select name="fields_list" id="fields_list"
@@@ -296,7 -296,6 +296,7 @@@ instance.web_kanban.KanbanView = instan
                  if(!self.nb_records) {
                      self.no_result();
                  }
 +                self.trigger('kanban_groups_processed');
              });
          });
      },
              self.do_clear_groups();
              self.dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).done(function(records) {
                  var kgroup = new instance.web_kanban.KanbanGroup(self, records, null, self.dataset);
+                 if (!_.isEmpty(self.dataset.ids) && (self.dataset.index === null || self.dataset.index >= self.dataset.ids.length)) {
+                     self.dataset.index = 0;
+                 } else if (_.isEmpty(self.dataset.ids)){
+                     self.dataset.index = null;
+                 }
                  self.do_add_groups([kgroup]).done(function() {
                      if (_.isEmpty(records)) {
                          self.no_result();
                      }
 +                    self.trigger('kanban_dataset_processed');
                      def.resolve();
                  });
              }).done(null, function() {
                      stop: function(event, ui) {
                          var stop_index = ui.item.index();
                          if (start_index !== stop_index) {
-                             var $start_column = $('.oe_kanban_groups_records .oe_kanban_column').eq(start_index);
-                             var $stop_column = $('.oe_kanban_groups_records .oe_kanban_column').eq(stop_index);
+                             var $start_column = self.$('.oe_kanban_groups_records .oe_kanban_column').eq(start_index);
+                             var $stop_column = self.$('.oe_kanban_groups_records .oe_kanban_column').eq(stop_index);
                              var method = (start_index > stop_index) ? 'insertBefore' : 'insertAfter';
                              $start_column[method]($stop_column);
                              var tmp_group = self.groups.splice(start_index, 1)[0];
@@@ -1154,7 -1157,7 +1159,7 @@@ instance.web_kanban.KanbanRecord = inst
   */
  instance.web_kanban.QuickCreate = instance.web.Widget.extend({
      template: 'KanbanView.quick_create',
-     
      /**
       * close_btn: If true, the widget will display a "Close" button able to trigger
       * a "close" event.
@@@ -1297,7 -1300,7 +1302,7 @@@ instance.web_kanban.Priority = instance
          this.record_id = self.parent.id;
          this.priorities = self.prepare_priority();
          this.$el = $(QWeb.render("Priority", {'widget': this}));
-         this.$el.find('.oe_legend').click(self.do_action.bind(self));
+         this.$el.find('li').click(self.do_action.bind(self));
      },
      do_action: function(e) {
          var self = this;
@@@ -1339,7 -1342,7 +1344,7 @@@ instance.web_kanban.KanbanSelection = i
          this.record_id = self.parent.id;
          this.states = self.prepare_dropdown_selection();;
          this.$el = $(QWeb.render("KanbanSelection", {'widget': self}));
-         this.$el.find('.oe_legend').click(self.do_action.bind(self));
+         this.$el.find('li').click(self.do_action.bind(self));
      },
      do_action: function(e) {
          var self = this;
@@@ -3,12 -3,15 +3,15 @@@ import cStringI
  import datetime
  from itertools import islice
  import json
+ import xml.etree.ElementTree as ET
  import logging
  import re
  
  from sys import maxint
  
  import werkzeug.utils
+ import urllib2
  import werkzeug.wrappers
  from PIL import Image
  
@@@ -53,8 -56,10 +56,10 @@@ class Website(openerp.addons.web.contro
          values = {
              'path': page,
          }
-         # allow shortcut for /page/<website_xml_id>
-         if '.' not in page:
+         # /page/website.XXX --> /page/XXX
+         if page.startswith('website.'):
+             return request.redirect('/page/' + page[8:], code=301)
+         elif '.' not in page:
              page = 'website.%s' % page
  
          try:
  
          return request.make_response(content, [('Content-Type', mimetype)])
  
+     @http.route('/website/info', type='http', auth="public", website=True)
+     def website_info(self):
+         try:
+             request.website.get_template('website.info').name
+         except Exception, e:
+             return request.registry['ir.http']._handle_exception(e, 404)
+         irm = request.env()['ir.module.module'].sudo()
+         apps = irm.search([('state','=','installed'),('application','=',True)])
+         modules = irm.search([('state','=','installed'),('application','=',False)])
+         values = {
+             'apps': apps,
+             'modules': modules,
+             'version': openerp.service.common.exp_version()
+         }
+         return request.render('website.info', values)
      #------------------------------------------------------
      # Edit
      #------------------------------------------------------
              return werkzeug.wrappers.Response(url, mimetype='text/plain')
          return werkzeug.utils.redirect(url)
  
 -    @http.route('/website/theme_change', type='http', auth="user", website=True)
 -    def theme_change(self, theme_id=False, **kwargs):
 -        imd = request.registry['ir.model.data']
 -        Views = request.registry['ir.ui.view']
 -
 -        _, theme_template_id = imd.get_object_reference(
 -            request.cr, request.uid, 'website', 'theme')
 -        views = Views.search(request.cr, request.uid, [
 -            ('inherit_id', '=', theme_template_id),
 -        ], context=request.context)
 -        Views.write(request.cr, request.uid, views, {
 -            'active': False,
 -        }, context=dict(request.context or {}, active_test=True))
 -
 -        if theme_id:
 -            module, xml_id = theme_id.split('.')
 -            _, view_id = imd.get_object_reference(
 -                request.cr, request.uid, module, xml_id)
 -            Views.write(request.cr, request.uid, [view_id], {
 -                'active': True
 -            }, context=dict(request.context or {}, active_test=True))
 -
 -        return request.render('website.themes', {'theme_changed': True})
 -
      @http.route(['/website/snippets'], type='json', auth="public", website=True)
      def snippets(self):
          return request.website._render('website.snippets')
          return request.redirect(redirect)
  
      @http.route('/website/customize_template_get', type='json', auth='user', website=True)
 -    def customize_template_get(self, xml_id, full=False):
 +    def customize_template_get(self, xml_id, full=False, bundles=False):
          """ Lists the templates customizing ``xml_id``. By default, only
          returns optional templates (which can be toggled on and off), if
          ``full=True`` returns all templates customizing ``xml_id``
 +        ``bundles=True`` returns also the asset bundles
          """
          imd = request.registry['ir.model.data']
          view_model, view_theme_id = imd.get_object_reference(
          user_groups = set(user.groups_id)
  
          views = request.registry["ir.ui.view"]\
-             ._views_get(request.cr, request.uid, xml_id, bundles=bundles, context=request.context)
 -            ._views_get(request.cr, request.uid, xml_id, context=dict(request.context or {}, active_test=False))
++            ._views_get(request.cr, request.uid, xml_id, bundles=bundles, context=dict(request.context or {}, active_test=False))
          done = set()
          result = []
          for v in views:
              if not user_groups.issuperset(v.groups_id):
                  continue
-             if full or (v.application != 'always' and v.inherit_id.id != view_theme_id):
+             if full or (v.customize_show and v.inherit_id.id != view_theme_id):
                  if v.inherit_id not in done:
                      result.append({
                          'name': v.inherit_id.name,
                      'xml_id': v.xml_id,
                      'inherit_id': v.inherit_id.id,
                      'header': False,
-                     'active': v.application in ('always', 'enabled'),
+                     'active': v.active,
                  })
          return result
  
  
      @http.route('/website/attach', type='http', auth='user', methods=['POST'], website=True)
      def attach(self, func, upload=None, url=None):
 -        Attachments = request.registry['ir.attachment']
 -
 -        website_url = message = None
 -        if not upload:
 -            website_url = url
 -            name = url.split("/").pop()
 +        # the upload argument doesn't allow us to access the files if more than
 +        # one file is uploaded, as upload references the first file
 +        # therefore we have to recover the files from the request object
 +        Attachments = request.registry['ir.attachment']  # registry for the attachment table
 +
 +        uploads = []
 +        message = None
 +        if not upload: # no image provided, storing the link and the image name
 +            uploads.append({'website_url': url})
 +            name = url.split("/").pop()                       # recover filename
              attachment_id = Attachments.create(request.cr, request.uid, {
                  'name':name,
                  'type': 'url',
                  'url': url,
                  'res_model': 'ir.ui.view',
              }, request.context)
 -        else:
 +        else:                                                  # images provided
              try:
 -                image_data = upload.read()
 -                image = Image.open(cStringIO.StringIO(image_data))
 -                w, h = image.size
 -                if w*h > 42e6: # Nokia Lumia 1020 photo resolution
 -                    raise ValueError(
 -                        u"Image size excessive, uploaded images must be smaller "
 -                        u"than 42 million pixel")
 -
 -                attachment_id = Attachments.create(request.cr, request.uid, {
 -                    'name': upload.filename,
 -                    'datas': image_data.encode('base64'),
 -                    'datas_fname': upload.filename,
 -                    'res_model': 'ir.ui.view',
 -                }, request.context)
 -
 -                [attachment] = Attachments.read(
 -                    request.cr, request.uid, [attachment_id], ['website_url'],
 -                    context=request.context)
 -                website_url = attachment['website_url']
 +                for c_file in request.httprequest.files.getlist('upload'):
 +                    image_data = c_file.read()
 +                    image = Image.open(cStringIO.StringIO(image_data))
 +                    w, h = image.size
 +                    if w*h > 42e6: # Nokia Lumia 1020 photo resolution
 +                        raise ValueError(
 +                            u"Image size excessive, uploaded images must be smaller "
 +                            u"than 42 million pixel")
 +    
 +                    attachment_id = Attachments.create(request.cr, request.uid, {
 +                        'name': c_file.filename,
 +                        'datas': image_data.encode('base64'),
 +                        'datas_fname': c_file.filename,
 +                        'res_model': 'ir.ui.view',
 +                    }, request.context)
 +    
 +                    [attachment] = Attachments.read(
 +                        request.cr, request.uid, [attachment_id], ['website_url'],
 +                        context=request.context)
 +                    uploads.append(attachment)
              except Exception, e:
                  logger.exception("Failed to upload image to attachment")
                  message = unicode(e)
  
          return """<script type='text/javascript'>
              window.parent['%s'](%s, %s);
 -        </script>""" % (func, json.dumps(website_url), json.dumps(message))
 +        </script>""" % (func, json.dumps(uploads), json.dumps(message))
  
      @http.route(['/website/publish'], type='json', auth="public", website=True)
      def publish(self, id, object):
          obj = _object.browse(request.cr, request.uid, _id)
          return bool(obj.website_published)
  
+     @http.route(['/website/seo_suggest/<keywords>'], type='http', auth="public", website=True)
+     def seo_suggest(self, keywords):
+         url = "http://google.com/complete/search"
+         try:
+             req = urllib2.Request("%s?%s" % (url, werkzeug.url_encode({
+                 'ie': 'utf8', 'oe': 'utf8', 'output': 'toolbar', 'q': keywords})))
+             request = urllib2.urlopen(req)
+         except (urllib2.HTTPError, urllib2.URLError):
+             return []
+         xmlroot = ET.fromstring(request.read())
+         return json.dumps([sugg[0].attrib['data'] for sugg in xmlroot if len(sugg) and sugg[0].attrib['data']])
      #------------------------------------------------------
 +    # Themes
 +    #------------------------------------------------------
 +
 +    def get_view_ids(self, xml_ids):
 +        ids = []
 +        imd = request.registry['ir.model.data']
 +        for xml_id in xml_ids:
 +            if "." in xml_id:
 +                xml = xml_id.split(".")
 +                view_model, id = imd.get_object_reference(request.cr, request.uid, xml[0], xml[1])
 +            else:
 +                id = int(xml_id)
 +            ids.append(id)
 +        return ids
 +
 +    @http.route(['/website/theme_customize_get'], type='json', auth="public", website=True)
 +    def theme_customize_get(self, xml_ids):
 +        view = request.registry["ir.ui.view"]
 +        enable = []
 +        disable = []
 +        ids = self.get_view_ids(xml_ids)
-         for v in view.browse(request.cr, request.uid, ids, context=request.context):
-             if v.application != "disabled":
++        context = dict(request.context or {}, active_test=True)
++        for v in view.browse(request.cr, request.uid, ids, context=context):
++            if v.active:
 +                enable.append(v.xml_id)
 +            else:
 +                disable.append(v.xml_id)
 +        return [enable, disable]
 +
 +    @http.route(['/website/theme_customize'], type='json', auth="public", website=True)
 +    def theme_customize(self, enable, disable):
 +        """ enable or Disable lists of ``xml_id`` of the inherit templates
 +        """
 +        cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
 +        view = pool["ir.ui.view"]
 +
-         def set_application(ids, application):
-             write_ids = []
-             for v in view.browse(cr, uid, self.get_view_ids(ids), context=context):
-                 if v.application == 'always':
-                     continue
-                 if v.application != application:
-                     write_ids.append(v.id)
-             if write_ids:
-                 view.write(cr, uid, write_ids, {'application': application})
++        def set_active(ids, active):
++            if ids:
++                view.write(cr, uid, ids, {'active': active}, context=dict(request.context or {}, active_test=True))
 +
-         set_application(disable, 'disabled')
-         set_application(enable, 'enabled')
++        set_active(disable, False)
++        set_active(enable, True)
 +
 +        return True
 +
 +    @http.route(['/website/theme_customize_reload'], type='http', auth="public", website=True)
 +    def theme_customize_reload(self, href, enable, disable):
 +        self.theme_customize(enable and enable.split(",") or [],disable and disable.split(",") or [])
 +        return request.redirect(href + ("&theme=true" if "#" in href else "#theme=true"))
 +
 +    #------------------------------------------------------
      # Helpers
      #------------------------------------------------------
      @http.route(['/website/kanban'], type='http', auth="public", methods=['POST'], website=True)
  
      @http.route([
          '/website/image',
 +        '/website/image/<xmlid>',
 +        '/website/image/<xmlid>/<field>',
-         '/website/image/<model>/<id>/<field>'
+         '/website/image/<model>/<id>/<field>',
+         '/website/image/<model>/<id>/<field>/<int:max_width>x<int:max_height>'
          ], auth="public", website=True)
 -    def website_image(self, model, id, field, max_width=None, max_height=None):
 +    def website_image(self, model=None, id=None, field=None, xmlid=None, max_width=None, max_height=None):
          """ Fetches the requested field and ensures it does not go above
          (max_width, max_height), resizing it if necessary.
  
  
          The requested field is assumed to be base64-encoded image data in
          all cases.
 +
 +        xmlid can be used to load the image. But the field image must by base64-encoded
          """
 +        if xmlid and "." in xmlid:
 +            xmlid = xmlid.split(".", 1)
 +            try:
 +                model, id = request.registry['ir.model.data'].get_object_reference(request.cr, request.uid, xmlid[0], xmlid[1])
 +            except:
 +                raise werkzeug.exceptions.NotFound()
 +            if model == 'ir.attachment':
 +                field = "datas"
 +
 +        if not model or not id or not field:
 +            raise werkzeug.exceptions.NotFound()
 +
-         response = werkzeug.wrappers.Response()
-         return request.registry['website']._image(
-                     request.cr, request.uid, model, id, field, response, max_width, max_height)
+         try:
+             response = werkzeug.wrappers.Response()
+             return request.registry['website']._image(
+                 request.cr, request.uid, model, id, field, response, max_width, max_height)
+         except Exception:
+             logger.exception("Cannot render image field %r of record %s[%s] at size(%s,%s)",
+                              field, model, id, max_width, max_height)
+             response = werkzeug.wrappers.Response()
+             return self.placeholder(response)
  
      #------------------------------------------------------
      # Server actions
@@@ -82,23 -82,39 +82,41 @@@ class ir_http(orm.AbstractModel)
                  self._authenticate(func.routing['auth'])
              else:
                  self._auth_method_public()
-             request.redirect = lambda url: werkzeug.utils.redirect(url_for(url))
+             request.redirect = lambda url, code=302: werkzeug.utils.redirect(url_for(url), code)
              request.website = request.registry['website'].get_current_website(request.cr, request.uid, context=request.context)
+             langs = [lg[0] for lg in request.website.get_languages()]
+             path = request.httprequest.path.split('/')
              if first_pass:
-                 request.lang = request.website.default_lang_code
+                 if request.website_multilang:
+                     # If the url doesn't contains the lang and that it's the first connection, we to retreive the user preference if it exists.
+                     if not path[1] in langs and not request.httprequest.cookies.get('session_id'):
+                         if request.lang not in langs:
+                             # Try to find a similar lang. Eg: fr_BE and fr_FR
+                             short = request.lang.split('_')[0]
+                             langs_withshort = [lg[0] for lg in request.website.get_languages() if lg[0].startswith(short)]
+                             if len(langs_withshort):
+                                 request.lang = langs_withshort[0]
+                             else:
+                                 request.lang = request.website.default_lang_code
+                         # We redirect with the right language in url
+                         if request.lang != request.website.default_lang_code:
+                             path.insert(1, request.lang)
+                             path = '/'.join(path) or '/'
+                             return request.redirect(path + '?' + request.httprequest.query_string)
+                     else:
+                         request.lang = request.website.default_lang_code
              request.context['lang'] = request.lang
 +            if not request.context.get('tz'):
 +                request.context['tz'] = request.session['geoip'].get('time_zone')
              if not func:
-                 path = request.httprequest.path.split('/')
-                 langs = [lg[0] for lg in request.website.get_languages()]
                  if path[1] in langs:
                      request.lang = request.context['lang'] = path.pop(1)
                      path = '/'.join(path) or '/'
                      if request.lang == request.website.default_lang_code:
                          # If language is in the url and it is the default language, redirect
                          # to url without language so google doesn't see duplicate content
-                         return request.redirect(path + '?' + request.httprequest.query_string)
+                         return request.redirect(path + '?' + request.httprequest.query_string, code=301)
                      return self.reroute(path)
          return super(ir_http, self)._dispatch()
  
                      path = '/' + request.lang + path
                  if request.httprequest.query_string:
                      path += '?' + request.httprequest.query_string
-                 return werkzeug.utils.redirect(path)
+                 return werkzeug.utils.redirect(path, code=301)
  
      def _serve_attachment(self):
          domain = [('type', '=', 'binary'), ('url', '=', request.httprequest.path)]
                  exception=exception,
                  traceback=traceback.format_exc(exception),
              )
 -            code = getattr(exception, 'code', code)
 +
 +            if isinstance(exception, werkzeug.exceptions.HTTPException):
 +                if exception.code is None:
 +                    # Hand-crafted HTTPException likely coming from abort(),
 +                    # usually for a redirect response -> return it directly
 +                    return exception
 +                else:
 +                    code = exception.code
  
              if isinstance(exception, openerp.exceptions.AccessError):
                  code = 403
                  if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError):
                      code = 403
  
 -            if isinstance(exception, werkzeug.exceptions.HTTPException) and code is None:
 -                # Hand-crafted HTTPException likely coming from abort(),
 -                # usually for a redirect response -> return it directly
 -                return exception
 -
              if code == 500:
                  logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
                  if 'qweb_exception' in values:
@@@ -1,8 -1,5 +1,5 @@@
  # -*- coding: utf-8 -*-
  import copy
- import re
- import simplejson
- import werkzeug
  
  from lxml import etree, html
  
@@@ -18,9 -15,11 +15,11 @@@ class view(osv.osv)
          'website_meta_title': fields.char("Website meta title", size=70, translate=True),
          'website_meta_description': fields.text("Website meta description", size=160, translate=True),
          'website_meta_keywords': fields.char("Website meta keywords", translate=True),
+         'customize_show': fields.boolean("Show As Optional Inherit"),
      }
      _defaults = {
          'page': False,
+         'customize_show': False,
      }
  
  
@@@ -37,7 -36,7 +36,7 @@@
  
      # Returns all views (called and inherited) related to a view
      # Used by translation mechanism, SEO and optional templates
 -    def _views_get(self, cr, uid, view_id, options=True, context=None, root=True):
 +    def _views_get(self, cr, uid, view_id, options=True, bundles=False, context=None, root=True):
          """ For a given view ``view_id``, should return:
  
          * the view itself
          result = [view]
  
          node = etree.fromstring(view.arch)
 -        for child in node.xpath("//t[@t-call]"):
 +        xpath = "//t[@t-call]"
 +        if bundles:
 +            xpath += "| //t[@t-call-assets]"
 +        for child in node.xpath(xpath):
              try:
 -                called_view = self._view_obj(cr, uid, child.get('t-call'), context=context)
 +                called_view = self._view_obj(cr, uid, child.get('t-call', child.get('t-call-assets')), context=context)
              except ValueError:
                  continue
              if called_view not in result:
 -                result += self._views_get(cr, uid, called_view, options=options, context=context)
 +                result += self._views_get(cr, uid, called_view, options=options, bundles=bundles, context=context)
  
          extensions = view.inherit_children_ids
          if not options:
              # only active children
-             extensions = (v for v in view.inherit_children_ids
-                           if v.application in ('always', 'enabled'))
+             extensions = (v for v in view.inherit_children_ids if v.active)
  
          # Keep options in a deterministic order regardless of their applicability
          for extension in sorted(extensions, key=lambda v: v.id):
              for r in self._views_get(
                      cr, uid, extension,
                      # only return optional grandchildren if this child is enabled
-                     options=extension.application in ('always', 'enabled'),
+                     options=extension.active,
                      context=context, root=False):
                  if r not in result:
                      result.append(r)
              if not context:
                  context = {}
  
+             company = self.pool['res.company'].browse(cr, SUPERUSER_ID, request.website.company_id.id, context=context)
              qcontext = dict(
                  context.copy(),
                  website=request.website,
                  url_for=website.url_for,
                  slug=website.slug,
-                 res_company=request.website.company_id,
+                 res_company=company,
                  user_id=self.pool.get("res.users").browse(cr, uid, uid),
                  translatable=context.get('lang') != request.website.default_lang_code,
                  editable=request.website.is_publisher(),
          view = self.browse(cr, SUPERUSER_ID, res_id, context=context)
          if view.model_data_id:
              view.model_data_id.write({'noupdate': True})
@@@ -238,6 -238,10 +238,6 @@@ footer 
    padding-left: 16px;
  }
  
 -#themes-list .well {
 -  padding: 0 0 20px 0;
 -}
 -
  /* -- Hack for removing double scrollbar from mobile preview -- */
  div#mobile-preview.modal {
    overflow: hidden;
@@@ -356,114 -360,6 +356,114 @@@ ul.nav-stacked > li > a 
    padding: 4px 0;
  }
  
 +/* gallery */
 +.o_gallery.o_grid.o_spc-none div.row, .o_gallery.o_masonry.o_spc-none div.row {
 +  margin: 0;
 +}
 +.o_gallery.o_grid.o_spc-none div.row > *, .o_gallery.o_masonry.o_spc-none div.row > * {
 +  padding: 0;
 +}
 +.o_gallery.o_grid.o_spc-small div.row, .o_gallery.o_masonry.o_spc-small div.row {
 +  margin: 5px 0;
 +}
 +.o_gallery.o_grid.o_spc-small div.row > *, .o_gallery.o_masonry.o_spc-small div.row > * {
 +  padding: 0 5px;
 +}
 +.o_gallery.o_grid.o_spc-medium div.row, .o_gallery.o_masonry.o_spc-medium div.row {
 +  margin: 10px 0;
 +}
 +.o_gallery.o_grid.o_spc-medium div.row > *, .o_gallery.o_masonry.o_spc-medium div.row > * {
 +  padding: 0 10px;
 +}
 +.o_gallery.o_grid.o_spc-big div.row, .o_gallery.o_masonry.o_spc-big div.row {
 +  margin: 15px 0;
 +}
 +.o_gallery.o_grid.o_spc-big div.row > *, .o_gallery.o_masonry.o_spc-big div.row > * {
 +  padding: 0 15px;
 +}
 +.o_gallery.o_grid .img, .o_gallery.o_masonry .img {
 +  width: 100%;
 +}
 +.o_gallery.o_grid.size-auto .row {
 +  height: auto;
 +}
 +.o_gallery.o_grid.size-small .row {
 +  height: 100px;
 +}
 +.o_gallery.o_grid.size-medium .row {
 +  height: 250px;
 +}
 +.o_gallery.o_grid.size-big .row {
 +  height: 400px;
 +}
 +.o_gallery.o_grid.size-small .img, .o_gallery.o_grid.size-medium .img, .o_gallery.o_grid.size-big .img {
 +  height: 100%;
 +}
 +.o_gallery.o_nomode.o_spc-none .img {
 +  padding: 0;
 +}
 +.o_gallery.o_nomode.o_spc-small .img {
 +  padding: 5px;
 +}
 +.o_gallery.o_nomode.o_spc-medium .img {
 +  padding: 10px;
 +}
 +.o_gallery.o_nomode.o_spc-big .img {
 +  padding: 15px;
 +}
 +.o_gallery.o_slideshow .carousel ul.carousel-indicators li {
 +  border: 1px solid #aaaaaa;
 +}
 +.o_gallery .carousel-inner .item img {
 +  max-width: none;
 +}
 +
 +.o_gallery.o_slideshow > .container {
 +  height: 100%;
 +}
 +
 +.o_gallery.o_slideshow .carousel, .modal-body.o_slideshow .carousel {
 +  height: 100%;
 +}
 +.o_gallery.o_slideshow .carousel img, .modal-body.o_slideshow .carousel img {
 +  max-height: 100%;
 +  max-width: 100%;
 +  margin: auto;
 +  position: relative;
 +  top: 50%;
 +  -webkit-transform: translateY(-50%);
 +  -ms-transform: translateY(-50%);
 +  transform: translateY(-50%);
 +}
 +.o_gallery.o_slideshow .carousel ul.carousel-indicators, .modal-body.o_slideshow .carousel ul.carousel-indicators {
 +  display: block;
 +  height: auto;
 +  padding: 0;
 +  border-width: 0;
 +  position: absolute;
 +  bottom: 0;
 +}
 +.o_gallery.o_slideshow .carousel ul.carousel-indicators li, .modal-body.o_slideshow .carousel ul.carousel-indicators li {
 +  list-style-image: none;
 +  display: inline-block;
 +  width: 35px;
 +  height: 35px;
 +  margin: 0 0px 5px 5px;
 +  padding: 0;
 +  border: 1px solid #aaaaaa;
 +  text-indent: initial;
 +  background-size: cover;
 +  opacity: 0.5;
 +  background-color: black;
 +}
 +.o_gallery.o_slideshow .carousel ul.carousel-indicators li.active, .modal-body.o_slideshow .carousel ul.carousel-indicators li.active {
 +  opacity: 1;
 +}
 +.o_gallery.o_slideshow .carousel .carousel-control.left, .o_gallery.o_slideshow .carousel .carousel-control.right, .modal-body.o_slideshow .carousel .carousel-control.left, .modal-body.o_slideshow .carousel .carousel-control.right {
 +  background-image: none;
 +  background-color: transparent;
 +}
 +
  /* Parallax Theme */
  div.carousel .carousel-indicators li {
    border: 1px solid grey;
@@@ -497,6 -393,62 +497,6 @@@ div.carousel div.carousel-content 
    padding: 32px 0;
  }
  
 -/* Background */
 -.oe_dark {
 -  background: #eff8f8;
 -  background: rgba(200, 200, 200, 0.14);
 -}
 -
 -.oe_black {
 -  background-color: rgba(0, 0, 0, 0.9);
 -  color: white;
 -}
 -
 -.oe_green {
 -  background-color: #169c78;
 -  color: white;
 -}
 -.oe_green .text-muted {
 -  color: #dddddd;
 -}
 -
 -.oe_blue_light {
 -  background-color: #41b6ab;
 -  color: white;
 -}
 -.oe_blue_light .text-muted {
 -  color: #dddddd;
 -}
 -
 -.oe_blue {
 -  background-color: #34495e;
 -  color: white;
 -}
 -
 -.oe_orange {
 -  background-color: #f05442;
 -  color: white;
 -}
 -.oe_orange .text-muted {
 -  color: #dddddd;
 -}
 -
 -.oe_purple {
 -  background-color: #b163a3;
 -  color: white;
 -}
 -.oe_purple .text-muted {
 -  color: #dddddd;
 -}
 -
 -.oe_red {
 -  background-color: #9c1b31;
 -  color: white;
 -}
 -.oe_red .text-muted {
 -  color: #dddddd;
 -}
 -
  /* Misc */
  .texttop {
    vertical-align: top;
@@@ -570,12 -522,15 +570,15 @@@ div.media_iframe_video 
  div.media_iframe_video iframe {
    width: 100%;
    height: 100%;
+   position: absolute;
+   margin-left: -50%;
  }
- div.media_iframe_video div {
+ div.media_iframe_video .css_editable_mode_display {
    position: absolute;
    width: 100%;
    height: 100%;
    display: none;
+   z-index: 2;
  }
  
  /* Mobile view */
@@@ -99,7 -99,7 +99,7 @@@ heade
          margin-right: 0 !important
  
  .css_non_editable_mode_hidden
-     display: none
+     display: none !important
  
  /* ----- BOOTSTRAP FIX ----- */
  
@@@ -192,6 -192,9 +192,6 @@@ foote
  .nav-hierarchy
      padding-left: 16px
  
 -#themes-list .well
 -    padding: 0 0 20px 0
 -
  /* -- Hack for removing double scrollbar from mobile preview -- */
  div#mobile-preview.modal
      overflow: hidden
@@@ -285,102 -288,6 +285,102 @@@ ul.nav-stacked > li > 
  .hr
      padding: 4px 0
  
 +/* gallery */
 +
 +.o_gallery
 +    &.o_grid, &.o_masonry
 +        &.o_spc-none
 +            div.row
 +                margin: 0
 +            div.row > *
 +                padding: 0
 +        &.o_spc-small
 +            div.row
 +                margin: 5px 0
 +            div.row > *
 +                padding: 0 5px
 +        &.o_spc-medium
 +            div.row
 +                margin: 10px 0
 +            div.row > *
 +                padding: 0 10px
 +        &.o_spc-big
 +            div.row
 +                margin: 15px 0
 +            div.row > *
 +                padding: 0 15px
 +        .img
 +            width: 100%
 +    &.o_grid
 +        &.size-auto .row
 +            height: auto
 +        &.size-small .row
 +            height: 100px
 +        &.size-medium .row
 +            height: 250px
 +        &.size-big .row
 +            height: 400px
 +        &.size-small, &.size-medium, &.size-big
 +            .img
 +                height: 100%
 +    &.o_nomode
 +        &.o_spc-none
 +            .img
 +                padding: 0
 +        &.o_spc-small
 +            .img
 +                padding: 5px
 +        &.o_spc-medium
 +            .img
 +                padding: 10px
 +        &.o_spc-big
 +            .img
 +                padding: 15px
 +
 +    &.o_slideshow .carousel ul.carousel-indicators li
 +        border: 1px solid #aaa
 +    .carousel-inner .item img
 +        max-width: none
 +
 +.o_gallery.o_slideshow > .container
 +    height: 100%
 +
 +.o_gallery.o_slideshow .carousel, .modal-body.o_slideshow .carousel
 +    height: 100%
 +    img
 +        max-height: 100%
 +        max-width: 100%
 +        margin: auto
 +        position: relative
 +        top: 50%
 +        -webkit-transform: translateY(-50%)
 +        -ms-transform: translateY(-50%)
 +        transform: translateY(-50%)
 +    ul.carousel-indicators
 +        display: block
 +        height: auto
 +        padding: 0
 +        border-width: 0
 +        position: absolute
 +        bottom: 0
 +        li
 +            list-style-image: none
 +            display: inline-block
 +            width: 35px
 +            height: 35px
 +            margin: 0 0px 5px 5px
 +            padding: 0
 +            border: 1px solid #aaa
 +            text-indent: initial
 +            background-size: cover
 +            opacity: 0.5
 +            background-color: #000
 +        li.active
 +            opacity: 1
 +    .carousel-control.left, .carousel-control.right
 +        background-image: none
 +        background-color: transparent
 +
  /* Parallax Theme */
  
  div.carousel
              vertical-align: middle
              padding: 32px 0
  
 -/* Background */
 -
 -.oe_dark
 -    background: #eff8f8
 -    background: rgba(200, 200, 200, 0.14)
 -
 -.oe_black
 -    background-color: rgba(0, 0, 0, 0.9)
 -    color: white
 -
 -.oe_green
 -    background-color: #169C78
 -    color: white
 -    .text-muted
 -        color: #ddd
 -
 -.oe_blue_light
 -    background-color: #41b6ab
 -    color: white
 -    .text-muted
 -        color: #ddd
 -
 -.oe_blue
 -    background-color: #34495e
 -    color: white
 -
 -.oe_orange
 -    background-color: #f05442
 -    color: white
 -    .text-muted
 -        color: #ddd
 -
 -.oe_purple
 -    background-color: #b163a3
 -    color: white
 -    .text-muted
 -        color: #ddd
 -
 -.oe_red
 -    background-color: #9C1b31
 -    color: white
 -    .text-muted
 -        color: #ddd
 -
  /* Misc */
  
  .texttop
@@@ -473,11 -424,14 +473,14 @@@ div.media_iframe_vide
      iframe
          width: 100%
          height: 100%
-     div
+         position: absolute
+         margin-left: -50%
+     .css_editable_mode_display
          position: absolute
          width: 100%
          height: 100%
          display: none
+         z-index: 2
  
  /* Mobile view */
  
              }
          });
  
 +        CKEDITOR.plugins.add('customColor', {
 +            requires: 'panelbutton,floatpanel',
 +            init: function (editor) {
 +                function create_button (buttonID, label) {
 +                    var btnID = buttonID;
 +                    editor.ui.add(buttonID, CKEDITOR.UI_PANELBUTTON, {
 +                        label: label,
 +                        title: label,
 +                        modes: { wysiwyg: true },
 +                        editorFocus: true,
 +                        context: 'font',
 +                        panel: {
 +                            css: [  '/web/css/web.assets_common/' + (new Date().getTime()),
 +                                    '/web/css/website.assets_frontend/' + (new Date().getTime()),
 +                                    '/web/css/website.assets_editor/' + (new Date().getTime())],
 +                            attributes: { 'role': 'listbox', 'aria-label': label },
 +                        },
 +                        enable: function () {
 +                            this.setState(CKEDITOR.TRISTATE_OFF);
 +                        },
 +                        disable: function () {
 +                            this.setState(CKEDITOR.TRISTATE_DISABLED);
 +                        },
 +                        onBlock: function (panel, block) {
 +                            var self = this;
 +                            var html = openerp.qweb.render('website.colorpicker');
 +                            block.autoSize = true;
 +                            block.element.setHtml( html );
 +                            $(block.element.$).on('click', 'button', function () {
 +                                self.clicked(this);
 +                            });
 +                            if (btnID === "TextColor") {
 +                                $(".only-text", block.element.$).css("display", "block");
 +                                $(".only-bg", block.element.$).css("display", "none");
 +                            }
 +                            var $body = $(block.element.$).parents("body");
 +                            setTimeout(function () {
 +                                $body.css('background-color', '#fff');
 +                            }, 0);
 +                        },
 +                        getClasses: function () {
 +                            var self = this;
 +                            var classes = [];
 +                            var id = this._.id;
 +                            var block = this._.panel._.panel._.blocks[id];
 +                            var $root = $(block.element.$);
 +                            $root.find("button").map(function () {
 +                                var color = self.getClass(this);
 +                                if(color) classes.push( color );
 +                            });
 +                            return classes;
 +                        },
 +                        getClass: function (button) {
 +                            var color = btnID === "BGColor" ? $(button).attr("class") : $(button).attr("class").replace(/^bg-/i, 'text-');
 +                            return color.length && color;
 +                        },
 +                        clicked: function (button) {
 +                            var className = this.getClass(button);
 +                            var ancestor = editor.getSelection().getCommonAncestor();
 +
 +                            editor.focus();
 +                            this._.panel.hide();
 +                            editor.fire('saveSnapshot');
 +
 +                            // remove style
 +                            var classes = [];
 +                            var $ancestor = $(ancestor.$);
 +                            var $fonts = $(ancestor.$).find('font');
 +                            if (!ancestor.$.tagName) {
 +                                $ancestor = $ancestor.parent();
 +                            }
 +                            if ($ancestor.is('font')) {
 +                                $fonts = $fonts.add($ancestor[0]);
 +                            }
 +
 +                            $fonts.filter("."+this.getClasses().join(",.")).map(function () {
 +                                var className = $(this).attr("class");
 +                                if (classes.indexOf(className) === -1) {
 +                                    classes.push(className);
 +                                }
 +                            });
 +                            for (var k in classes) {
 +                                editor.removeStyle( new CKEDITOR.style({
 +                                    element: 'font',
 +                                    attributes: { 'class': classes[k] },
 +                                }) );
 +                            }
 +
 +                            // add new style
 +                            if (className) {
 +                                editor.applyStyle( new CKEDITOR.style({
 +                                    element: 'font',
 +                                    attributes: { 'class': className },
 +                                }) );
 +                            }
 +                            editor.fire('saveSnapshot');
 +                        }
 +
 +                    });
 +                }
 +                create_button("BGColor", "Background Color");
 +                create_button("TextColor", "Text Color");
 +            }
 +        });
 +
          CKEDITOR.plugins.add('oeref', {
              requires: 'widget',
  
      website.EditorBarCustomize = openerp.Widget.extend({
          events: {
              'mousedown a.dropdown-toggle': 'load_menu',
 -            'click ul a[data-action!=ace]': 'do_customize',
 +            'click li:not(#html_editor):not(#theme_customize):not(#install_apps) a': 'do_customize',
          },
          start: function() {
              var self = this;
                  fillEmptyBlocks: false,
                  filebrowserImageUploadUrl: "/website/attach",
                  // Support for sharedSpaces in 4.x
 -                extraPlugins: 'sharedspace,customdialogs,tablebutton,oeref',
 +                extraPlugins: 'customColor,sharedspace,customdialogs,tablebutton,oeref',
                  // Place toolbar in controlled location
                  sharedSpaces: { top: 'oe_rte_toolbar' },
                  toolbar: [{
                      {name: "Heading 5", element: 'h5'},
                      {name: "Heading 6", element: 'h6'},
                      {name: "Formatted", element: 'pre'},
 -                    {name: "Address", element: 'address'}
 +                    {name: "Address", element: 'address'},
                  ],
              };
          },
          make_link: function (url, new_window, label, classes) {
          },
          bind_data: function () {
+             var self = this;
              var href = this.element && (this.element.data( 'cke-saved-href')
                                      ||  this.element.getAttribute('href'));
              var new_window = this.element
                          : false;
              var text = this.element ? this.element.getText() : '';
              if (!text.length) {
-                 var selection = this.editor.getSelection();
-                 text = selection.getSelectedText();
+                 if (this.editor) {
+                     text = this.editor.getSelection().getSelectedText();
+                 } else {
+                     text = this.data.name;
+                     href = this.data.url;
+                     new_window = this.data.new_window;
+                 }
              }
  
              this.$('input#link-text').val(text);
                  this.$('input.email-address').val(match[1]).change();
              }
              if (href && !$control) {
-                 this.$('input.url').val(href).change();
-                 this.$('input.window-new').closest("div").show();
+                 this.page_exists(href).then(function (exist) {
+                     if (exist) {
+                         self.$('#link-page').select2('data', {'id': href, 'text': href});
+                     } else {
+                         self.$('input.url').val(href).change();
+                         self.$('input.window-new').closest("div").show();
+                     }
+                 });
              }
              this.preview();
          },
              }
              var callback = _.uniqueId('func_');
              this.$('input[name=func]').val(callback);
 -            window[callback] = function (url, error) {
 +            window[callback] = function (attachments, error) {
                  delete window[callback];
 -                self.file_selected(url, error);
 +                self.file_selected(attachments[0]['website_url'], error);
              };
          },
          file_selection: function () {
@@@ -1,6 -1,135 +1,6 @@@
  (function () {
      'use strict';
  
 -/*  Building block / Snippet Editor
 - 
 -    The building blocks appear in the edit bar website. These prebuilt html block
 -    allowing the designer to easily generate content on a page (drag and drop).
 -    Options allow snippets to add customizations part html code according to their
 -    selector (jQuery) and javascript object.
 -    
 -    How to create content?
 -
 -    Designers can add their own html block in the "snippets" (/website/views/snippets.xml).
 -    The block must be added in one of four menus (structure, content, feature or effect).
 -    Structure:
 -        <div>
 -            <div class="oe_snippet_thumbnail">
 -                <img class="oe_snippet_thumbnail_img" src="...image src..."/>
 -                <span class="oe_snippet_thumbnail_title">...Block Name...</span>
 -            </div>
 -            <div class="oe_snippet_body">
 -                ...
 -                <!-- 
 -                    The block with class 'oe_snippet_body' is inserted in the page.
 -                    This class is removed when the block is dropped.
 -                    The block can be made of any html tag and content. -->
 -            </div>
 -        </div>
 -
 -    How to create options?
 -
 -    Designers can add their own html block in the "snippet_options" (/website/views/snippets.xml).
 -    Structure:
 -
 -        <div data-snippet-option-id='...'           <!-- Required: javascript object id (but javascript
 -                                                        for this option object is not required) -->
 -            data-selector="..."                     <!-- Required: jQuery selector.
 -                                                        Apply options on all The part of html who 
 -                                                        match with this jQuery selector.
 -                                                        E.g.: If the selector is div, all div will be selected
 -                                                        and can be highlighted and assigned an editor.  -->
 -            data-selector-siblings="..."            <!-- Optional: jQuery selector.
 -                                                        The html part can be insert or move beside
 -                                                        the selected html block -->
 -            data-selector-children="..."            <!-- Optional: jQuery selector.
 -                                                        The html part can be insert or move inside
 -                                                        the selected html block -->
 -            data-selector-vertical-children='...'>  <!-- Optional: jQuery selector.
 -                                                        The html part can be insert or move inside
 -                                                        the selected html block. The drop zone is
 -                                                        displayed vertically -->
 -                ...
 -                <li><a href="#">...</a></li>        <!-- Optional: html li list.
 -                                                        List of menu items displayed in customize
 -                                                        menu. If the li tag have 'data-class', the
 -                                                        class is automaticcally added or removed to
 -                                                        the html content when the user select this item. -->
 -                ...
 -                <li class="dropdown-submenu"                <!-- Optional: html li list exemple. !-->
 -                    data-required="true">                   <!-- Optional: if only one item can be selected
 -                                                                and can't be unselect. !-->
 -                    <a tabindex="-1" href="#">...</a>       <!-- bootstrap dropdown button !-->
 -                    <ul class="dropdown-menu">
 -                        <li data-value="text_only"><a>...</a></li>      <!-- by default data-value is apply
 -                                                                            like a class to html block !-->
 -                    </ul>
 -                </li>
 -        </div>
 -
 -        How to create a javascript object for an options?
 -
 -        openerp.website.snippet.options["...option-id..."] = website.snippet.Option.extend({
 -            // start is called when the user click into a block or when the user drop a block 
 -            // into the page (just after the init method).
 -            // start is usually used to bind event.
 -            //
 -            // this.$target: block html inserted inside the page
 -            // this.$el: html li list of this options
 -            // this.$overlay: html editor overlay who content resize bar, customize menu...
 -            start: function () {},
 -
 -
 -            // onFocus is called when the user click inside the block inserted in page
 -            // and when the user drop on block into the page
 -            onFocus : function () {},
 -
 -
 -            // onBlur is called when the user click outside the block inserted in page, if
 -            // the block is focused
 -            onBlur : function () {},
 -
 -
 -            // on_clone is called when the snippet is duplicate
 -            // @variables: $clone is allready inserted is the page
 -            on_clone: function ($clone) {},
 -
 -
 -            // on_remove is called when the snippet is removed (dom is removing after this tigger)
 -            on_remove: function () {},
 -
 -
 -            // drop_and_build_snippet is called just after that a thumbnail is drag and dropped
 -            // into a drop zone. The content is already inserted in the page.
 -            drop_and_build_snippet: function () {},
 -
 -            // select is called when a user select an item in the li list of options
 -            // By default, if the li item have a data-value attribute, the data-vlue it's apply
 -            // like a class to the html block (this.$target)
 -            // @variables: next_previous = {$next, $prev}
 -            //      $next = next item selected or false
 -            //      $prev = previous item selected or false
 -            select: function (event, next_previous) {}
 -
 -            // preview is called when a user is on mouse over or mouse out of an item
 -            // variables: next_previous = {$next, $prev}
 -            //      $next = next item selected or false
 -            //      $prev = previous item selected or false
 -            preview: function (event, next_previous) {}
 -
 -            // clean_for_save
 -            // clean_for_save is called just before to save the vue
 -            // Sometime it's important to remove or add some datas (contentEditable, added 
 -            // classes to a running animation...)
 -            clean_for_save: function () {}
 -        });
 -
 -
 -    // 'snippet-dropped' is triggered on '#oe_snippets' whith $target as attribute when a snippet is dropped
 -    // 'snippet-activated' is triggered on '#oe_snippets' (and on snippet) when a snippet is activated
 -
 -*/
 -
      var dummy = function () {};
  
      var website = openerp.website;
          }
      });
  
 +    $.extend($.expr[':'],{
 +        checkData: function(node,i,m){
 +            var dataName = m[3];
 +            while (node) {
 +                if (node.dataset && node.dataset[dataName]) {
 +                    return true;
 +                } else {
 +                    node = node.parentNode;
 +                }
 +            }
 +            return false;
 +        },
 +        hasData: function(node,i,m){
 +            return !!_.toArray(node.dataset).length;
 +        },
 +    });
 +
      if (!website.snippet) website.snippet = {};
 -    website.snippet.templateOptions = {};
 +    website.snippet.templateOptions = [];
      website.snippet.globalSelector = "";
      website.snippet.selector = [];
      website.snippet.BuildingBlock = openerp.Widget.extend({
                  subtree: true,
              });
          },
 -        dom_filter: function (dom, sibling) {
 -            if (typeof dom === "string") {
 -                var include = "[data-oe-model]";
 -                var sdom = dom.split(',');
 -                dom = "";
 -                _.each(sdom, function (val) {
 -                    val = val.replace(/^\s+|\s+$/g, '');
 -                    dom += include + " " + val + ", ";
 -                    if (!sibling) {
 -                        val = val.split(" ");
 -                        dom += val.shift() + include + val.join(" ") + ", ";
 -                    }
 -                });
 -                dom = dom.replace(/,\s*$/g, '');
 -                return $(dom);
 -            } else {
 -                return (!sibling && $(dom).is("[data-oe-model]")) || $(dom).parents("[data-oe-model]").length ? $(dom) : $("");
 -            }
 -        },
          start: function() {
              var self = this;
  
                  .prependTo(this.parent.$("#website-top-edit ul"))
                  .find("button");
  
 -            this.$button.click(function () {
 -                self.make_active(false);
 -                self.$el.toggleClass("hidden");
 -            });
 -            $("#wrapwrap").click(function () {
 +            this.$button.click(_.bind(this.show_blocks, this));
 +
 +            this.$snippet = $("#oe_snippets");
 +            this.$wrapwrap = $("#wrapwrap");
 +            this.$wrapwrap.click(function () {
                  self.$el.addClass("hidden");
              });
  
              this.fetch_snippet_templates();
 -
              this.bind_snippet_click_editor();
 -
              this.$el.addClass("hidden");
  
              $(document).on('click', '.dropdown-submenu a[tabindex]', function (e) {
              this.getParent().on('change:height', this, function (editor) {
                  self.$el.css('top', editor.get('height'));
              });
 -            self.$el.css('top', this.parent.get('height'));
 +            this.$el.css('top', this.parent.get('height'));
 +        },
 +        show_blocks: function () {
 +            var self = this;
 +            this.make_active(false);
 +            this.$el.toggleClass("hidden");
 +            if (this.$el.hasClass("hidden")) {
 +                return;
 +            }
 +
 +            //this.enable_snippets( this.$snippet.find(".tab-pane.active") );
 +            var categories = this.$snippet.find(".tab-pane.active")
 +                .add(this.$snippet.find(".tab-pane:not(.active)"))
 +                .get().reverse();
 +            function enable() {
 +                self.enable_snippets( $(categories.pop()) );
 +                if (categories.length) {
 +                    setTimeout(enable,10);
 +                }
 +            }
 +            setTimeout(enable,0);
 +        },
 +        enable_snippets: function ($category) {
 +            var self = this;
 +            $category.find(".oe_snippet_body").each(function () {
 +                var $snippet = $(this);
 +
 +                if (!$snippet.data('selectors')) {
 +                    var selectors = [];
 +                    for (var k in website.snippet.templateOptions) {
 +                        var option = website.snippet.templateOptions[k];
 +                        if ($snippet.is(option.base_selector)) {
 +
 +                            var dropzone = [];
 +                            if (option['drop-near']) dropzone.push(option['drop-near']);
 +                            if (option['drop-in']) dropzone.push(option['drop-in']);
 +                            if (option['drop-in-vertical']) dropzone.push(option['drop-in-vertical']);
 +                            selectors = selectors.concat(dropzone);
 +                        }
 +                    }
 +                    $snippet.data('selectors', selectors.length ? selectors.join(":first, ") + ":first" : "");
 +                }
 +
 +                if ($snippet.data('selectors').length && self.$wrapwrap.find($snippet.data('selectors')).size()) {
 +                    $snippet.closest(".oe_snippet").removeClass("disable");
 +                } else {
 +                    $snippet.closest(".oe_snippet").addClass("disable");
 +                }
 +            });
 +            $('#oe_snippets .scroll a[data-toggle="tab"][href="#' + $category.attr("id") + '"]')
 +                .toggle(!!$category.find(".oe_snippet:not(.disable)").size());
          },
          _get_snippet_url: function () {
              return '/website/snippets';
          },
 +        _add_check_selector : function (selector, no_check) {
 +            var data = selector.split(",");
 +            var selectors = [];
 +            for (var k in data) {
 +                selectors.push(data[k].replace(/^\s+|\s+$/g, '') + (no_check ? "" : ":checkData(oeModel)"));
 +            }
 +            return selectors.join(", ");
 +        },
          fetch_snippet_templates: function () {
              var self = this;
  
                      var $html = $(html);
  
                      var selector = [];
 -                    var $styles = $html.find("[data-snippet-option-id]");
 +                    var $styles = $html.find("[data-js], [data-selector]");
                      $styles.each(function () {
                          var $style = $(this);
 -                        var style_id = $style.data('snippet-option-id');
 -                        website.snippet.templateOptions[style_id] = {
 -                            'snippet-option-id' : style_id,
 -                            'selector': $style.data('selector'),
 +                        var no_check = $style.data('no-check');
 +                        var option_id = $style.data('js');
 +                        var option = {
 +                            'option' : option_id,
 +                            'base_selector': $style.data('selector'),
 +                            'selector': self._add_check_selector($style.data('selector'), no_check),
                              '$el': $style,
 -                            'selector-siblings': $style.data('selector-siblings'),
 -                            'selector-children': $style.data('selector-children'),
 -                            'selector-vertical-children': $style.data('selector-vertical-children'),
 +                            'drop-near': $style.data('drop-near') && self._add_check_selector($style.data('drop-near'), no_check),
 +                            'drop-in': $style.data('drop-in') && self._add_check_selector($style.data('drop-in'), no_check),
                              'data': $style.data()
                          };
 -                        selector.push($style.data('selector'));
 +                        website.snippet.templateOptions.push(option);
 +                        selector.push(option.selector);
                      });
                      $styles.addClass("hidden");
                      website.snippet.globalSelector = selector.join(",");
  
 -                    self.$snippets = $html.find(".tab-content > div > div").addClass("oe_snippet");
 -                    self.$el.append($html);
 -
 +                    self.$snippets = $html.find(".tab-content > div > div")
 +                        .addClass("oe_snippet")
 +                        .each(function () {
 +                            if (!$('.oe_snippet_thumbnail', this).size()) {
 +                                var $div = $(
 +                                    '<div class="oe_snippet_thumbnail">'+
 +                                        '<div class="oe_snippet_thumbnail_img"/>'+
 +                                        '<span class="oe_snippet_thumbnail_title"></span>'+
 +                                    '</div>');
 +                                $div.find('span').text($(this).attr("name"));
 +                                $(this).prepend($div);
 +                            }
 +                            $("> *:not(.oe_snippet_thumbnail)", this).addClass('oe_snippet_body');
 +                        });
  
 -                    var snippets = 0;
 -                    self.$snippets.each(function () {
 -                        if (self.snippet_have_dropzone($(this)))
 -                            snippets++;
 -                    });
 -                    if (!snippets) self.$button.css("display", "none");
 +                    self.$el.append($html);
  
                      self.make_snippet_draggable(self.$snippets);
                  });
          hide: function () {
              this.$el.addClass("hidden");
          },
          bind_snippet_click_editor: function () {
              var self = this;
              var snipped_event_flag;
 -            $("#wrapwrap").on('click', function (event) {
 +            self.$wrapwrap.on('click', function (event) {
-                 if (snipped_event_flag || !event.srcElement) {
+                 var srcElement = event.srcElement || (event.originalEvent && (event.originalEvent.originalTarget || event.originalEvent.target));
+                 if (snipped_event_flag || !srcElement) {
                      return;
                  }
                  snipped_event_flag = true;
  
                  setTimeout(function () {snipped_event_flag = false;}, 0);
-                 var $target = $(event.srcElement);
+                 var $target = $(srcElement);
  
                  if ($target.parents(".oe_overlay").length) {
                      return;
                      $target = $target.parents(website.snippet.globalSelector).first();
                  }
  
 -                if (!self.dom_filter($target).length) {
 -                    $target = false;
 -                }
                  if (self.$active_snipped_id && self.$active_snipped_id.is($target)) {
                      return;
                  }
          snippet_blur: function ($snippet) {
              if ($snippet) {
                  if ($snippet.data("snippet-editor")) {
 -                    $snippet.data("snippet-editor").onBlur();
 +                    $snippet.data("snippet-editor").on_blur();
                  }
              }
          },
          snippet_focus: function ($snippet) {
              if ($snippet) {
                  if ($snippet.data("snippet-editor")) {
 -                    $snippet.data("snippet-editor").onFocus();
 +                    $snippet.data("snippet-editor").on_focus();
                  }
              }
          },
              var self = this;
              var options = website.snippet.options;
              var template = website.snippet.templateOptions;
 -            for (var k in options) {
 -                if (template[k] && options[k].prototype.clean_for_save !== dummy) {
 -                    var $snippet = this.dom_filter(template[k].selector);
 -                    $snippet.each(function () {
 -                        new options[k](self, null, $(this), k).clean_for_save();
 +            for (var k in template) {
 +                var Option = options[template[k]['option']];
 +                if (Option && Option.prototype.clean_for_save !== dummy) {
 +                    self.$wrapwrap.find(template[k].selector).each(function () {
 +                        new Option(self, null, $(this), k).clean_for_save();
                      });
                  }
              }
 -            $("*[contentEditable], *[attributeEditable]")
 +            self.$wrapwrap.find("*[contentEditable], *[attributeEditable]")
                  .removeAttr('contentEditable')
                  .removeAttr('attributeEditable');
          },
                  this.create_overlay(this.$active_snipped_id);
                  this.snippet_focus($snippet);
              }
 -            $("#oe_snippets").trigger('snippet-activated', $snippet);
 +            this.$snippet.trigger('snippet-activated', $snippet);
              if ($snippet) {
                  $snippet.trigger('snippet-activated', $snippet);
              }
              this.cover_target($snippet.data('overlay'), $snippet);
          },
  
          // activate drag and drop for the snippets in the snippet toolbar
          make_snippet_draggable: function($snippets){
              var self = this;
 -            var $tumb = $snippets.find(".oe_snippet_thumbnail:first");
 +            var $tumb = $snippets.find(".oe_snippet_thumbnail_img:first");
              var left = $tumb.outerWidth()/2;
              var top = $tumb.outerHeight()/2;
              var $toInsert, dropped, $snippet, action, snipped_id;
                  start: function(){
                      self.hide();
                      dropped = false;
 -                    // snippet_selectors => to get selector-siblings, selector-children, selector-vertical-children
 +                    // snippet_selectors => to get drop-near, drop-in
                      $snippet = $(this);
 -                    $toInsert = $snippet.find('.oe_snippet_body').clone();
 -
 +                    var $base_body = $snippet.find('.oe_snippet_body');
                      var selector = [];
                      var selector_siblings = [];
                      var selector_children = [];
 -                    var selector_vertical_children = [];
 -                    for (var k in website.snippet.templateOptions) {
 -                        if ($toInsert.is(website.snippet.templateOptions[k].selector)) {
 -                            selector.push(website.snippet.templateOptions[k].selector);
 -                            if (website.snippet.templateOptions[k]['selector-siblings'])
 -                                selector_siblings.push(website.snippet.templateOptions[k]['selector-siblings']);
 -                            if (website.snippet.templateOptions[k]['selector-children'])
 -                                selector_children.push(website.snippet.templateOptions[k]['selector-children']);
 -                            if (website.snippet.templateOptions[k]['selector-vertical-children'])
 -                                selector_vertical_children.push(website.snippet.templateOptions[k]['selector-vertical-children']);
 +                    var vertical = false;
 +                    var temp = website.snippet.templateOptions;
 +                    for (var k in temp) {
 +                        if ($base_body.is(temp[k].base_selector)) {
 +                            selector.push(temp[k].base_selector);
 +                            if (temp[k]['drop-near'])
 +                                selector_siblings.push(temp[k]['drop-near']);
 +                            if (temp[k]['drop-in'])
 +                                selector_children.push(temp[k]['drop-in']);
                          }
                      }
  
 +                    $toInsert = $base_body.clone();
                      action = $snippet.find('.oe_snippet_body').size() ? 'insert' : 'mutate';
  
                      if( action === 'insert'){
 -                        if (!selector_siblings.length && !selector_children.length && !selector_vertical_children.length) {
 -                            console.debug($snippet.data("snippet-id") + " have oe_snippet_body class and have not for insert action"+
 -                                "data-selector-siblings, data-selector-children or data-selector-vertical-children tag for mutate action");
 +                        if (!selector_siblings.length && !selector_children.length) {
 +                            console.debug($snippet.find(".oe_snippet_thumbnail_title").text() + " have not insert action: data-drop-near or data-drop-in");
                              return;
                          }
                          self.activate_insertion_zones({
                              siblings: selector_siblings.join(","),
                              children: selector_children.join(","),
 -                            vertical_children: selector_vertical_children.join(","),
                          });
  
                      } else if( action === 'mutate' ){
                          if (!$snippet.data('selector')) {
 -                            console.debug($snippet.data("snippet-id") + " have not oe_snippet_body class and have not data-selector tag");
 +                            console.debug($snippet.data("option") + " have not oe_snippet_body class and have not data-selector tag");
                              return;
                          }
                          var $targets = self.activate_overlay_zones(selector_children.join(","));
                  stop: function(ev, ui){
                      $toInsert.removeClass('oe_snippet_body');
                      
 -                    if (action === 'insert' && ! dropped && $('.oe_drop_zone') && ui.position.top > 3) {
 -                        var el = $('.oe_drop_zone').nearest({x: ui.position.left, y: ui.position.top}).first();
 +                    if (action === 'insert' && ! dropped && self.$wrapwrap.find('.oe_drop_zone') && ui.position.top > 3) {
 +                        var el = self.$wrapwrap.find('.oe_drop_zone').nearest({x: ui.position.left, y: ui.position.top}).first();
                          if (el.length) {
                              el.after($toInsert);
                              dropped = true;
                          }
                      }
  
 -                    $('.oe_drop_zone').droppable('destroy').remove();
 +                    self.$wrapwrap.find('.oe_drop_zone').droppable('destroy').remove();
                      
                      if (dropped) {
                          var $target = false;
                          $target = $toInsert;
  
                          setTimeout(function () {
 -                            $("#oe_snippets").trigger('snippet-dropped', $target);
 +                            self.$snippet.trigger('snippet-dropped', $target);
  
                              website.snippet.start_animation(true, $target);
 -                            // drop_and_build_snippet
 -                            self.create_overlay($target);
 -                            if ($target.data("snippet-editor")) {
 -                                $target.data("snippet-editor").drop_and_build_snippet();
 -                            }
 -                            for (var k in website.snippet.templateOptions) {
 -                                $target.find(website.snippet.templateOptions[k].selector).each(function () {
 -                                    var $snippet = $(this);
 -                                    self.create_overlay($snippet);
 -                                    if ($snippet.data("snippet-editor")) {
 -                                        $snippet.data("snippet-editor").drop_and_build_snippet();
 -                                    }
 -                                });
 -                            }
  
                              // reset snippet for rte
                              $target.removeData("snippet-editor");
                                      $snippet.removeData("overlay");
                                  }
                              });
 +                            // end
 +
 +                            // drop_and_build_snippet
                              self.create_overlay($target);
 +                            if ($target.data("snippet-editor")) {
 +                                $target.data("snippet-editor").drop_and_build_snippet();
 +                            }
 +                            for (var k in website.snippet.templateOptions) {
 +                                $target.find(website.snippet.templateOptions[k].selector).each(function () {
 +                                    var $snippet = $(this);
 +                                    self.create_overlay($snippet);
 +                                    if ($snippet.data("snippet-editor")) {
 +                                        $snippet.data("snippet-editor").drop_and_build_snippet();
 +                                    }
 +                                });
 +                            }
                              // end
  
                              self.make_active($target);
          // return the original snippet in the editor bar from a snippet id (string)
          get_snippet_from_id: function(id){
              return $('.oe_snippet').filter(function(){
 -                    return $(this).data('snippet-id') === id;
 +                    return $(this).data('option') === id;
                  }).first();
          },
  
              var self = this;
              var child_selector = selector.children;
              var sibling_selector = selector.siblings;
 -            var vertical_child_selector   =  selector.vertical_children;
  
 -            var zone_template = "<div class='oe_drop_zone oe_insert'></div>";
 +            var zone_template = $("<div class='oe_drop_zone oe_insert'></div>");
  
              if(child_selector){
 -                self.dom_filter(child_selector).each(function (){
 +                self.$wrapwrap.find(child_selector).each(function (){
                      var $zone = $(this);
 -                    $zone.find('> *:not(.oe_drop_zone):visible').after(zone_template);
 -                    $zone.prepend(zone_template);
 -                });
 -            }
 -
 -            if(vertical_child_selector){
 -                self.dom_filter(vertical_child_selector).each(function (){
 -                    var $zone = $(this);
 -                    var $template = $(zone_template).addClass("oe_vertical");
 -                    var nb = 0;
 -                    var $lastinsert = false;
 -                    var left = 0;
 -                    var temp_left = 0;
 -                    $zone.find('> *:not(.oe_drop_zone):visible').each(function () {
 -                        var $col = $(this);
 -                        $template.css('height', ($col.outerHeight() + parseInt($col.css("margin-top")) + parseInt($col.css("margin-bottom")))+'px');
 -                        $lastinsert = $template.clone();
 -                        $(this).after($lastinsert);
 -
 -                        temp_left = $col.position().left;
 -                        if (left === temp_left) {
 -                            $col.prev(".oe_drop_zone.oe_vertical").remove();
 -                            $col.before($template.clone().css("clear", "left"));
 -                        }
 -                        else if (!nb) {
 -                            $col.before($template.clone());
 -                        }
 -                        left = temp_left;
 -                        nb ++;
 -                    });
 -                    if (!nb) {
 -                        $zone.prepend($template.css('height', $zone.outerHeight()+'px'));
 +                    var vertical;
 +                    var float = window.getComputedStyle(this).float;
 +                    if (float === "left" || float === "right") {
 +                        vertical = $zone.parent().outerHeight()+'px';
                      }
 +                    var $drop = zone_template.clone();
 +                    if (vertical) {
 +                        $drop.addClass("oe_vertical").css('height', vertical);
 +                    }
 +                    $zone.find('> *:not(.oe_drop_zone):visible').after($drop);
 +                    $zone.prepend($drop.clone());
                  });
              }
  
              if(sibling_selector){
 -                self.dom_filter(sibling_selector, true).each(function (){
 +                self.$wrapwrap.find(sibling_selector, true).each(function (){
                      var $zone = $(this);
 +                    var $drop, vertical;
 +                    var float = window.getComputedStyle(this).float;
 +                    if (float === "left" || float === "right") {
 +                        vertical = $zone.parent().outerHeight()+'px';
 +                    }
 +
                      if($zone.prev('.oe_drop_zone:visible').length === 0){
 -                        $zone.before(zone_template);
 +                        $drop = zone_template.clone();
 +                        if (vertical) {
 +                            $drop.addClass("oe_vertical").css('height', vertical);
 +                        }
 +                        $zone.before($drop);
                      }
                      if($zone.next('.oe_drop_zone:visible').length === 0){
 -                        $zone.after(zone_template);
 +                        $drop = zone_template.clone();
 +                        if (vertical) {
 +                            $drop.addClass("oe_vertical").css('height', vertical);
 +                        }
 +                        $zone.after($drop);
                      }
                  });
              }
                  // count += $zones.length;
                  // $zones.remove();
  
 -                $zones = $('.oe_drop_zone > .oe_drop_zone:not(.oe_vertical)').remove();   // no recursive zones
 +                $zones = self.$wrapwrap.find('.oe_drop_zone > .oe_drop_zone:not(.oe_vertical)').remove();   // no recursive zones
                  count += $zones.length;
                  $zones.remove();
              } while (count > 0);
  
              // Cleaning up zones placed between floating or inline elements. We do not like these kind of zones.
 -            var $zones = $('.oe_drop_zone:not(.oe_vertical)');
 +            var $zones = self.$wrapwrap.find('.oe_drop_zone:not(.oe_vertical)');
              $zones.each(function (){
                  var zone = $(this);
                  var prev = zone.prev();
          // generate drop zones covering the elements selected by the selector
          // we generate overlay drop zones only to get an idea of where the snippet are, the drop
          activate_overlay_zones: function(selector){
 -            var $targets = this.dom_filter(selector);
 +            var $targets = typeof selector === "string" ? this.$wrapwrap.find(selector) : selector;
              var self = this;
  
 -            if (typeof selector !== 'string' && !$targets.length) {
 -                console.debug( "A good node must have a [data-oe-model] attribute or must have at least one parent with [data-oe-model] attribute.");
 -                console.debug( "Wrong node(s): ", selector);
 -            }
 -
              function is_visible($el){
                  return     $el.css('display')    != 'none'
                          && $el.css('opacity')    != '0'
                      $target.on("DOMNodeInserted DOMNodeRemoved DOMSubtreeModified", function () {
                          self.cover_target($zone, $target);
                      });
 -                    $('body').on("resize", function () {
 -                        self.cover_target($zone, $target);
 -                    });
 +                    var resize = function () {
 +                        if ($zone.parent().length) {
 +                            self.cover_target($zone, $target);
 +                        } else {
 +                            $('body').off("resize", resize);
 +                        }
 +                    };
 +                    $('body').on("resize", resize);
                  }
                  self.cover_target($target.data('overlay'), $target);
              });
      website.snippet.options = {};
      website.snippet.Option = openerp.Class.extend({
          // initialisation (don't overwrite)
 -        init: function (BuildingBlock, editor, $target, snippet_id) {
 +        init: function (BuildingBlock, editor, $target, option_id) {
              this.BuildingBlock = BuildingBlock;
              this.editor = editor;
              this.$target = $target;
 +            var option = website.snippet.templateOptions[option_id];
              var styles = this.$target.data("snippet-option-ids") || {};
 -            styles[snippet_id] = this;
 +            styles[option_id] = this;
              this.$target.data("snippet-option-ids", styles);
-             this.$overlay = this.$target.data('overlay');
+             this.$overlay = this.$target.data('overlay') || $('<div>');
 -            this['snippet-option-id'] = snippet_id;
 -            var $option = website.snippet.templateOptions[snippet_id].$el;
 -            this.$el = $option.find(">li").clone();
 -            this.data = $option.data();
 -
 -            this.required = this.$el.data("required");
 +            this.option= option_id;
 +            this.$el = option.$el.find(">li").clone();
 +            this.data = option.$el.data();
  
              this.set_active();
 -            this.$el.find('li[data-value] a').on('mouseenter mouseleave click', _.bind(this._mouse, this));
 -            this.$el.not(':not([data-value])').find("a").on('mouseenter mouseleave click', _.bind(this._mouse, this));
 -            this.$target.on('snippet-style-reset', _.bind(this.set_active, this));
 +            this.$target.on('snippet-option-reset', _.bind(this.set_active, this));
 +            this._bind_li_menu();
  
              this.start();
          },
 -        _mouse: function (event) {
 -            var self = this;
  
 -            if (event.type === 'mouseleave') {
 -                if (!this.over) return;
 -                this.over = false;
 -            } else if (event.type === 'click') {
 -                this.over = false;
 -            }else {
 -                this.over = true;
 -            }
 +        _bind_li_menu: function () {
 +            this.$el.filter("li:hasData").find('a:first')
 +                .off('mouseenter click')
 +                .on('mouseenter click', _.bind(this._mouse, this));
  
 -            var $prev, $next;
 -            if (event.type === 'mouseleave') {
 -                $prev = $(event.currentTarget).parent();
 -                $next = this.$el.find("li[data-value].active");
 -            } else {
 -                $prev = this.$el.find("li[data-value].active");
 -                $next = $(event.currentTarget).parent();
 -            }
 -            if (!$prev.length) {
 -                $prev = false;
 -            }
 -            if ($prev && $prev[0] === $next[0]) {
 -                $next = false;
 -                if (this.required) {
 -                    return;
 -                }
 -            }
 +            this.$el
 +                .off('mouseenter click', "li:hasData a")
 +                .on('mouseenter click', "li:hasData a", _.bind(this._mouse, this));
 +
 +            this.$el.closest("ul").add(this.$el)
 +                .off('mouseleave')
 +                .on('mouseleave', _.bind(this.reset, this));
 +
 +            this.$el
 +                .off('mouseleave', "ul")
 +                .on('mouseleave', "ul", _.bind(this.reset, this));
  
 -            var np = {'$next': $next, '$prev': $prev};
 +            this.reset_methods = [];
 +            this.reset_time = null;
 +        },
 +
 +        /**
 +         * this method handles mouse:over and mouse:leave on the snippet editor menu
 +         */
 +         _time_mouseleave: null,
 +        _mouse: function (event) {
 +            var $next = $(event.currentTarget).parent();
  
 +            // triggers preview or apply methods if a menu item has been clicked
 +            this.select(event.type === "click" ? "click" : "over", $next);
              if (event.type === 'click') {
 -                setTimeout(function () {
 -                    self.set_active();
 -                    self.$target.trigger("snippet-style-change", [self, np]);
 -                },0);
 -                this.select({'$next': $next, '$prev': $prev});
 +                this.set_active();
 +                this.$target.trigger("snippet-option-change", [this]);
              } else {
 -                setTimeout(function () {
 -                    self.$target.trigger("snippet-style-preview", [self, np]);
 -                },0);
 -                this.preview(np);
 +                this.$target.trigger("snippet-option-preview", [this]);
              }
          },
 -        /* set_active
 +        /* 
          *  select and set item active or not (add highlight item and his parents)
          *  called before start
          */
          set_active: function () {
 -            var self = this;
 -            this.$el.find('li').removeClass("active");
 -            var $active = this.$el.find('li[data-value]')
 -                .filter(function () {
 -                    var $li = $(this);
 -                    return  ($li.data('value') && self.$target.hasClass($li.data('value')));
 -                })
 -                .first()
 +            var classes = _.uniq((this.$target.attr("class") || '').split(/\s+/));
 +            this.$el.find('[data-toggle_class], [data-select_class]')
 +                .add(this.$el)
 +                .filter('[data-toggle_class], [data-select_class]')
 +                .removeClass("active")
 +                .filter('[data-toggle_class="' + classes.join('"], [data-toggle_class="') + '"] ,'+
 +                    '[data-select_class="' + classes.join('"], [data-select_class="') + '"]')
                  .addClass("active");
 -            this.$el.find('li:has(li[data-value].active)').addClass("active");
          },
  
          start: function () {
          },
  
 -        onFocus : function () {
 +        on_focus : function () {
 +            this._bind_li_menu();
          },
  
 -        onBlur : function () {
 +        on_blur : function () {
          },
  
          on_clone: function ($clone) {
          drop_and_build_snippet: function () {
          },
  
 -        select: function (np) {
 +        reset: function (event) {
              var self = this;
 -            // add or remove html class
 -            if (np.$prev && this.required) {
 -                this.$target.removeClass(np.$prev.data('value' || ""));
 +            var lis = self.$el.add(self.$el.find('li')).filter('.active').get();
 +            lis.reverse();
 +            _.each(lis, function (li) {
 +                var $li = $(li);
 +                for (var k in self.reset_methods) {
 +                    var method = self.reset_methods[k];
 +                    if ($li.is('[data-'+method+']') || $li.closest('[data-'+method+']').size()) {
 +                        delete self.reset_methods[k];
 +                    }
 +                }
 +                self.select("reset", $li);
 +            });
 +
 +            for (var k in self.reset_methods) {
 +                var method = self.reset_methods[k];
 +                if (method) {
 +                    self[method]("reset", null);
 +                }
 +            }
 +            self.reset_methods = [];
 +            self.$target.trigger("snippet-option-reset", [this]);
 +        },
 +
 +        // call data-method args as method
 +        select: function (type, $li) {
 +            var self = this,
 +                $methods = [],
 +                el = $li[0],
 +                $el;
 +            clearTimeout(this.reset_time);
 +
 +            function filter (k) { return k !== 'oeId' && k !== 'oeModel' && k !== 'oeField' && k !== 'oeXpath' && k !== 'oeSourceId';}
 +            function hasData(el) {
 +                for (var k in el.dataset) {
 +                    if (filter (k)) {
 +                        return true;
 +                    }
 +                }
 +                return false;
              }
 -            if (np.$next) {
 -                this.$target.addClass(np.$next.data('value') || "");
 +            function method(el) {
 +                var data = {};
 +                for (var k in el.dataset) {
 +                    if (filter (k)) {
 +                        data[k] = el.dataset[k];
 +                    }
 +                }
 +                return data;
              }
 +
 +            while (el && this.$el.is(el) || _.some(this.$el.map(function () {return $.contains(this, el);}).get()) ) {
 +                if (hasData(el)) {
 +                    $methods.push(el);
 +                }
 +                el = el.parentNode;
 +            }
 +
 +            $methods.reverse();
 +
 +            _.each($methods, function (el) {
 +                var $el = $(el);
 +                var methods = method(el);
 +
 +                for (var k in methods) {
 +                    if (self[k]) {
 +                        if (type !== "reset" && self.reset_methods.indexOf(k) === -1) {
 +                            self.reset_methods.push(k);
 +                        }
 +                        self[k](type, methods[k], $el);
 +                    } else {
 +                        console.error("'"+self.option+"' snippet have not method '"+k+"'");
 +                    }
 +                }
 +            });
          },
  
 -        preview: function (np) {
 -            var self = this;
 +        // default method for snippet
 +        toggle_class: function (type, value, $li) {
 +            var $lis = this.$el.find('[data-toggle_class]').add(this.$el).filter('[data-toggle_class]');
  
 -            // add or remove html class
 -            if (np.$prev) {
 -                this.$target.removeClass(np.$prev.data('value') || "");
 +            function map ($lis) {
 +                return $lis.map(function () {return $(this).data("toggle_class");}).get().join(" ");
              }
 -            if (np.$next) {
 -                this.$target.addClass(np.$next.data('value') || "");
 +            var classes = map($lis);
 +            var active_classes = map($lis.filter('.active, :has(.active)'));
 +
 +            this.$target.removeClass(classes);
 +            this.$target.addClass(active_classes);
 +
 +            if (type !== 'reset') {
 +                this.$target.toggleClass(value);
              }
          },
 +        select_class: function (type, value, $li) {
 +            var $lis = this.$el.find('[data-select_class]').add(this.$el).filter('[data-select_class]');
  
 -        clean_for_save: dummy
 -    });
 +            var classes = $lis.map(function () {return $(this).data('select_class');}).get();
  
 -    website.snippet.options.background = website.snippet.Option.extend({
 -        _get_bg: function () {
 -            return this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, "");
 +            this.$target.removeClass(classes.join(" "));
 +            if(value) this.$target.addClass(value);
          },
 -        _set_bg: function (src) {
 -            this.$target.css("background-image", src && src !== "" ? 'url(' + src + ')' : "");
 +        eval: function (type, value, $li) {
 +            var fn = new Function("node", "type", "value", "$li", value);
 +            fn.call(this, this, type, value, $li);
          },
 +
 +        clean_for_save: dummy
 +    });
 +    website.snippet.options.background = website.snippet.Option.extend({
          start: function () {
              this._super();
 -            var src = this._get_bg();
 -            this.$el.find("li[data-value].active.oe_custom_bg").data("src", src);
 +            var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, "");
 +            if (this.$target.hasClass('oe_custom_bg')) {
 +                this.$el.find('li[data-choose_image]').data("background", src).attr("data-background", src);
 +            }
          },
 -        select: function(np) {
 -            var self = this;
 -            this._super(np);
 -            if (np.$next) {
 -                if (np.$next.hasClass("oe_custom_bg")) {
 -                    var $image = $('<img class="hidden"/>');
 -                    $image.attr("src", np.$prev ? np.$prev.data("src") : '');
 -                    $image.appendTo(self.$target);
 -
 -                    self.element = new CKEDITOR.dom.element($image[0]);
 -                    var editor = new website.editor.MediaDialog(self, self.element);
 -                    editor.appendTo(document.body);
 -                    editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden');
 -
 -                    $image.on('saved', self, function (o) {
 -                        var src = $image.attr("src");
 -                        self._set_bg(src);
 -                        np.$next.data("src", src);
 -                        self.$target.trigger("snippet-style-change", [self, np]);
 -                        $image.remove();
 -                    });
 -                    editor.on('cancel', self, function () {
 -                        if (!np.$prev || np.$prev.data("src") === "") {
 -                            self.$target.removeClass(np.$next.data("value"));
 -                            self.$target.trigger("snippet-style-change", [self, np]);
 -                        }
 -                        $image.remove();
 -                    });
 -                } else {
 -                    this._set_bg(np.$next.data("src"));
 -                }
 +        background: function(type, value, $li) {
 +            if (value && value.length) {
 +                this.$target.css("background-image", 'url(' + value + ')');
 +                this.$target.addClass("oe_img_bg");
              } else {
 -                this._set_bg(false);
 -                this.$target.removeClass(np.$prev.data("value"));
 +                this.$target.css("background-image", "");
 +                this.$target.removeClass("oe_img_bg").removeClass("oe_custom_bg");
              }
          },
 -        preview: function (np) {
 -            this._super(np);
 -            if (np.$next) {
 -                this._set_bg(np.$next.data("src"));
 -            }
 +        choose_image: function(type, value, $li) {
 +            if(type !== "click") return;
 +
 +            var self = this;
 +            var $image = $('<img class="hidden"/>');
 +            $image.attr("src", value);
 +            $image.appendTo(self.$target);
 +
 +            self.element = new CKEDITOR.dom.element($image[0]);
 +            var editor = new website.editor.MediaDialog(self, self.element);
 +            editor.appendTo(document.body);
 +            editor.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden');
 +
 +            $image.on('saved', self, function (o) {
 +                var value = $image.attr("src");
 +                self.$target.css("background-image", 'url(' + value + ')');
 +                self.$el.find('li[data-choose_image]').data("background", value).attr("data-background", value);
 +                self.$target.trigger("snippet-option-change", [self]);
 +                $image.remove();
 +                self.$target.addClass('oe_custom_bg oe_img_bg');
 +                self.set_active();
 +            });
 +            editor.on('cancel', self, function () {
 +                self.$target.trigger("snippet-option-change", [self]);
 +                $image.remove();
 +            });
          },
          set_active: function () {
              var self = this;
 -            var bg = self.$target.css("background-image");
 -            this.$el.find('li').removeClass("active");
 -            this.$el.find('li').removeClass("btn-primary");
 -            var $active = this.$el.find('li[data-value]')
 -                .filter(function () {
 -                    var $li = $(this);
 -                    return  ($li.data('src') && bg.indexOf($li.data('src')) >= 0) ||
 -                            (!$li.data('src') && self.$target.hasClass($li.data('value')));
 -                })
 -                .first();
 -            if (!$active.length) {
 -                $active = this.$target.css("background-image") !== 'none' ?
 -                    this.$el.find('li[data-value].oe_custom_bg') :
 -                    this.$el.find('li[data-value=""]');
 +            var src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)|^none$/g, "");
 +            this._super();
 +
 +            this.$el.find('li[data-background]:not([data-background=""])')
 +                .removeClass("active")
 +                .each(function () {
 +                    var background = $(this).data("background") || $(this).attr("data-background");
 +                    if ((src.length && background.length && src.indexOf(background) !== -1) || (!src.length && !background.length)) {
 +                        $(this).addClass("active");
 +                    }
 +                });
 +
 +            if (!this.$el.find('li[data-background].active').size()) {
 +                this.$el.find('li[data-background=""]:not([data-choose_image])').addClass("active");
 +            } else {
 +                this.$el.find('li[data-background=""]:not([data-choose_image])').removeClass("active");
              }
 +        }
 +    });
 +
 +    website.snippet.options.colorpicker = website.snippet.Option.extend({
 +        start: function () {
 +            var self = this;
 +            var res = this._super();
 +
 +            this.$el.find('li').append( openerp.qweb.render('website.colorpicker') );
  
 -            //don't set active on an OpenDialog link, else it not possible to click on it again after.
 -            // TODO in Saas-4 - Once bootstrap is in less
 -            //      - add a class active-style to get the same display but without the active behaviour used by bootstrap in JS.
 -            var classStr = _.string.contains($active[0].className, "oe_custom_bg") ? "btn-primary" : "active";
 -            $active.addClass(classStr);
 -            this.$el.find('li:has(li[data-value].active)').addClass(classStr);
 +            var classes = [];
 +            this.$el.find("table.colorpicker td > *").map(function () {
 +                var $color = $(this);
 +                var color = $color.attr("class");
 +                if (self.$target.hasClass(color)) {
 +                    self.color = color;
 +                    $color.parent().addClass("selected");
 +                }
 +                classes.push(color);
 +            });
 +            this.classes = classes.join(" ");
 +
 +            this.bind_events();
 +            return res;
 +        },
 +        bind_events: function () {
 +            var self = this;
 +            var $td = this.$el.find("table.colorpicker td");
 +            var $colors = $td.children();
 +            $colors
 +                .mouseenter(function () {
 +                    self.$target.removeClass(self.classes).addClass($(this).attr("class"));
 +                })
 +                .mouseleave(function () {
 +                    self.$target.removeClass(self.classes)
 +                        .addClass($td.filter(".selected").children().attr("class"));
 +                })
 +                .click(function () {
 +                    $td.removeClass("selected");
 +                    $(this).parent().addClass("selected");
 +                });
          }
      });
  
          },
          clean_for_save: function () {
              this._super();
 -            this.$target.find(".item").removeClass("next prev left right active");
 -            this.$target.find(".item:first").addClass("active");
 -            this.$indicators.find('li').removeClass('active');
 -            this.$indicators.find('li:first').addClass('active');
 +            this.$target.find(".item").removeClass("next prev left right active")
 +                .first().addClass("active");
 +            this.$indicators.find('li').removeClass('active')
 +                .first().addClass("active");
          },
          start : function () {
              var self = this;
              this.id = this.$target.attr("id");
              this.$inner = this.$target.find('.carousel-inner');
              this.$indicators = this.$target.find('.carousel-indicators');
              this.$target.carousel('pause');
              this.rebind_event();
          },
 -        on_add_slide: function () {
 +        add_slide: function (type, value) {
 +            if(type !== "click") return;
 +
              var self = this;
              var cycle = this.$inner.find('.item').length;
              var $active = this.$inner.find('.item.active, .item.prev, .item.next').first();
              },0);
              return $clone;
          },
 -        on_remove_slide: function () {
 +        remove_slide: function (type, value) {
 +            if(type !== "click") return;
 +
              if (this.remove_process) {
                  return;
              }
                  this.$target.find('.carousel-control, .carousel-indicators').addClass("hidden");
              }
          },
 +        interval : function(type, value) {
 +            this.$target.attr("data-interval", value);
 +        },
 +        set_active: function () {
 +            this.$el.find('li[data-interval]').removeClass("active")
 +                .filter('li[data-interval='+this.$target.attr("data-interval")+']').addClass("active");
 +        },
      });
      website.snippet.options.carousel = website.snippet.options.slider.extend({
          getSize: function () {
          },
          clean_for_save: function () {
              this._super();
 -            this.$target.css("background-image", "");
 -            this.$target.removeClass(this._class);
 +            this.$target.removeClass('oe_img_bg ' + this._class).css("background-image", "");
          },
          load_style_options : function () {
              this._super();
 -            $(".snippet-style-size li[data-value='']").remove();
 +            $(".snippet-option-size li[data-value='']").remove();
          },
          start : function () {
              var self = this;
                  if (c) self._class = (self._class || "").replace(new RegExp("[ ]+" + c.replace(" ", "|[ ]+")), '') + ' ' + c;
                  return self._class || "";
              };
 -            this.$target.on('snippet-style-change snippet-style-preview', function (event, style, np) {
 -                var $active = self.$target.find(".item.active");
 -                if (style['snippet-option-id'] === "size") return;
 -                if (style['snippet-option-id'] === "background") {
 -                    $active.css("background-image", self.$target.css("background-image"));
 +            this.$target.on('slid.bs.carousel', function () {
 +                if(self.editor && self.editor.styles.background) {
 +                    self.editor.styles.background.$target = self.$target.find(".item.active");
 +                    self.editor.styles.background.set_active();
                  }
 -                if (np.$prev) {
 -                    $active.removeClass(np.$prev.data("value"));
 -                }
 -                if (np.$next) {
 -                    $active.addClass(np.$next.data("value"));
 -                    add_class(np.$next.data("value"));
 -                }
 -            });
 -            this.$target.on('slid', function () { // slide.bs.carousel
 -                var $active = self.$target.find(".item.active");
 -                self.$target
 -                    .css("background-image", $active.css("background-image"))
 -                    .removeClass(add_class($active.attr("class")))
 -                    .addClass($active.attr("class"))
 -                    .trigger("snippet-style-reset");
 -
                  self.$target.carousel("pause");
              });
 -            this.$target.trigger('slid');
 +            this.$target.trigger('slid.bs.carousel');
          },
 -        on_add_slide: function () {
 -            var $clone = this._super();
 +        add_slide: function (type, data) {
 +            if(type !== "click") return;
  
 +            var $clone = this._super(type, data);
              // choose an other background
              var bg = this.$target.data("snippet-option-ids").background;
              if (!bg) return $clone;
  
 -            var $styles = bg.$el.find("li[data-value]:not(.oe_custom_bg)");
 -            var styles_index = $styles.index($styles.filter(".active")[0]);
 -            $styles.removeClass("active");
 -            var $select = $($styles[styles_index >= $styles.length-1 ? 0 : styles_index+1]);
 +            var $styles = bg.$el.find("li[data-background]");
 +            var $select = $styles.filter(".active").removeClass("active").next("li[data-background]");
 +            if (!$select.length) {
 +                $select = $styles.first();
 +            }
              $select.addClass("active");
 -            $clone.css("background-image", $select.data("src") ? "url('"+ $select.data("src") +"')" : "");
 -            $clone.addClass($select.data("value") || "");
 +            $clone.css("background-image", $select.data("background") ? "url('"+ $select.data("background") +"')" : "");
  
              return $clone;
          },
              this._super();
          },
      });
 -
      website.snippet.options.marginAndResize = website.snippet.Option.extend({
          start: function () {
              var self = this;
              return this.grid;
          },
  
 -        onFocus : function () {
 +        on_focus : function () {
              this._super();
              this.change_cursor();
          },
          hide_remove_button: function() {
              this.$overlay.find('.oe_snippet_remove').toggleClass("hidden", !this.$target.siblings().length);
          },
 -        onFocus : function () {
 +        on_focus : function () {
              this._super();
              this.hide_remove_button();
          },
              return false;
          },
          on_remove: function () {
 -            if (!this.$target.siblings().length) {
 -                var $parent = this.$target.parents(".row:first");
 -                if($parent.find("[class*='col-md']").length > 1) {
 -                    return false;
 -                } else {
 -                    if (!$parent.data("snippet-editor")) {
 -                        this.BuildingBlock.create_overlay($parent);
 -                    }
 -                    $parent.data("snippet-editor").on_remove();
 -                }
 -            }
              this._super();
              this.hide_remove_button();
 -            return false;
          },
          on_resize: function (compass, beginClass, current) {
              if (compass === 'w') {
          },
      });
  
 -    website.snippet.options["resize"] = website.snippet.options.marginAndResize.extend({
 +    website.snippet.options.resize = website.snippet.options.marginAndResize.extend({
          getSize: function () {
              this.grid = this._super();
              this.grid.size = 8;
                  this.$target.data("snippet-view", new website.snippet.animationRegistry.parallax(this.$target));
              }
              this.scroll();
 -            this.$target.on('snippet-style-change snippet-style-preview', function () {
 +            this.$target.on('snippet-option-change snippet-option-preview', function () {
                  self.$target.data("snippet-view").set_values();
              });
              this.$target.attr('contentEditable', 'false');
  
              this.$target.find('> div > div:not(.oe_structure) > .oe_structure').attr('contentEditable', 'true');
          },
 -        scroll: function () {
 -            var self = this;
 -            var $ul = this.$el.find('ul[name="parallax-scroll"]');
 -            var $li = $ul.find("li");
 -            var speed = this.$target.data('scroll-background-ratio') || 0.6 ;
 -            $ul.find('[data-value="' + speed + '"]').addClass('active');
 -            $li.on('click', function (event) {
 -                $li.removeClass("active");
 -                $(this).addClass("active");
 -                var speed =  $(this).data('value');
 -                self.$target.attr('data-scroll-background-ratio', speed);
 -                self.$target.data("snippet-view").set_values();
 -                return false;
 -            });
 +        scroll: function (type, value) {
 +            this.$target.attr('data-scroll-background-ratio', value);
              this.$target.data("snippet-view").set_values();
          },
 +        set_active: function () {
 +            var value = this.$target.attr('data-scroll-background-ratio') || 0;
 +            this.$el.find('[data-scroll]').removeClass("active")
 +                .filter('[data-scroll="' + (this.$target.attr('data-scroll-background-ratio') || 0) + '"]').addClass("active");
 +        },
          clean_for_save: function () {
              this._super();
              this.$target.find(".parallax")
          start: function () {
              var self = this;
              this._super();
 -
 -            this.$el.find(".clear-style").click(function (event) {
 -                self.$target.removeClass("fa-spin").attr("style", "");
 -                self.resetTransfo();
 -            });
 -
 -            this.$el.find(".style").click(function (event) {
 -                var settings = self.$target.data("transfo").settings;
 -                self.$target.transfo({ hide: (settings.hide = !settings.hide) });
 -            });
 -
              this.$overlay.find('.oe_snippet_clone, .oe_handles').addClass('hidden');
 -
              this.$overlay.find('[data-toggle="dropdown"]')
                  .on("mousedown", function () {
                      self.$target.transfo("hide");
                  });
          },
 +        style: function (type, value) {
 +            if (type !== 'click') return;
 +            var settings = this.$target.data("transfo").settings;
 +            this.$target.transfo({ hide: (settings.hide = !settings.hide) });
 +        },
 +        clear_style: function (type, value) {
 +            if (type !== 'click') return;
 +            this.$target.removeClass("fa-spin").attr("style", "");
 +            this.resetTransfo();
 +        },
          resetTransfo: function () {
              var self = this;
              this.$target.transfo("destroy");
                  })
                  .mouseover();
          },
 -        onFocus : function () {
 +        on_focus : function () {
              this.resetTransfo();
          },
 -        onBlur : function () {
 +        on_blur : function () {
              this.$target.transfo("hide");
          },
      });
              website.snippet.start_animation(true, this.$target);
  
              $(document.body).on("media-saved", self, function (event, prev , item) {
 -                self.editor.onBlur();
 +                self.editor.on_blur();
                  self.BuildingBlock.make_active(false);
                  if (self.$target.parent().data("oe-field") !== "image") {
                      self.BuildingBlock.make_active($(item));
                  }
              });
 -
 -            this.$el.find(".edition").click(function (event) {
 -                event.preventDefault();
 -                event.stopPropagation();
 -                self.element = new CKEDITOR.dom.element(self.$target[0]);
 -                new website.editor.MediaDialog(self, self.element).appendTo(document.body);
 -            });
          },
 -        onFocus : function () {
 +        edition: function (type, value) {
 +            if(type !== "click") return;
 +            this.element = new CKEDITOR.dom.element(this.$target[0]);
 +            new website.editor.MediaDialog(this, this.element).appendTo(document.body);
 +        },
 +        on_focus : function () {
              var self = this;
              if (this.$target.parent().data("oe-field") === "image") {
                  this.$overlay.addClass("hidden");
                  new website.editor.MediaDialog(self, self.element).appendTo(document.body);
                  self.BuildingBlock.make_active(false);
              }
+             setTimeout(function () {
+                 self.$target.find(".css_editable_mode_display").removeAttr("_moz_abspos");
+             },0);
          },
      });
  
 -
      website.snippet.Editor = openerp.Class.extend({
          init: function (BuildingBlock, dom) {
              this.BuildingBlock = BuildingBlock;
              this.$target = $(dom);
              this.$overlay = this.$target.data('overlay');
 -            this.snippet_id = this.$target.data("snippet-id");
 -            this._readXMLData();
              this.load_style_options();
              this.get_parent_block();
              this.start();
          },
  
 -        /*
 -        *  _readXMLData
 -        *  Read data XML and set value into:
 -        *  this.$el :
 -        *       all xml data
 -        *  this.$overlay :
 -        *       Dom hover the $target who content options
 -        */
 -        _readXMLData: function() {
 -            var self = this;
 -            if(this && this.BuildingBlock && this.BuildingBlock.$snippets) {
 -                this.$el = this.BuildingBlock.$snippets.filter(function () { return $(this).data("snippet-id") == self.snippet_id; }).clone();
 -            }
 -            var $options = this.$overlay.find(".oe_overlay_options");
 -            if ($options.find(".oe_options ul li").length) {
 -                $options.find(".oe_options").removeClass("hidden");
 -            }
 -        },
 -
 -
          // activate drag and drop for the snippets in the snippet toolbar
          _drag_and_drop: function(){
              var self = this;
              self.BuildingBlock.activate_insertion_zones({
                  siblings: self.selector_siblings,
                  children: self.selector_children,
 -                vertical_children: self.selector_vertical_children,
              });
  
              $("body").addClass('move-important');
              this.styles = {};
              this.selector_siblings = [];
              this.selector_children = [];
 -            this.selector_vertical_children = [];
 -            _.each(website.snippet.templateOptions, function (val) {
 +            _.each(website.snippet.templateOptions, function (val, option_id) {
                  if (!self.$target.is(val.selector)) {
                      return;
                  }
 -                if (val['selector-siblings']) self.selector_siblings.push(val['selector-siblings']);
 -                if (val['selector-children']) self.selector_children.push(val['selector-children']);
 -                if (val['selector-vertical-children']) self.selector_vertical_children.push(val['selector-vertical-children']);
 -
 -                var style = val['snippet-option-id'];
 -                var Editor = website.snippet.options[style] || website.snippet.Option;
 -                var editor = self.styles[style] = new Editor(self.BuildingBlock, self, self.$target, style);
 -                $ul.append(editor.$el.addClass("snippet-style-" + style));
 +                if (val['drop-near']) self.selector_siblings.push(val['drop-near']);
 +                if (val['drop-in']) self.selector_children.push(val['drop-in']);
 +
 +                var option = val['option'];
 +                var Editor = website.snippet.options[option] || website.snippet.Option;
 +                var editor = self.styles[option] = new Editor(self.BuildingBlock, self, self.$target, option_id);
 +                $ul.append(editor.$el.addClass("snippet-option-" + option));
              });
              this.selector_siblings = this.selector_siblings.join(",");
              if (this.selector_siblings === "")
              this.selector_children = this.selector_children.join(",");
              if (this.selector_children === "")
                  this.selector_children = false;
 -            this.selector_vertical_children = this.selector_vertical_children.join(",");
 -            if (this.selector_vertical_children === "")
 -                this.selector_vertical_children = false;
  
 -            if (!this.selector_siblings && !this.selector_children && !this.selector_vertical_children) {
 -                this.$overlay.find(".oe_snippet_move").addClass('hidden');
 +            if (!this.selector_siblings && !this.selector_children) {
 +                this.$overlay.find(".oe_snippet_move, .oe_snippet_clone, .oe_snippet_remove").addClass('hidden');
              }
  
  
          },
  
          on_remove: function () {
 -            this.onBlur();
 +            this.on_blur();
              var index = _.indexOf(this.BuildingBlock.snippets, this.$target.get(0));
              for (var i in this.styles){
                  this.styles[i].on_remove();
              }
              delete this.BuildingBlock.snippets[index];
 +
 +            // remove node and his empty
 +            var parent,
 +                node = this.$target.parent()[0];
 +
              this.$target.remove();
 +            function check(node) {
 +                if ($(node).outerHeight() > 8) {
 +                    return false;
 +                }
 +                for (var k=0; k<node.children.length; k++) {
 +                    if (node.children[k].tagName || node.children[k].textContent.match(/[^\s]/)) {
 +                        return false;
 +                    }
 +                }
 +                return true;
 +            }
 +            while (check(node)) {
 +                parent = node.parentNode;
 +                parent.removeChild(node);
 +                node = parent;
 +            }
 +
              this.$overlay.remove();
              return false;
          },
              }
          },
  
 -        /* onFocus
 +        /* on_focus
          *  This method is called when the user click inside the snippet in the dom
          */
 -        onFocus : function () {
 +        on_focus : function () {
              this.$overlay.addClass('oe_active');
              for (var i in this.styles){
 -                this.styles[i].onFocus();
 +                this.styles[i].on_focus();
              }
          },
  
 -        /* onFocus
 +        /* on_focus
          *  This method is called when the user click outside the snippet in the dom, after a focus
          */
 -        onBlur : function () {
 +        on_blur : function () {
              for (var i in this.styles){
 -                this.styles[i].onBlur();
 +                this.styles[i].on_blur();
              }
              this.$overlay.removeClass('oe_active');
          },
  <openerp>
  <data>
  
 +
  <!--
 -     Theme Selector
 +    Customize Themes
 +
 +    Use INPUT type 'checkbox' or 'radio' or use OPTION in select box
 +    'data-xmlid' (optional) xml id of the template to add if the input is checked.
-         You can set a list of xml id separate by comma (all template is enable or 
++        You can set a list of xml id separate by comma (all template is enable or
 +        disable in same time)
 +    'data-enable' (optional) to checked one or more HTML ids, or list separate by comma
 +    'data-disable' (optional) to unchecked one or more HTML ids, or list separate by comma
 +    'data-reload="/"' (optional) force the reloading of the page if the url match with
 +        the string ( = regexp).
 +        Otherwise, only the '/web/css/website.assets_frontend' is reloaded
 +
 +    For the sets (data-enable and/or data-disable without data-xmlid), the set is
 +    automatically checked if:
 +    - all related fields are enabled for data-enable
 +    - all related fields are disabled for data-disable
 +    else unchecked
-     
++
 +    HTML apply classes:
 +    - 'checked': on the parent label when input is checked
-     - 'in': on the container (e.g.: bootstrap modal) after added in DOM (removed together 
++    - 'in': on the container (e.g.: bootstrap modal) after added in DOM (removed together
 +       out is added)
 +    - 'out': on the container 1 second before removed from ths DOM
 +    - 'loading': on the container/modal when the new css is loading
  -->
  
 -    <template id="website.themes" name="Themes">
 -        <t t-call="website.layout">
 -          <div id="wrap" groups="base.group_website_publisher">
 -            <div class="container">
 -    
 -            <div class="alert alert-info mt32" t-if="theme_changed">
 -                <button type="button" class="close" data-dismiss="alert">&amp;times;</button>
 -                <p>
 -                    <h4>Theme Changed!</h4>
 -                    Have a look at <a href="/">your homepage</a> or try another theme below.
 -                </p>
 -            </div>
 -            <h1 class="text-center">Try a New Theme</h1>
 -            <h3 class="text-center text-muted">You'll be able to change the theme at anytime</h3>
 -    
 -            <div class="row mt32" id="themes-list">
 -    
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img src="/website/static/src/img/bootswatch_default_thumbnail.png" class="img-responsive" alt="Default Theme"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Default</h3>
 -                    <p>Pure Bootstrap</p>
 -                    <a class="btn btn-info" href="/website/theme_change">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/amelia/thumbnail.png" alt="Amelia"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Amelia</h3>
 -                    <p>Sweet and cheery</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_amelia">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/cerulean/thumbnail.png" alt="Cerulean"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Cerulean</h3>
 -                    <p>A calm blue sky</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_cerulean">Apply</a>
 -                  </div>
 -                </div>
 +<template id="website.theme_customize" name="Theme Modal for Customization">
 +  <div id="theme_customize_modal" class="modal fade">
 +      <div class="modal-dialog">
 +          <div class="modal-content">
 +              <input id="less" data-xmlid="website.option_bootstrap_less" style="display: none;"/>
 +
 +              <div class="loading_backdrop"></div>
 +              <div class="modal-header text-center">
 +                  <button type="button" class="close">×</button>
 +                  <a class="btn btn-primary" href="/web#return_label=Website&amp;action=website.action_module_theme">Click here to change theme</a>
 +                  <hr style="margin: 20px -10px 10px -10px;"/>
 +                  <h4 class="modal-title">Customize this default theme</h4>
                </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/cosmo/thumbnail.png" alt="Cosmo"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Cosmo</h3>
 -                    <p>An ode to Metro</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_cosmo">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/cyborg/thumbnail.png" alt="Cyborg"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Cyborg</h3>
 -                    <p>Jet black and electric blue</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_cyborg">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/flatly/thumbnail.png" alt="Flatly"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Flatly</h3>
 -                    <p>Flat and modern</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_flatly">Apply</a>
 -                  </div>
 -                </div>
 +              <div class="modal-body">
 +                  <h5 class="modal-h5">LAYOUT</h5>
 +                  <table name="layout">
 +                      <tr>
 +                          <td class=" text-center" width="50%">
 +                              <label class=" center-block">
 +                                  <div class="text-center" style="background-image: url(/website/static/src/img/theme/layout-full.gif); background-size: 100%; line-height: 40px;">FULL WIDTH</div>
 +                                  <input name="layoutvar" type="radio" data-xmlid=""/>
-                               </label>                                 
++                              </label>
 +                          </td>
 +                          <td class=" text-center">
 +                              <label class=" center-block">
 +                                 <div class="text-center" style="background-image: url(/website/static/src/img/theme/layout-boxed.gif); background-size: 100%; line-height: 40px;">BOXED</div>
 +                                  <input name="layoutvar" type="radio" data-xmlid="website.option_layout_boxed" data-enable="less"/>
 +                              </label>
 +                          </td>
 +                      </tr>
 +                  </table>
 +
 +                  <h5 class="modal-h5">MAIN COLOR</h5>
 +                  <table name="color">
 +                      <tr>
 +                          <td>
 +                              <label class="chd-color-combi">
 +                                  <img src="/website/static/src/img/theme/variant-stone.gif" alt="Stone" class="chd-color-combi-img"/>
 +                                  <input name="colorvar" type="radio" data-xmlid="" data-disable="less"/>
 +                              </label>
 +                          </td>
 +                          <td>
 +                              <label class="chd-color-combi">
 +                                  <img src="/website/static/src/img/theme/variant-emerald.gif" alt="Emerald" class="chd-color-combi-img"/>
 +                                  <input name="colorvar" type="radio" data-xmlid="website.option_color_emerald" data-enable="less"/>
 +                              </label>
 +                          </td>
 +                          <td>
 +                              <label class="chd-color-combi">
 +                                  <img src="/website/static/src/img/theme/variant-cobalt.gif" alt="Cobalt" class="chd-color-combi-img"/>
 +                                  <input name="colorvar" type="radio" data-xmlid="website.option_color_cobalt" data-enable="less"/>
 +                              </label>
 +                          </td>
 +                          <td>
 +                              <label class="chd-color-combi">
 +                                  <img src="/website/static/src/img/theme/variant-amethyst.gif" alt="Amethyst" class="chd-color-combi-img"/>
 +                                  <input name="colorvar" type="radio" data-xmlid="website.option_color_amethyst" data-enable="less"/>
 +                              </label>
 +                          </td>
 +                          <td>
 +                              <label class="chd-color-combi">
 +                                  <img src="/website/static/src/img/theme/variant-ruby.gif" alt="Blue" class="chd-color-combi-img"/>
 +                                  <input name="colorvar" type="radio" data-xmlid="website.option_color_ruby" data-enable="less"/>
 +                              </label>
 +                          </td>
 +                          <td>
 +                              <label class="chd-color-combi">
 +                                  <img src="/website/static/src/img/theme/variant-gold.gif" alt="Gold" class="chd-color-combi-img"/>
 +                                  <input name="colorvar" type="radio" data-xmlid="website.option_color_gold" data-enable="less"/>
 +                              </label>
 +                          </td>
 +                      </tr>
 +                  </table>
 +
 +                  <h5 class="modal-h5">FONTS COMBINATIONS</h5>
 +                  <table name="font">
 +                      <tr>
 +                          <td width="50%">
 +                              <label>
 +                                  <div>
 +                                      <span style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:11px">Helvetica</span>
 +                                      <span style="font-family:Georgia, 'Times New Roman', Times, serif; font-size:11px" >/ Georgia</span>
 +                                  </div>
 +                                  <input name="theme" type="radio" data-xmlid=""/>
 +                              </label>
 +                          </td>
 +                          <td width="50%">
 +                              <label>
 +                                  <div>
 +                                      <span style="font-family:Georgia, 'Times New Roman', Times, serif; font-size:11px" >Georgia</span>
 +                                      <span style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:11px">/ Helvetica</span>
 +                                    <input name="theme" type="radio" data-xmlid="website.option_font" data-enable="less"/>
 +                                  </div>
 +                              </label>
 +                          </td>
 +                      </tr>
 +                  </table>
                </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/journal/thumbnail.png" alt="Journal"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Journal</h3>
 -                    <p>Crisp like a new sheet of paper.</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_journal">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/readable/thumbnail.png" alt="Readable"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Readable</h3>
 -                    <p>Optimized for legibility</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_readable">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/simplex/thumbnail.png" alt="Simplex"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Simplex</h3>
 -                    <p>Mini and minimalist.</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_simplex">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/slate/thumbnail.png" alt="Slate"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Slate</h3>
 -                    <p>Shades of gunmetal gray</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_slate">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/spacelab/thumbnail.png" alt="Spacelab"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Spacelab</h3>
 -                    <p>Silvery and sleek.</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_spacelab">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/united/thumbnail.png" alt="United"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>United</h3>
 -                    <p>Ubuntu orange and unique font</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_united">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -              <div class="col-md-4">
 -                <div class="well text-center">
 -                  <div class="image">
 -                    <img class="img-responsive" src="http://bootswatch.com/yeti/thumbnail.png" alt="Yeti"/>
 -                  </div>
 -                  <div class="options">
 -                    <h3>Yeti</h3>
 -                    <p>A friendly foundation</p>
 -                    <a class="btn btn-info" href="/website/theme_change?theme_id=website.theme_yeti">Apply</a>
 -                  </div>
 -                </div>
 -              </div>
 -
 -            </div>
 -            </div>
            </div>
 -        </t>
 -    </template>
 -
 +      </div>
 +  </div>
 +</template>
 +
 +<!-- color-picker -->
 +<!-- HTML class to hide option for one mode : only-text, only-bg -->
 +
 +<template id="website.colorpicker" name="Color-Picker">
 +    <table class="colorpicker">
 +        <tr>
 +            <td><button class="automatic-color" title="Automatic Color"/></td>
 +            <td><button class="bg-primary" title="Primary Color"/></td>
 +            <td><button class="bg-success" title="Success Color"/></td>
 +            <td><button class="bg-info" title="Info Color"/></td>
 +            <td><button class="bg-warning" title="Warning Color"/></td>
 +            <td><button class="bg-danger" title="Danger Color"/></td>
 +        </tr>
 +        <tr><td colspan="8"><hr style="width: 100%; height: 1px;"/></td></tr>
 +        <tr>
 +            <td><button class="bg-blue"/></td>
 +            <td><button class="bg-turquoise"/></td>
 +            <td><button class="bg-green"/></td>
 +            <td><button class="bg-yellow"/></td>
 +            <td><button class="bg-red"/></td>
 +            <td><button class="bg-pink"/></td>
 +            <td><button class="bg-purple"/></td>
 +            <td><button class="bg-brown"/></td>
 +        </tr>
 +    </table>
 +</template>
  
  <!--
 -     All Default Themes
 +     Theme
  -->
  
 -    <template id="website.theme_amelia" name="Amelia" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/amelia.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/amelia.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_cerulean" name="Cerulean" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/cerulean.min.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_cosmo" name="Cosmo" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/cosmo.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/cosmo.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_cyborg" name="Cyborg" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/cyborg.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/cyborg.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_flatly" name="Flatly" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/flatly.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/flatly.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_journal" name="Journal" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/journal.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/journal.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_readable" name="Readable" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/readable.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/readable.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_simplex" name="Simplex" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/simplex.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/simplex.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_slate" name="Slate" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/slate.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/slate.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_spacelab" name="Spacelab" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/spacelab.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/spacelab.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_united" name="United" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/united.min.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 -
 -    <template id="website.theme_yeti" name="Yeti" inherit_id="website.theme" active="False" customize_show="True">
 -        <xpath expr="//link[@id='bootstrap_css']" position="replace">
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/yeti.min.css' t-ignore="true"/>
 -            <link rel='stylesheet' href='/website/static/src/css/bootswatch/yeti.fix.css' t-ignore="true"/>
 -        </xpath>
 -    </template>
 +<template id="website.theme" name="Theme">
 +    <link rel="stylesheet" href='/website/static/src/css/website.css'/>
 +    <!-- Boostrap Fallback if server can't compute less files -->
 +    <link id="bootstrap_css" rel='stylesheet' href='/web/static/lib/bootstrap/css/bootstrap.css'/>
 +</template>
 +
 +<!-- Bootstrap Less File layout -->
 +
- <template id="option_bootstrap_less" name="option_bootstrap_less" inherit_id="website.theme" optional="disabled">
++<template id="option_bootstrap_less" name="option_bootstrap_less" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[@id='bootstrap_css']" position="replace">
 +        <link rel="stylesheet" href='/website/static/src/less/import_bootstrap.less'/>
 +        <link rel="stylesheet" href='/website/static/src/less/colors.less' t-ignore="true"/>
 +    </xpath>
 +</template>
 +
 +<!-- Option layout -->
 +
- <template id="option_layout_boxed" name="option_layout_boxed" inherit_id="website.theme" optional="disabled">
++<template id="option_layout_boxed" name="option_layout_boxed" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[last()]" position="after">
 +        <link href="/website/static/src/less/option_layout_boxed.less" rel="stylesheet" type="text/less"/>
 +    </xpath>
 +</template>
 +
 +<!-- Option color -->
 +
- <template id="option_color_stone" name="option_color_stone" inherit_id="website.theme" optional="disabled">
++<template id="option_color_stone" name="option_color_stone" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[last()]" position="after"><link href="/website/static/src/less/option_color_stone.less" rel="stylesheet" type="text/less"/></xpath>
 +</template>
- <template id="option_color_emerald" name="option_color_emerald" inherit_id="website.theme" optional="disabled">
++<template id="option_color_emerald" name="option_color_emerald" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[last()]" position="after"><link href="/website/static/src/less/option_color_emerald.less" rel="stylesheet" type="text/less"/></xpath>
 +</template>
- <template id="option_color_cobalt" name="option_color_cobalt" inherit_id="website.theme" optional="disabled">
++<template id="option_color_cobalt" name="option_color_cobalt" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[last()]" position="after"><link href="/website/static/src/less/option_color_cobalt.less" rel="stylesheet" type="text/less"/></xpath>
 +</template>
- <template id="option_color_amethyst" name="option_color_amethyst" inherit_id="website.theme" optional="disabled">
++<template id="option_color_amethyst" name="option_color_amethyst" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[last()]" position="after"><link href="/website/static/src/less/option_color_amethyst.less" rel="stylesheet" type="text/less"/></xpath>
 +</template>
- <template id="option_color_ruby" name="option_color_ruby" inherit_id="website.theme" optional="disabled">
++<template id="option_color_ruby" name="option_color_ruby" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[last()]" position="after"><link href="/website/static/src/less/option_color_ruby.less" rel="stylesheet" type="text/less"/></xpath>
 +</template>
- <template id="option_color_gold" name="option_color_gold" inherit_id="website.theme" optional="disabled">
++<template id="option_color_gold" name="option_color_gold" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[last()]" position="after"><link href="/website/static/src/less/option_color_gold.less" rel="stylesheet" type="text/less"/></xpath>
 +</template>
 +
- <template id="option_font" name="option_font" inherit_id="website.theme" optional="disabled">
++<template id="option_font" name="option_font" inherit_id="website.theme" active="False" customize_show="False">
 +    <xpath expr="//link[last()]" position="after"><link href="/website/static/src/less/option_font.less" rel="stylesheet" type="text/less"/></xpath>
 +</template>
  
  </data>
  </openerp>
@@@ -6,6 -6,11 +6,6 @@@
  
  <!-- Layout and generic templates -->
  
 -<template id="website.theme" name="Theme">
 -    <link id="bootstrap_css" rel='stylesheet' href='/web/static/lib/bootstrap/css/bootstrap.css' t-ignore="true"/>
 -    <link rel="stylesheet" href='/website/static/src/css/website.css' t-ignore="true"/>
 -</template>
 -
  <template id="website.assets_frontend" name="Website assets">
      <t t-call="website.theme"/>
  
@@@ -15,7 -20,7 +15,7 @@@
  
      <script type="text/javascript" src="/website/static/src/js/website.snippets.animation.js"></script>
      <script type="text/javascript" src="/web/static/lib/bootstrap/js/bootstrap.js"></script>
 -
 +    
  </template>
  
  <template id="assets_backend" name="website assets for backend" inherit_id="web.assets_backend">
              <title><t t-esc="title"/></title>
  
              <t t-set="languages" t-value="website.get_languages() if website else None"/>
-             <t t-if="request and request.website_multilang">
-                 <t t-foreach="languages" t-as="lg">
-                     <t t-set="force_lang" t-value="lg[0] if website and lg[0] != website.default_lang_code else None"/>
-                     <link rel="alternate" t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang=force_lang)" t-att-hreflang="lg[0].replace('_', '-').lower()" />
+             <t t-if="request and request.website_multilang and website">
+                 <t t-foreach="website.get_alternate_languages(request.httprequest)" t-as="lang">
+                     <link rel="alternate" t-att-hreflang="lang['hreflang']" t-att-href="lang['href']"/>
                  </t>
              </t>
  
                      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
                      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
                      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-                     })(window,document,'script','//www.google-analytics.com/analytics.js','_gaw');
+                     })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
  
-                     _gaw('create',_.str.trim('<t t-esc="website.google_analytics_key"/>'));
-                     _gaw('send','pageview');
+                     ga('create',_.str.trim('<t t-esc="website.google_analytics_key"/>'));
+                     ga('send','pageview');
                  </script>
              </t>
          </head>
                                      <li class="dropdown" t-ignore="true" t-if="website.user_id != user_id">
                                          <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                                              <b>
 -                                                <span t-esc="user_id.name"/>
 +                                                <span t-esc="(len(user_id.name)&gt;25) and (user_id.name[:23]+'...') or user_id.name"/>
                                                  <span class="caret"></span>
                                              </b>
                                          </a>
                  <footer>
                      <div id="footer_container">
                      </div>
-                     <div class="container mt16 mb8">
-                         <div class="pull-right" t-ignore="true" t-if="not editable">
-                             Create a <a href="http://www.odoo.com/page/website-builder">free website</a> with
-                             <a class="label label-danger" href="http://www.odoo.com/page/website-builder">Odoo</a>
-                         </div>
-                         <div class="pull-left text-muted">
-                             Copyright &amp;copy; <span t-field="res_company.name">Company name</span>
-                         </div>
-                     </div>
                  </footer>
              </div>
          </body>
      </html>
  </template>
  
- <template id="layout_logo_show" inherit_id="website.layout" optional="enabled" name="Show Logo">
+ <template id="layout_footer_copyright" inherit_id="website.layout" name="Footer Copyright">
+     <xpath expr="//footer" position="inside">
+         <div class="container mt16 mb8">
+             <div class="pull-right" t-ignore="true" t-if="not editable">
+                 Create a <a href="http://www.odoo.com/page/website-builder">free website</a> with
+                 <a class="label label-danger" href="http://www.odoo.com/page/website-builder">Odoo</a>
+             </div>
+             <div class="pull-left text-muted">
+                 Copyright &amp;copy; <span t-field="res_company.name">Company name</span>
+             </div>
+         </div>
+     </xpath>
+ </template>
+ <template id="layout_logo_show" inherit_id="website.layout" customize_show="True" name="Show Logo">
      <xpath expr="//header//a[@class='navbar-brand']" position="replace">
          <a href="/" class="navbar-brand logo">
              <img src="/logo.png"/>
      <script type="text/javascript" src="/website/static/src/js/website.tour.js"></script>
      <script type="text/javascript" src="/website/static/src/js/website.tour.banner.js"></script> <!-- groups="base.group_website_designer" -->
      <script type="text/javascript" src="/website/static/src/js/website.snippets.editor.js"></script>
 +    <script type="text/javascript" src="/website/static/src/js/website.snippets.gallery.js" />
      <script type="text/javascript" src="/website/static/src/js/website.ace.js"></script>
      <script type="text/javascript" src="/website/static/src/js/website.translator.js"></script>
 +    <script type="text/javascript" src="/website/static/src/js/website.theme.js"></script>
  
  </template>
  
      </xpath>
  </template>
  
- <template id="show_sign_in" optional="enabled" inherit_id="website.layout" name="Show Sign In" groups="base.group_public">
+ <template id="show_sign_in" customize_show="True" inherit_id="website.layout" name="Show Sign In" groups="base.group_public">
      <xpath expr="//ul[@id='top_menu']" position="inside">
          <li class="divider"/>
          <li>
  <template id="footer_custom" inherit_id="website.layout" name="Footer">
      <xpath expr="//div[@id='footer_container']" position="replace">
          <div class="oe_structure" id="footer">
 -            <section data-snippet-id='three-columns' class="mt16 mb16">
 +            <section class="mt16 mb16">
                  <div class="container">
                      <div class="row">
                          <div class="col-md-4">
      </xpath>
  </template>
  
- <template id="footer_default" inherit_id="website.footer_custom" optional="enabled" name="Automatic Footer">
+ <template id="footer_default" inherit_id="website.footer_custom" customize_show="True" name="Automatic Footer">
      <xpath expr="//div[@id='footer']" position="replace">
          <div class="container hidden-print">
              <div class="row">
  
  <!-- Error and special pages -->
  
+ <template id="info" name="Odoo Information">
+     <t t-call="website.layout">
+       <div id="wrap">
+         <div class="oe_structure">
+             <section class="container">
+                 <h1><t t-esc="res_company.name"/>
+                     <small>Odoo Version <t t-raw="version.get('server_version')"/></small>
+                 </h1>
+                 <p>
+                     Information about the <t t-esc="res_company.name"/> instance of Odoo, the <a href="https://www.odoo.com">Open Source ERP</a>.
+                 </p>
+                 <div class="alert alert-warning alert-dismissable mt16" groups="base.group_website_publisher">
+                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
+                    <p>
+                      Note: To hide this page, uncheck it from the top Customize menu.
+                    </p>
+                 </div>
+                 <h2>Installed Applications</h2>
+                 <dl class="dl-horizontal" t-foreach="apps" t-as="app">
+                     <dt>
+                         <a t-att-href="app.website" t-if="app.website">
+                             <t t-raw="app.shortdesc"/>
+                         </a>
+                         <span t-raw="app.shortdesc" t-if="not app.website"/>
+                     </dt>
+                     <dd>
+                         <span t-raw="app.summary"/>
+                     </dd><dd class="text-muted">
+                         Technical name: <span t-field="app.name"/>, updated: <span t-field="app.write_date"/>, author: <span t-field="app.author"/>
+                     </dd>
+                 </dl>
+                 <h2>Installed Modules</h2>
+                 <dl class="dl-horizontal">
+                     <t t-foreach="modules" t-as="app">
+                         <dt>
+                             <span t-raw="app.shortdesc"/>
+                         </dt>
+                         <dd t-if="app.summary">
+                             <span t-raw="app.summary"/>
+                         </dd><dd>
+                             Technical name: <span t-field="app.name"/>,
+                             updated: <span t-field="app.write_date"/>
+                             <t t-if="app.author">
+                                 , author: <span t-field="app.author"/>
+                             </t>
+                         </dd>
+                     </t>
+                 </dl>
+             </section>
+         </div>
+       </div>
+     </t>
+ </template>
  <template id="default_page">
      <t t-call="website.layout">
        <div id="wrap" class="oe_structure oe_empty"></div>
                      <div class="oe_structure">
                          <h1 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h1>
                      </div>
                      <div class="container" t-if="views">
                          <div class="alert alert-danger" t-if="qweb_exception and editable">
                              <h4>Template fallback</h4>
@@@ -700,7 -766,7 +763,7 @@@ Sitemap: <t t-esc="url_root"/>sitemap.x
  
  <template id="sitemap_index_xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  <sitemapindex t-attf-xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
-   <sitemap t-foreach="pages" t-as="page">
+   <sitemap t-translate="off" t-foreach="pages" t-as="page">
      <loc><t t-esc="url_root"/>sitemap-<t t-esc="page"/>.xml</loc>
    </sitemap>
  </sitemapindex>
          <div id="wrap">
              <div class="oe_structure">
  
 -                <section data-snippet-id="title">
 +                <section>
                      <div class="container">
                          <div class="row">
                              <div class="col-md-12">
                      </div>
                  </section>
  
 -                <section data-snippet-id="text-image">
 +                <section>
                      <div class="container">
                          <div class="row">
                              <div class="col-md-6 mt32">
@@@ -2,23 -2,13 +2,23 @@@
  <openerp>
  <data>
  
 -<template id="assets_editor" inherit_id="website.assets_editor" name="Blog Editor" groups="base.group_document_user">
 +<template id="assets_editor" inherit_id="website.assets_editor" name="Blog Editor">  <!-- groups="base.group_document_user" -->
      <xpath expr="." position="inside">
          <script type="text/javascript" src="/website_blog/static/src/js/website_blog.editor.js"></script>
          <script type="text/javascript" src="/website_blog/static/src/js/website.tour.blog.js"></script>
      </xpath>
  </template>
  
 +<template id="assets_frontend" inherit_id="website.assets_frontend" name="Blog Front-end assets">
 +    <xpath expr="." position="inside">
 +        <link rel='stylesheet' href='/website_blog/static/src/css/website_blog.css'/>
 +
 +        <script type="text/javascript" src="/website_blog/static/lib/contentshare.js"/>
 +        <script type="text/javascript" src="/website_blog/static/src/js/website_blog.inline.discussion.js"></script>
 +        <script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"/>
 +    </xpath>
 +</template>
 +
  <!-- Layout add nav and footer -->
  <template id="header_footer_custom" inherit_id="website.footer_default" name="Footer News Blog Link">
      <xpath expr="//div[@id='info']/ul" position="inside">
@@@ -46,7 -36,7 +46,7 @@@
                      <t t-set="classname">pull-right</t>
                  </t>
              </section>
 -            <section data-snippet-id="title" class="container">
 +            <section class="container">
                  <div class="row">
                      <div class="col-md-12 text-center">
                          <h1>Latest Posts</h1>
                  <div t-foreach="blog_posts" t-as="blog_post" class="mb32">
  
                    <img class="img-circle pull-right mt16"
-                     t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(blog_post.author_id.id)"
+                     t-attf-src="/website/image/res.partner/{{ blog_post.author_id.id }}/image_small"
                      style="width: 50px;"/>
  
                    <a t-attf-href="/blog/#{ slug(blog_post.blog_id) }/post/#{ slug(blog_post) }">
  
  <!-- Option: Blog Post List: show tags -->
  <template id="opt_blog_post_short_tags" name="Tags"
-         optional="enabled" inherit_id="website_blog.blog_post_short">
+         customize_show="True" inherit_id="website_blog.blog_post_short">
      <xpath expr="//div[@name='blog_post_data']" position="inside">
          <p class="post-meta text-muted text-center" t-if="len(blog_post.tag_ids)">
              <span class="fa fa-tags"/>
              <h2 t-field="blog_post.subtitle"/>
              <p class="post-meta text-muted text-center" name="blog_post_data"/>
              <div>
-                 <img class="img-circle" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(blog_post.author_id.id)" style="width: 30px; margin-right: 10px;"/>
+                 <img class="img-circle" t-attf-src="/website/image/res.partner/{{ blog_post.author_id.id }}/image_small" style="width: 30px; margin-right: 10px;"/>
                  <span t-field="blog_post.author_id" style="display: inline-block;" t-field-options='{
                      "widget": "contact",
                      "fields": ["name"]
          <ul class="media-list" id="comments-list">
              <li t-foreach="comments" t-as="message" class="media">
                  <span class="pull-left">
-                     <img class="media-object img img-circle" t-att-src="'/website/image?model=mail.message&amp;field=author_avatar&amp;id='+str(message.id)" style="width: 30px"/>
+                     <img class="media-object img img-circle" t-attf-src="/website/image/mail.message/{{ message.id }}/author_avatar" style="width: 30px"/>
                  </span>
                  <div class="media-body">
                      <t t-call="website.publish_short">
                  <h1 t-field="next_post.name"/>
                  <h2 t-field="next_post.subtitle"/>
                  <div>
-                     <img class="img-circle" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(next_post.author_id.id)" style="width: 30px; margin-right: 10px;"/>
+                     <img class="img-circle" t-attf-src="'/website/image/res.partner/{{ next_post.author_id.id }}/image_small" style="width: 30px; margin-right: 10px;"/>
                      <span t-field="next_post.author_id" style="display: inline-block;" t-field-options='{
                              "widget": "contact",
                              "fields": ["name"]
  
  <!-- Options: Blog Post: breadcrumb -->
  <template id="blog_breadcrumb" name="Breadcrumb"
-         inherit_id="website_blog.blog_post_complete" optional="disabled">
+         inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
      <xpath expr="//div[@id='title']" position="before">
          <div class="container">
            <div class="row">
  
  <!-- Options: Blog Post: user can reply -->
  <template id="opt_blog_post_complete_comment" name="Allow blog post comment"
-         inherit_id="website_blog.blog_post_complete" optional="disabled"
+         inherit_id="website_blog.blog_post_complete" active="False" customize_show="True"
          groups="website_mail.group_comment">
      <xpath expr="//ul[@id='comments-list']" position="before">
          <section class="mb32 read_width css_editable_mode_hidden">
              <form id="comment" t-attf-action="/blogpost/comment" method="POST">
                  <div class="media">
                      <span class="pull-left">
-                         <img class="img img-circle media-object" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 30px"/>
+                         <img class="img img-circle media-object" t-attf-src="/website/image/res.partner/{{ user_id.partner_id.id }}/image_small" style="width: 30px"/>
                      </span>
                      <div class="media-body">
                          <input name="blog_post_id" t-att-value="blog_post.id" type="hidden"/>
  
  <!-- Options: Blog Post: user can select text for tweet -->
  <template id="opt_blog_post_select_to_tweet" name="Select to Tweet"
-         inherit_id="website_blog.blog_post_complete" optional="disabled">
+         inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
      <xpath expr="//div[@id='blog_content']" position="attributes">
          <attribute name="class">js_tweet mt32</attribute>
      </xpath>
  
  <!-- Options: Blog Post: user can add Inline Discussion -->
  <template id="opt_blog_post_inline_discussion" name="Allow comment in text"
-         inherit_id="website_blog.blog_post_complete" optional="disabled">
+         inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
      <xpath expr="//div[@id='blog_content']" position="attributes">
          <attribute name="enable_chatter_discuss">True</attribute>
      </xpath>
  
  <!-- Options: Blog Post: show tags -->
  <template id="opt_blog_post_complete_tags" name="Tags"
-         optional="enabled" inherit_id="website_blog.blog_post_complete">
+         customize_show="True" inherit_id="website_blog.blog_post_complete">
      <xpath expr="//p[@name='blog_post_data']" position="after">
          <p class="post-meta text-muted text-center" t-if="len(blog_post.tag_ids)">
              <span class="fa fa-tags"/>
  </template>
  
  <!-- Page -->
 -<template id="assets_frontend" inherit_id="website.assets_frontend" name="website_blog assets" >
 -    <xpath expr="/t" position="inside">
 -        <link rel='stylesheet' href='/website_blog/static/src/css/website_blog.css'/>
 -        <script type="text/javascript" src="/website_blog/static/src/js/website_blog.inline.discussion.js"></script>
 -        <script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"/>
 -        <script type="text/javascript" src="/website_blog/static/lib/contentshare.js"/>
 -    </xpath>
 -</template>
 -
  <template id="index" name="Blog Navigation">
      <t t-call="website.layout">
-         <div id="wrap" class="js_blog">
+         <div id="wrap" class="js_blog website_blog">
              <t t-raw="0"/>
          </div>
      </t>
  <!-- Option:Right Column for extra info -->
  
  <template id="index_right" name="Right Column"
-         inherit_id="website_blog.blog_post_short" optional="disabled">
+         inherit_id="website_blog.blog_post_short" active="False" customize_show="True">
      <xpath expr="//div[@id='main_column']" position="attributes">
          <attribute name="class">col-sm-8</attribute>
      </xpath>
  
  <!-- Option:Right Column: tags -->
  <template id="opt_blog_rc_tags" name="Tags"
-         inherit_id="website_blog.index_right" optional="disabled">
+         inherit_id="website_blog.index_right" active="False" customize_show="True">
      <xpath expr="//div[@id='blog_right_column']" position="inside">
          <section class="mt32">
          <h4>Tags</h4>
  
  <!-- Option:Right Column: archives -->
  <template id="opt_blog_rc_history" name="Archives"
-         inherit_id="website_blog.index_right" optional="disabled">
+         inherit_id="website_blog.index_right" active="False" customize_show="True">
      <xpath expr="//div[@id='blog_right_column']" position="inside">
          <section class="mt32">
          <h4>Archives</h4>
  
  <!-- Option:Right Column: about us -->
  <template id="opt_blog_rc_about_us" name="About Us" priority="2"
-         inherit_id="website_blog.index_right" optional="disabled">
+         inherit_id="website_blog.index_right" active="False" customize_show="True">
      <xpath expr="//div[@id='blog_right_column']" position="inside">
          <section class="mt32">
          <h4>About us</h4>
  
  <!-- Option:Right Column: follow us -->
  <template id="opt_blog_rc_follow_us" name="Follow us" priority="4"
-         inherit_id="website_blog.index_right" optional="disabled">
+         inherit_id="website_blog.index_right" active="False" customize_show="True">
      <xpath expr="//div[@id='blog_right_column']" position="inside">
          <section class="mt32">
              <h4>Follow us<small t-if="blog">: <t t-esc="blog.name"/></small></h4>
  
  <!-- Option:Right Column: blogs -->
  <template id="opt_blog_rc_blogs" name="Our Blogs" priority="6"
-         inherit_id="website_blog.index_right" optional="disabled">
+         inherit_id="website_blog.index_right" active="False" customize_show="True">
      <xpath expr="//div[@id='blog_right_column']" position="inside">
          <section class="mt32 mb32">
          <h4>Our Blogs</h4>
@@@ -13,7 -13,7 +13,7 @@@
      <t t-call="website.layout">
          <div id="wrap">
              <div class="oe_structure">
 -                <section data-snippet-id="title">
 +                <section>
                      <h1 class="text-center">
                          Our References
                      </h1><h2 class="text-center text-muted">
@@@ -66,7 -66,7 +66,7 @@@
  </template>
  
  <!-- Option: left column: World Map -->
- <template id="opt_country" inherit_id="website_customer.index" optional="enabled" name="Show Map">
+ <template id="opt_country" inherit_id="website_customer.index" customize_show="True" name="Show Map">
      <xpath expr="//div[@id='ref_left_column']" position="inside">
          <!-- modal for large map -->
          <div class="modal fade customer_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
          <!-- modal end -->
          <h3>World Map<button class="btn btn-link" data-toggle="modal" data-target=".customer_map_modal"><span class="fa fa-external-link" /></button></h3>
          <ul class="nav">
-             <iframe t-attf-src="/google_map/?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/customers/"
-                 style="width:260px; height:240px; border:0; padding:0; margin:0;"></iframe>
+             <iframe t-attf-src="/google_map?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/customers/"
+                 style="width:260px; height:240px; border:0; padding:0; margin:0;" scrolling="no"></iframe>
          </ul>
      </xpath>
  </template>
  
- <template id="opt_country_list" inherit_id="website_customer.index" optional="enabled" name="Filter on Countries">
+ <template id="opt_country_list" inherit_id="website_customer.index" customize_show="True" name="Filter on Countries">
      <xpath expr="//div[@id='ref_left_column']" position="inside">
          <h3>References by Country</h3>
          <ul class="nav nav-pills nav-stacked mt16 mb32">
@@@ -57,6 -57,7 +57,7 @@@
                              <div itemscope="itemscope" itemtype="http://schema.org/Event" class="media-body">
                                  <h4 class="media-heading">
                                      <a itemprop="url" t-att-class="event.state == 'done' and 'text-success'" t-attf-href="/event/#{ slug(event) }/#{(not event.menu_id) and 'register' or ''}"><span itemprop="name" t-field="event.name"> </span></a>
+                                     <small t-if="not event.address_id" class="label label-info">Online</small>
                                      <small t-if="not event.website_published" class="label label-danger">not published</small>
                                  </h4>
                                  <div>
@@@ -87,7 -88,7 +88,7 @@@
      </t>
  </template>
  
- <template id="event_right_photos" inherit_id="website_event.index" optional="disabled" name="Photos">
+ <template id="event_right_photos" inherit_id="website_event.index" active="False" customize_show="True" name="Photos">
      <xpath expr="//div[@id='right_column']" position="inside">
          <div class="row">
              <div class="col-md-12 mb16">
      </xpath>
  </template>
  
- <template id="event_right_quotes" inherit_id="website_event.index" optional="disabled" name="Quotes">
+ <template id="event_right_quotes" inherit_id="website_event.index" active="False" customize_show="True" name="Quotes">
      <xpath expr="//div[@id='right_column']" position="inside">
          <div class="row">
              <div class="col-md-12 mb16">
      </xpath>
  </template>
  
- <template id="event_right_country_event" inherit_id="website_event.index" optional="disabled" name="Country Events">
+ <template id="event_right_country_event" inherit_id="website_event.index" active="False" customize_show="True" name="Country Events">
      <xpath expr="//div[@id='right_column']" position="inside">
          <div class="row">
              <div class="col-md-12 mb16 mt16 country_events">
      </xpath>
  </template>
  
- <template id="event_left_column" optional="enabled" inherit_id="website_event.index" name="Filters">
+ <template id="event_left_column" customize_show="True" inherit_id="website_event.index" name="Filters">
      <xpath expr="//div[@id='middle_column']" position="attributes">
          <attribute name="class">col-md-6</attribute>
      </xpath>
      </xpath>
  </template>
  
- <template id="event_category" inherit_id="website_event.event_left_column" optional="disabled" name="Filter by Category">
+ <template id="event_category" inherit_id="website_event.event_left_column" active="False" customize_show="True" name="Filter by Category">
      <xpath expr="//div[@id='left_column']" position="inside">
          <ul class="nav nav-pills nav-stacked mt32">
              <t t-foreach="types" t-as="type">
      </xpath>
  </template>
  
- <template id="event_location" inherit_id="website_event.event_left_column" optional="disabled" name="Filter by Country">
+ <template id="event_location" inherit_id="website_event.event_left_column" active="False" customize_show="True" name="Filter by Country">
      <xpath expr="//div[@id='left_column']" position="inside">
          <ul class="nav nav-pills nav-stacked mt32">
              <t t-foreach="countries" t-as="country">
                  <li t-if="country['country_id']" t-att-class="searches.get('country') == str(country['country_id'] and country['country_id'][0]) and 'active' or ''">
-                     <a t-attf-href="/event?{{ keep_query('type', 'data', country=country['country_id'][0]) }}"><t t-esc="country['country_id'][1]"/>
+                     <a t-attf-href="/event?{{ keep_query('type', 'date', country=country['country_id'][0]) }}"><t t-esc="country['country_id'][1]"/>
+                         <span class="badge pull-right"><t t-esc="country['country_id_count']"/></span>
+                     </a>
+                 </li>
+                 <li t-if="not country['country_id']" t-att-class="searches.get('country') == 'online' and 'active' or ''">
+                     <a t-attf-href="/event?{{ keep_query('type', 'date', country='online') }}">
+                         <span>Online Events</span>
                          <span class="badge pull-right"><t t-esc="country['country_id_count']"/></span>
                      </a>
                  </li>
  
  <template id="template_location">
      <t t-call="website_event.layout">
 -        <section class="container" data-snippet-id="title">
 +        <section class="container">
              <h1 class="text-center">Event Location</h1>
          </section>
          <div class="oe_structure oe_empty"/>
  
  <template id="template_intro">
      <t t-call="website_event.layout">
 -        <section class="container" data-snippet-id="title">
 +        <section class="container">
              <h1 class="text-center">Event Introduction</h1>
          </section>
          <div class="oe_structure oe_empty"/>
          </div>
      </xpath>
      <xpath expr="//div[@id='snippet_options']" position="inside">
 -        <div data-snippet-option-id='country_events'
 +        <div data-js='country_events'
              data-selector=".oe_country_events"
              data-selector-siblings="p, h1, h2, h3, blockquote, .well, .panel"
              data-selector-children=".content"
              <div class="country_events_list">
                  <div>
                      <t t-if="country">
-                         <img class="img-rounded img-responsive" t-att-src="'/website/image?model=res.country&amp;field=image&amp;id='+str(country.id)"></img>
+                         <img class="img-rounded img-responsive" t-attf-src="/website/image/res.country/{{ country.id }}/image"></img>
                          <h4><b>Events: <span t-esc="country.name"></span></b></h4>
                      </t>
                      <t t-if="not country">
      </t>
  </template>
  
+ <template id="website.layout_footer_copyright" inherit_id="website.layout" name="Footer Copyright">
+   <xpath expr="//footer" position="inside">
+       <div class="container mt16 mb8">
+           <div class="pull-right" t-ignore="true" t-if="not editable">
+             Powered by <a class="label label-danger" href="http://www.odoo.com/page/website-builder">Odoo</a>.
+             Try the <a href="http://www.odoo.com/page/website-builder">open source website builder</a>.
+           </div>
+           <div class="pull-left text-muted">
+               Copyright &amp;copy; <span t-field="res_company.name">Company name</span>
+           </div>
+       </div>
+   </xpath>
+ </template>
  <!-- User Navbar -->
  <template id="content_new_event" inherit_id="website.user_navbar" groups="event.group_event_manager">
      <xpath expr="//ul[@id='oe_systray']/li/ul[@class='dropdown-menu oe_content_menu']" position="inside">
@@@ -2,20 -2,20 +2,20 @@@
  <openerp>
  <data>
  
 -<template name="Sponsors" id="event_sponsor" customize_show="True" inherit_id="website_event.layout">
 -    <xpath expr="//t[@t-call='website.layout']" position="inside">
 -        <t t-set="head">
 -            <link rel='stylesheet' href='/website_event_track/static/src/css/website_event_track.css'/>
 -            <t t-raw="head or ''"/>
 -        </t>
 +<template id="assets_frontend" inherit_id="website.assets_frontend" name="Website Event Track Assets">
 +    <xpath expr="." position="inside">
 +        <script type="text/javascript" src="/website_event_track/static/src/js/website_event_track.js"></script>
      </xpath>
 +</template>
 +
- <template name="Sponsors" id="event_sponsor" optional="enabled" inherit_id="website_event.layout">
++<template name="Sponsors" id="event_sponsor" customize_show="True" inherit_id="website_event.layout">
      <xpath expr="//div[@id='wrap']" position="inside">
          <div class="container mt32 mb16 hidden-print" t-if="event.sponsor_ids">
 -            <section data-snippet-id="title">
 +            <section>
                  <h2 class="text-center mb32">Our Sponsors</h2>
              </section>
              <div class="row">
 -                <div t-attf-class="col-md-#{(len(event.sponsor_ids) > 6) and 2 or (12/ len(event.sponsor_ids))} text-center mb16" t-foreach="event.sponsor_ids" t-as="sponsor">
 +                <div t-attf-class="col-md-#{(len(event.sponsor_ids) > 6) and 2 or (12/ len(event.sponsor_ids))} text-center mb16 oe_sponsor" t-foreach="event.sponsor_ids" t-as="sponsor">
                      <t t-if="sponsor.url">
                          <a t-att-href="sponsor.url" style="position: relative; display: inline-block;">
                              <span t-field="sponsor.image_medium"
  
  <template id="agenda">
      <t t-call="website_event.layout">
 -        <t t-set="head">
 -          <script type="text/javascript" src="/website_event_track/static/src/js/website_event_track.js"></script>
 -          <t t-raw="head or ''"/>
 -        </t>
          <section class="container">
              <h1 class="text-center" t-field="event.name"/>
              <div class="form-inline pull-right">
      </t>
  </template>
  
- <template id="tracks_filter" inherit_id="website_event_track.tracks" optional="enabled" name="Filter on Tags">
+ <template id="tracks_filter" inherit_id="website_event_track.tracks" customize_show="True" name="Filter on Tags">
      <xpath expr="//div[@id='left_column']" position="inside">
          <ul class="nav nav-pills nav-stacked">
              <li t-att-class="'' if searches.get('tag') else 'active'"><a t-attf-href="/event/#{ slug(event) }/track">All Tags</a></li>
      </t>
  </template>
  
- <template id="event_track_social" name="Social Widgets" inherit_id="website_event_track.track_view" optional="disabled">
+ <template id="event_track_social" name="Social Widgets" inherit_id="website_event_track.track_view" active="False" customize_show="True">
      <xpath expr="//div[@id='right_column']" position="inside">
          <div class="panel panel-default">
              <div class="panel-heading">
      <t t-call="website_event.layout">
          <div class="oe_structure"/>
          <div class="container">
 -            <section data-snippet-id="title">
 +            <section>
                  <h1 class="text-center mb0">Call for Proposals</h1>
                  <h2 class="text-center text-muted mb32" t-esc="event.name"></h2>
              </section>
              <section class="row">
                  <div class="col-md-8">
                      <div class="oe_structure">
 -                        <section data-snippet-id="text">
 +                        <section>
                              <h3 class="page-header mt16">
                                  Introduction
                              </h3>
                                  inclusion in the programme.
                              </p>
                          </section>
 -                        <section data-snippet-id="text">
 +                        <section>
                              <h3 class="page-header">Application</h3>
                              <p>
                                  Fill this form to propose your talk.
@@@ -315,9 -315,6 +315,9 @@@ class Post(osv.Model)
          create_context = dict(context, mail_create_nolog=True)
          post_id = super(Post, self).create(cr, uid, vals, context=create_context)
          post = self.browse(cr, SUPERUSER_ID, post_id, context=context)  # SUPERUSER_ID to avoid read access rights issues when creating
 +        # deleted or closed questions
 +        if post.parent_id and (post.parent_id.state == 'close' or post.parent_id.active == False):
 +            osv.except_osv(_('Error !'), _('Posting answer on [Deleted] or [Closed] question is prohibited'))
          # karma-based access
          if post.parent_id and not post.can_ask:
              raise KarmaError('Not enough karma to create a new question')
          """ Tools to convert an answer (forum.post) to a comment (mail.message).
          The original post is unlinked and a new comment is posted on the question
          using the post create_uid as the comment's author. """
-         post = self.browse(cr, uid, id, context=context)
+         post = self.browse(cr, SUPERUSER_ID, id, context=context)
          if not post.parent_id:
              return False
  
@@@ -529,6 -526,10 +529,10 @@@ class Vote(osv.Model)
          'user_id': fields.many2one('res.users', 'User', required=True),
          'vote': fields.selection([('1', '1'), ('-1', '-1'), ('0', '0')], 'Vote', required=True),
          'create_date': fields.datetime('Create Date', select=True, readonly=True),
+         # TODO master: store these two
+         'forum_id': fields.related('post_id', 'forum_id', type='many2one', relation='forum.forum', string='Forum'),
+         'recipient_id': fields.related('post_id', 'create_uid', type='many2one', relation='res.users', string='To', help="The user receiving the vote"),
      }
      _defaults = {
          'user_id': lambda self, cr, uid, ctx: uid,
          vote_id = super(Vote, self).create(cr, uid, vals, context=context)
          vote = self.browse(cr, uid, vote_id, context=context)
          if vote.post_id.parent_id:
-             karma_value = self._get_karma_value('0', vote.vote, vote.post_id.forum_id.karma_gen_answer_upvote, vote.post_id.forum_id.karma_gen_answer_downvote)
+             karma_value = self._get_karma_value('0', vote.vote, vote.forum_id.karma_gen_answer_upvote, vote.forum_id.karma_gen_answer_downvote)
          else:
-             karma_value = self._get_karma_value('0', vote.vote, vote.post_id.forum_id.karma_gen_question_upvote, vote.post_id.forum_id.karma_gen_question_downvote)
-         self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.post_id.create_uid.id], karma_value, context=context)
+             karma_value = self._get_karma_value('0', vote.vote, vote.forum_id.karma_gen_question_upvote, vote.forum_id.karma_gen_question_downvote)
+         self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.recipient_id.id], karma_value, context=context)
          return vote_id
  
      def write(self, cr, uid, ids, values, context=None):
          if 'vote' in values:
              for vote in self.browse(cr, uid, ids, context=context):
                  if vote.post_id.parent_id:
-                     karma_value = self._get_karma_value(vote.vote, values['vote'], vote.post_id.forum_id.karma_gen_answer_upvote, vote.post_id.forum_id.karma_gen_answer_downvote)
+                     karma_value = self._get_karma_value(vote.vote, values['vote'], vote.forum_id.karma_gen_answer_upvote, vote.forum_id.karma_gen_answer_downvote)
                  else:
-                     karma_value = self._get_karma_value(vote.vote, values['vote'], vote.post_id.forum_id.karma_gen_question_upvote, vote.post_id.forum_id.karma_gen_question_downvote)
-                 self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.post_id.create_uid.id], karma_value, context=context)
+                     karma_value = self._get_karma_value(vote.vote, values['vote'], vote.forum_id.karma_gen_question_upvote, vote.forum_id.karma_gen_question_downvote)
+                 self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.recipient_id.id], karma_value, context=context)
          res = super(Vote, self).write(cr, uid, ids, values, context=context)
          return res
  
@@@ -2,29 -2,15 +2,28 @@@
  <openerp>
      <data>
  
- <!-- Editor custo -->
+ <!-- Editor custom -->
 -<template id="assets_frontend" inherit_id="website.assets_frontend" name="website_forum assets">
 -    <xpath expr="/t" position="inside">
 +<template id="assets_editor" inherit_id="website.assets_editor" name="Forum Editor Assets">
 +    <xpath expr="." position="inside">
          <link rel='stylesheet' href="/website_forum/static/src/css/website_forum.css"/>
          <script type="text/javascript" src="/website_forum/static/src/js/website.tour.forum.js"/>
          <script type="text/javascript" src="/website_forum/static/src/js/website_forum.editor.js"/>
      </xpath>
  </template>
  
 +<template id="assets_forum" name="Forum Assets">
 +    <link rel='stylesheet' href="/web/static/lib/jquery.textext/jquery.textext.css"/>
 +    <link rel='stylesheet' href='/website_forum/static/src/css/website_forum.css'/>
 +
 +    <script type="text/javascript" src="/website_forum/static/src/js/website_forum.js"/>
 +    <script type="text/javascript" src="/web/static/lib/jquery.textext/jquery.textext.js"/>
 +    <script type="text/javascript" src="/web/static/lib/ckeditor/ckeditor.js"/>
 +    <script type="text/javascript">
 +        CKEDITOR.config.toolbar = [['Bold','Italic','Underline','Strike'],['NumberedList','BulletedList', 'Blockquote']
 +        ,['Outdent','Indent','Link','Unlink','Image'],] ;
 +    </script>
 +</template>
 +
  <!-- Layout add nav and footer -->
  <template id="header_footer_custom" inherit_id="website.footer_default"
      name="Footer Questions Link">
      </form>
  </template>
  
 -<template id="assets_frontend" inherit_id="website.assets_frontend" name="website_forum assets">
 -    <xpath expr="." position="inside">
 -        <link rel='stylesheet' href="/web/static/lib/jquery.textext/jquery.textext.css"/>
 -        <link rel='stylesheet' href='/website_forum/static/src/css/website_forum.css'/>
 -        <script type="text/javascript" src="/website_forum/static/src/js/website_forum.js"/>
 -        <script type="text/javascript" src="/web/static/lib/jquery.textext/jquery.textext.js"/>
 -    </xpath>
 -</template>
 -
  <!-- Page Index -->
  <template id="header" name="Forum Index">
      <t t-call="website.layout">
          <t t-set="head">
 -            <script type="text/javascript" src="/web/static/lib/ckeditor/ckeditor.js"/>
 -            <script type="text/javascript">
 -                CKEDITOR.config.toolbar = [['Bold','Italic','Underline','Strike'],['NumberedList','BulletedList', 'Blockquote']
 -                ,['Outdent','Indent','Link','Unlink','Image'],] ;
 -            </script>
 +            <t t-call-assets="website_forum.assets_forum"/>
+             <t t-raw="0"/>
          </t>
-         <div class="container mt16">
+         <div class="container mt16 website_forum">
              <div class="navbar navbar-default">
                  <div class="navbar-header">
                      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#oe-help-navbar-collapse">
@@@ -76,7 -76,7 +76,7 @@@
                              <a t-attf-href="/forum/#{ slug(forum) }/badge">Badges</a>
                          </li>
                          <li t-if="not is_public_user" t-att-class="searches.get('my_profile') and 'active' or '' ">
-                             <a t-attf-href="/forum/#{ slug(forum) }/user/#{user.id}">My Profile</a>
+                             <a t-attf-href="/forum/#{ slug(forum) }/user/#{uid}">My Profile</a>
                          </li>
                      </ul>
                      <form class="navbar-form navbar-right" role="search" t-attf-action="/forum/#{ slug(forum) }" method="get">
  
  <!-- Display a post -->
  <template id="display_post">
-     <div class="question clearfix">
+     <div class="question row">
          <div class="col-md-2 hidden-xs text-center">
              <div t-attf-class="box #{question.is_correct and 'oe_green' or 'oe_grey'} #{(question.child_count == 0) and 'text-muted' or ''}">
                  <span t-esc="question.child_count"/>
  <!-- Edition: ask your question -->
  <template id="ask_question">
      <t t-call="website_forum.header">
 -        <h1 class="mt0">Ask your Question</h1>
 +        <h1 class="mt0">Ask Your Question</h1>
 +        <p>
 +            To improve your chance getting an answer:
 +        </p>
          <ul>
 -            <li> please, try to make your question interesting to others </li>
 -            <li> provide enough details and, if possible, give an example </li>
 -            <li> be clear and concise, avoid unnecessary introductions (Hi, ... Thanks...) </li>
 +            <li>Set a clear, explicit and concise question title
 +                (check
 +                <a href="#" data-placement="top" data-toggle="popover" data-content="Inventory Date Problem, Task remaining hours, Can you help solve solve my tax computation problem in Canada?" title="Click to get bad question samples">bad examples</a>
 +                and
 +                <a href="#" data-placement="bottom" data-toggle="popover" data-content="How to create a physical inventory at an anterior date?, How is the 'remaining hours' field computed on tasks?, How to configure TPS and TVQ's canadian taxes?" title="Click to get good question titles">good examples</a>
 +                ),
 +            </li>
 +            <li>Avoid unnecessary introductions (Hi,... Please... Thanks...),</li>
 +            <li>Provide enough details and, if possible, give an example.</li>
          </ul>
 +        <script type="text/javascript">
 +            $(function () {
 +                $("[data-toggle='popover']").popover();
 +            });
 +        </script>
          <form t-attf-action="/forum/#{ slug(forum) }/question/new" method="post" role="form" class="tag_text">
              <input type="text" name="question_name" required="True" t-attf-value="#{question_name}"
 -                class="form-control" placeholder="Enter your Question"/>
 -            <h5 class="mt20">Please enter a descriptive question (should finish with a '?')</h5>
 +                class="form-control mb16" placeholder="Your Question Title..."/>
              <input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
              <textarea name="content" required="True" class="form-control load_editor">
                  <t t-esc="question_content"/>
      <form t-attf-action="/forum/#{ slug(forum) }/post/#{slug(question)}/new" method="post" role="form">
          <input type="hidden" name="karma" t-attf-value="#{user.karma}" id="karma"/>
          <textarea name="content" class="form-control load_editor" required="True"/>
-         <button class="btn btn-primary" id="btn_ask_your_question">Post Your Answer</button>
+         <button class="btn btn-primary mt16" id="btn_ask_your_question">Post Your Answer</button>
      </form>
      <script type="text/javascript">
          CKEDITOR.replace("content");
  <!-- Specific Post Layout -->
  <template id="post_description_full" name="Question Navigation">
      <t t-call="website_forum.header">
-         <div t-attf-class="question">
 -        <div t-attf-class="row question #{not question.active and 'alert alert-danger' or ''}">
++        <div t-attf-class="row question">
              <div class="col-md-2 hidden-xs text-center">
                  <t t-call="website_forum.vote">
                      <t t-set="post" t-value="question"/>
                          t-attf-class="favourite_question no-decoration fa fa-2x fa-star #{question.user_favourite and 'forum_favourite_question' or ''}"/>
                  </div>
              </div>
 -            <div class="col-md-10">
 +            <div t-attf-class="col-md-10 #{not question.active and 'alert alert-danger' or ''}">
                  <h1 class="mt0">
                      <a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
                      <span t-if="not question.active"><b> [Deleted]</b></span>
  
          <div t-foreach="question.child_ids" t-as="answer" class="mt16 mb32">
              <a t-attf-id="answer-#{str(answer.id)}"/>
-             <div t-attf-class="forum_answer" t-attf-id="answer_#{answer.id}" >
+             <div t-attf-class="forum_answer row" t-attf-id="answer_#{answer.id}" >
                  <div class="col-md-2 hidden-xs text-center">
                      <t t-call="website_forum.vote">
                          <t t-set="post" t-value="answer"/>
                  </div>
              </div>
          </div>
 -        <div t-if="not question.uid_has_answered">
 +        <div t-if="not question.uid_has_answered and question.state != 'close' and question.active != False">
              <t t-call="website_forum.post_answer"/>
          </div>
          <div t-if="question.uid_has_answered" class="mb16">
          <div class="col-sm-10 col-sm-offset-2">
              <div t-foreach="reversed(object.website_message_ids)" t-as="message" class="comment oe_comment_grey">
                  <small class="text-muted">
-                     <button type="button" t-if="user.partner_id.id == message.author_id.id and user.karma&gt;=750"
-                         t-attf-data-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete"
-                         class="close comment_delete">&amp;times;</button>
+                     <t t-set="required_karma" t-value="message.author_id.id == user.partner_id.id and object.forum_id.karma_comment_unlink_own or object.forum_id.karma_comment_unlink_all"/>
+                     <t t-call="website_forum.link_button">
+                         <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/delete'"/>
+                         <t t-set="label" t-value="' '"/>
+                         <t t-set="karma" t-value="user.karma&lt;required_karma and required_karma or 0"/>
+                         <t t-set="classes" t-value="'close comment_delete fa-times'"/>
+                     </t>
                      <span t-field="message.body"/>
                      <t t-set="required_karma" t-value="message.author_id.id == user.partner_id.id and object.forum_id.karma_comment_convert_own or object.forum_id.karma_comment_convert_all"/>
 -                    <t t-call="website_forum.link_button">
 -                        <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/convert_to_answer'"/>
 -                        <t t-set="label" t-value="'Convert as an answer'"/>
 -                        <t t-set="karma" t-value="user.karma&lt;required_karma and required_karma or 0"/>
 -                        <t t-set="classes" t-value="'fa-magic pull-right'"/>
 +                    <t t-if="(object.parent_id and object.parent_id.state != 'close' and object.parent_id.active != False) or (not object.parent_id and object.state != 'close' and object.active != False)">
 +                        <t t-set="allow_post_comment" t-value="True" />
 +                    </t>
 +                    <t t-if="allow_post_comment">
 +                        <t t-call="website_forum.link_button" >
 +                            <t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/convert_to_answer'"/>
 +                            <t t-set="label" t-value="'Convert as an answer'"/>
 +                            <t t-set="karma" t-value="user.karma&lt;required_karma and required_karma or 0"/>
 +                            <t t-set="classes" t-value="'fa-magic pull-right'"/>
 +                        </t>
                      </t>
                      <a t-attf-href="/forum/#{slug(forum)}/partner/#{message.author_id.id}"
                          t-field="message.author_id" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
  
  <template id="edit_profile">
      <t t-call="website_forum.header">
-         <h3>Edit Profile
-             <t t-call="website.publish_management">
-               <t t-set="object" t-value="user"/>
-             </t>
-         </h3>
-         <div class="col-md-2">
-             <img class="img img-responsive img-circle" t-attf-src="/forum/user/#{user.id}/avatar"/>
-         </div>
-         <form t-attf-action="/forum/#{slug(forum)}/user/#{slug(user)}/save" method="post" role="form" class="form-horizontal">
-             <input name="user_id" t-att-value="user.id" type="hidden"/>
-             <label class="col-md-2 control-label mb16" for="user.name">Real name</label>
-             <div class="col-md-7 mb16">
-                 <input type="text" class="col-md-7 mb16 form-control" name="name" required="True" t-attf-value="#{user.name}"/>
-             </div> 
-             <label class="col-md-2 control-label mb16" for="user.partner_id.website">Website</label>
-             <div class="col-md-7 mb16">
-                 <input type="text" class="form-control" name="website" t-attf-value="#{user.partner_id.website or ''}"/>
-             </div>
-             <label class="col-md-4 control-label mb16" for="user.partner_id.email">Email</label>
-             <div class="col-md-7 mb16">
-                 <input type="text" class="form-control" name="email" required="True" t-attf-value="#{user.partner_id.email}"/>
-             </div>
-             <label class="col-md-4 control-label mb16" for="user.partner_id.city">City</label>
-             <div class="col-md-7  mb16">
-                 <input type="text" class="form-control" name="city" t-attf-value="#{user.partner_id.city or ''}"/>
-             </div>
-             <label class="col-md-4 control-label mb16" for="contact_name">Country</label>
-             <div class="col-md-7 mb16">
-                 <select class="form-control" name="country">
-                     <option value="">Country...</option>
-                     <t t-foreach="countries or []" t-as="country">
-                         <option t-att-value="country.id" t-att-selected="country.id == user.partner_id.country_id.id"><t t-esc="country.name"/></option>
-                     </t>
-                  </select>
-             </div>
-             <!--Note: using website_description fiels instead of using commnt firld of partner-->
-             <label class="col-md-4 control-label mb16" for="user.partner_id.website_description">Biography</label>
-             <div class="col-md-7 mb16">
-                 <textarea name="description" style="min-height: 120px" required="True" 
-                     class="form-control"><t t-esc="user.partner_id.website_description"/></textarea>
+         <h3>Edit Profile</h3>
+         <div class="row">
+             <div class="col-md-2">
+                 <img class="img img-responsive img-circle" t-attf-src="/forum/user/#{user.id}/avatar"/>
              </div>
-             <div class="col-sm-offset-4 col-md-4 mb16">
-                 <button class="btn btn-primary btn-lg">Update</button>
+             <div class="col-md-10">
+             <form t-attf-action="/forum/#{slug(forum)}/user/#{slug(user)}/save" method="post" role="form" class="form-horizontal">
+                 <input name="user_id" t-att-value="user.id" type="hidden"/>
+                 <div class="form-group">
+                     <label class="col-md-2 control-label mb16" for="user_name">Real name</label>
+                     <div class="col-md-8 mb16">
+                         <input type="text" class="form-control" name="name" id="user_name" required="True" t-attf-value="#{user.name}"/>
+                     </div>
+                 </div>
+                 <div class="form-group">
+                     <label class="col-md-2 control-label mb16" for="user_website_published" t-if="user.id == uid">Public profile</label>
+                     <div class="col-md-8 mb16" t-if="user.id == uid">
+                         <input type="checkbox" class="mt8" name="website_published" id="user_website_published" value="True" t-if="not user.website_published"/>
+                         <input type="checkbox" class="mt8" name="website_published" id="user_website_published" value="True" checked="checked" t-if="user.website_published"/>
+                     </div>
+                 </div>
+                 <div class="form-group">
+                     <label class="col-md-2 control-label mb16" for="user_website">Website</label>
+                     <div class="col-md-8 mb16">
+                         <input type="text" class="form-control" name="website" id="user_website" t-attf-value="#{user.partner_id.website or ''}"/>
+                     </div>
+                 </div>
+                 <div class="form-group">
+                     <div t-if="email_required" class="alert alert-danger alert-dismissable oe_forum_email_required">
+                         <button type="button" class="close" data-dismiss="alert">x</button>
+                         <p>Please enter a valid email address in order to receive notifications from answers or comments.</p>
+                     </div>
+                     <label class="col-md-2 control-label mb16" for="user_email">Email</label>
+                     <div class="col-md-8 mb16">
+                         <input type="text" class="form-control" name="email" id="user_email" required="True" t-attf-value="#{user.partner_id.email}"/>
+                     </div>
+                 </div>
+                 <div class="form-group">
+                     <label class="col-md-2 control-label mb16" for="user_city">City</label>
+                     <div class="col-md-8 mb16">
+                         <input type="text" class="form-control" name="city" id="user_city" t-attf-value="#{user.partner_id.city or ''}"/>
+                     </div>
+                 </div>
+                 <div class="form-group">
+                     <label class="col-md-2 control-label mb16">Country</label>
+                     <div class="col-md-8 mb16">
+                         <select class="form-control" name="country">
+                             <option value="">Country...</option>
+                             <t t-foreach="countries or []" t-as="country">
+                                 <option t-att-value="country.id" t-att-selected="country.id == user.partner_id.country_id.id"><t t-esc="country.name"/></option>
+                             </t>
+                          </select>
+                     </div>
+                 </div>
+                 <!--Note: using website_description fiels instead of using commnt firld of partner-->
+                 <div class="form-group">
+                     <label class="col-md-2 control-label mb16" for="description">Biography</label>
+                     <div class="col-md-8 mb16">
+                         <textarea name="description" id="description" style="min-height: 120px" required="True" 
+                             class="form-control load_editor"><t t-esc="user.partner_id.website_description"/></textarea>
+                     </div>
+                 </div>
+                 <div class="col-sm-offset-2 col-md-2 mb16">
+                     <button class="btn btn-primary btn-lg">Update</button>
+                 </div>
+             </form>
              </div>
-         </form>
+         </div>
+         <script type="text/javascript">
+             CKEDITOR.replace("description");
+         </script>
      </t>
  </template>
  
  
          <ul class="nav nav-tabs">
              <li class="active">
-                 <a href="#questions" data-toggle="tab"><t t-esc="len(questions)"/> Questions</a>
+                 <a href="#questions" data-toggle="tab"><t t-esc="count_questions"/> Questions</a>
              </li>
              <li>
-                 <a href="#answers" data-toggle="tab"><t t-esc="len(answers)"/> Answers</a>
+                 <a href="#answers" data-toggle="tab"><t t-esc="count_answers"/> Answers</a>
              </li>
              <li t-if="uid == user.id">
                  <a href="#favourite_question" data-toggle="tab"><t t-esc="len(favourite)"/> Favourite Questions</a>
              <div class="tab-pane" id="badges">
                  <t t-call="website_forum.user_badges"/>
              </div>
-             <div class="tab-pane" id="favourite_question">
+             <div class="tab-pane" id="favourite_question" t-if="uid == user.id">
                  <div t-foreach="favourite" t-as="question">
                      <t t-call="website_forum.display_post"/>
                  </div>
              </div>
-             <div class="tab-pane" id="followed_question">
+             <div class="tab-pane" id="followed_question" t-if="uid == user.id">
                  <div t-foreach="followed" t-as="question" class="mb16">
                      <t t-call="website_forum.display_post"/>
                  </div>
              </div>
-             <div class="tab-pane" id="votes">
+             <div class="tab-pane" id="votes" t-if="uid == user.id">
                  <t t-call="website_forum.user_votes"/>
              </div>
-             <div class="tab-pane" id="activity">
+             <div class="tab-pane" id="activity" t-if="uid == user.id">
                  <ul class="list-unstyled">
                      <li t-foreach="activities" t-as="activity">
                          <span t-field="activity.date" t-field-options='{"format": "short"}'/>
      <t t-call="website.layout">
          <div id="wrap">
              <div class="oe_structure">
 -                <section data-snippet-id="text-block" class="mb16">
 +                <section class="mb16">
                      <div class="container">
                          <div class="row">
 -                            <div class="col-md-12 text-center mb16" data-snippet-id="colmd">
 +                            <div class="col-md-12 text-center mb16">
                                  <h2>Our Job Offers</h2>
                                  <h3 class="text-muted">Join us and help disrupt the enterprise market!</h3>
                              </div>
 -                            <div class="col-md-12" data-snippet-id="colmd">
 +                            <div class="col-md-12">
                                  <p>
                                      With a small team of smart people, we released the most
                                      disruptive enterprise management software in the world.
          <div t-field="job.website_description"/>
  
          <div class="oe_structure">
 -            <section data-snippet-id="cta" class="mt16 mb16">
 +            <section class="mt16 mb16">
                  <div class="container">
                      <div class="row">
                          <div class="col-md-12 text-center mt16 mb16">
                          we will get back to you soon.
                      </p>
                  </div>
 -                <section data-snippet-id="cta" class="oe_dark">
 +                <section class="oe_dark">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-12 text-center mt16 mb16">
      </t>
  </template>
  
- <template id="job_countries" inherit_id="website_hr_recruitment.index" optional="disabled" name="Filter by Countries">
+ <template id="job_countries" inherit_id="website_hr_recruitment.index" active="False" customize_show="True" name="Filter by Countries">
      <xpath expr="//div[@id='jobs_grid_left']" position="inside">
          <ul class="nav nav-pills nav-stacked mb32">
              <li t-att-class=" '' if country_id else 'active' "><a t-attf-href="/jobs#{ '/department/%s' % slug(department_id) if department_id else '' }#{ '/office/%s' % office_id if office_id else '' }">All Countries</a></li>
      </xpath>
  </template>
  
- <template id="job_departments" inherit_id="website_hr_recruitment.index" optional="disabled" name="Filter by Departments">
+ <template id="job_departments" inherit_id="website_hr_recruitment.index" active="False" customize_show="True" name="Filter by Departments">
      <xpath expr="//div[@id='jobs_grid_left']" position="inside">
          <ul class="nav nav-pills nav-stacked mb32">
              <li t-att-class=" '' if department_id else 'active' "><a t-attf-href="/jobs#{ '/country/%s' % slug(country_id) if country_id else '' }#{ '/office/%s' % office_id if office_id else '' }">All Departments</a></li>
      </xpath>
  </template>
  
- <template id="job_offices" inherit_id="website_hr_recruitment.index" optional="disabled" name="Filter by Offices">
+ <template id="job_offices" inherit_id="website_hr_recruitment.index" active="False" customize_show="True" name="Filter by Offices">
      <xpath expr="//div[@id='jobs_grid_left']" position="inside">
          <ul class="nav nav-pills nav-stacked mb32">
              <li t-att-class=" '' if office_id else 'active' "><a t-attf-href="/jobs#{ '/country/%s' % slug(country_id) if country_id else '' }#{ '/department/%s' % slug(department_id) if department_id else '' }">All Offices</a></li>
@@@ -14,7 -14,7 +14,7 @@@
          <t t-set="additional_title">Members</t>
          <div id="wrap">
              <div class="oe_structure">
 -                <section data-snippet-id="title">
 +                <section>
                      <div class="container">
                          <div class="row">
                              <div class="col-md-12">
@@@ -84,7 -84,7 +84,7 @@@
  </template>
  
  <template id="opt_index_country" name="Location"
-         optional="enabled" inherit_id="website_membership.index">
+         customize_show="True" inherit_id="website_membership.index">
      <xpath expr="//div[@id='left_column']/ul[1]" position="after">
          <ul class="nav nav-pills nav-stacked mt16">
              <li class="nav-header"><h3>Location</h3></li>
  
  <!-- Option: index: Left Google Map -->
  <template id="opt_index_google_map" name="Left World Map"
-         optional="enabled" inherit_id="website_membership.index">
+         customize_show="True" inherit_id="website_membership.index">
      <xpath expr="//div[@id='left_column']/ul[last()]" position="after">
          <!-- modal for large map -->
          <div class="modal fade partner_map_modal" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
@@@ -10,21 -10,9 +10,21 @@@ class WebsiteResPartner(osv.Model)
      def _get_ids(self, cr, uid, ids, flds, args, context=None):
          return {i: i for i in ids}
  
 +    def _set_private(self, cr, uid, ids, field_name, value, arg, context=None):
 +        return self.write(cr, uid, ids, {'website_published': not value}, context=context)
 +
 +    def _get_private(self, cr, uid, ids, field_name, arg, context=None):
 +        return dict((rec.id, not rec.website_published) for rec in self.browse(cr, uid, ids, context=context))
 +
 +    def _search_private(self, cr, uid, obj, name, args, context=None):
 +        return [('website_published', '=', not args[0][2])]
 +
      _columns = {
          'website_published': fields.boolean(
              'Publish', help="Publish on the website", copy=False),
 +        'website_private': fields.function(
 +            _get_private, fnct_inv=_set_private, fnct_search=_search_private,
 +            type='boolean', string='Private Profile'),
          'website_description': fields.html(
              'Website Partner Full Description'
          ),
@@@ -36,5 -24,5 +36,5 @@@
      }
  
      _defaults = {
-         'website_published': False
+         'website_published': True
      }
@@@ -2,6 -2,7 +2,7 @@@
      'name': 'Online Proposals',
      'category': 'Website',
      'summary': 'Send Professional Quotations',
+     'website': 'https://www.odoo.com/page/quote-builder',
      'version': '1.0',
      'description': """
  OpenERP Sale Quote Roller
      'data': [
          'views/website_quotation.xml',
          'views/website_quotation_backend.xml',
+         'views/report_saleorder.xml',
          'data/website_quotation_data.xml',
          'security/ir.model.access.csv',
 +        'data/quotation_tip_data.xml',
      ],
      'demo': [
          'data/website_quotation_demo.xml'
@@@ -51,7 -51,7 +51,7 @@@ class sale_quote_line(osv.osv)
          'quote_id': fields.many2one('sale.quote.template', 'Quotation Template Reference', required=True, ondelete='cascade', select=True),
          'name': fields.text('Description', required=True, translate=True),
          'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True),
-         'website_description': fields.html('Line Description', translate=True),
+         'website_description': fields.related('product_id', 'product_tmpl_id', 'quote_description', string='Line Description', type='html', translate=True),
          'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
          'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
          'product_uom_qty': fields.float('Quantity', required=True, digits_compute= dp.get_precision('Product UoS')),
      def on_change_product_id(self, cr, uid, ids, product, context=None):
          vals = {}
          product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
+         name = product_obj.name
+         if product_obj.description_sale:
+             name += '\n' + product_obj.description_sale
          vals.update({
              'price_unit': product_obj.list_price,
              'product_uom_id': product_obj.uom_id.id,
-             'website_description': product_obj.website_description,
-             'name': product_obj.name,
+             'website_description': product_obj and (product_obj.quote_description or product_obj.website_description) or '',
+             'name': name,
          })
          return {'value': vals}
  
+     def _inject_quote_description(self, cr, uid, values, context=None):
+         values = dict(values or {})
+         if not values.get('website_description') and values.get('product_id'):
+             product = self.pool['product.product'].browse(cr, uid, values['product_id'], context=context)
+             values['website_description'] = product.quote_description or product.website_description or ''
+         return values
+     def create(self, cr, uid, values, context=None):
+         values = self._inject_quote_description(cr, uid, values, context)
+         ret = super(sale_quote_line, self).create(cr, uid, values, context=context)
+         # hack because create don t make the job for a related field
+         if values.get('website_description'):
+             self.write(cr, uid, ret, {'website_description': values['website_description']}, context=context)
+         return ret
+     def write(self, cr, uid, ids, values, context=None):
+         values = self._inject_quote_description(cr, uid, values, context)
+         return super(sale_quote_line, self).write(cr, uid, ids, values, context=context)
  
  class sale_order_line(osv.osv):
      _inherit = "sale.order.line"
          'option_line_id': fields.one2many('sale.order.option', 'line_id', 'Optional Products Lines'),
      }
  
-     def _inject_website_description(self, cr, uid, values, context=None):
+     def _inject_quote_description(self, cr, uid, values, context=None):
          values = dict(values or {})
          if not values.get('website_description') and values.get('product_id'):
              product = self.pool['product.product'].browse(cr, uid, values['product_id'], context=context)
-             values['website_description'] = product.website_description
+             values['website_description'] = product.quote_description or product.website_description
          return values
  
      def create(self, cr, uid, values, context=None):
-         values = self._inject_website_description(cr, uid, values, context)
-         return super(sale_order_line, self).create(cr, uid, values, context=context)
+         values = self._inject_quote_description(cr, uid, values, context)
+         ret = super(sale_order_line, self).create(cr, uid, values, context=context)
+         # hack because create don t make the job for a related field
+         if values.get('website_description'):
+             self.write(cr, uid, ret, {'website_description': values['website_description']}, context=context)
+         return ret
  
      def write(self, cr, uid, ids, values, context=None):
-         values = self._inject_website_description(cr, uid, values, context)
+         values = self._inject_quote_description(cr, uid, values, context)
          return super(sale_order_line, self).write(cr, uid, ids, values, context=context)
  
  
@@@ -111,23 -137,15 +137,23 @@@ class sale_order(osv.osv)
  
      _columns = {
          'access_token': fields.char('Security Token', required=True, copy=False),
 -        'template_id': fields.many2one('sale.quote.template', 'Quote Template'),
 +        'template_id': fields.many2one('sale.quote.template', 'Quote Template', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
          'website_description': fields.html('Description'),
 -        'options' : fields.one2many('sale.order.option', 'order_id', 'Optional Products Lines'),
 -        'validity_date': fields.date('Expiry Date'),
 +        'options' : fields.one2many('sale.order.option', 'order_id', 'Optional Products Lines', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
          'amount_undiscounted': fields.function(_get_total, string='Amount Before Discount', type="float",
              digits_compute=dp.get_precision('Account'))
      }
 +
 +    def _get_template_id(self, cr, uid, context=None):
 +        try:
 +            template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'website_quote', 'website_quote_template_default')[1]
 +        except ValueError:
 +            template_id = False
 +        return template_id
 +
      _defaults = {
 -        'access_token': lambda self, cr, uid, ctx={}: str(uuid.uuid4())
 +        'access_token': lambda self, cr, uid, ctx={}: str(uuid.uuid4()),
 +        'template_id' : _get_template_id,
      }
  
      def open_quotation(self, cr, uid, quote_id, context=None):
  
          if context is None:
              context = {}
 -        context = dict(context, lang=self.pool.get('res.partner').browse(cr, uid, partner, context).lang)
 -        
 +        if partner:
 +            context['lang'] = self.pool['res.partner'].browse(cr, uid, partner, context).lang
 +
          lines = [(5,)]
          quote_template = self.pool.get('sale.quote.template').browse(cr, uid, template_id, context=context)
          for line in quote_template.quote_line:
          for line in order_line:
              products += line.product_id.product_tmpl_id.recommended_products(context=context)
          return products
-         
+     def get_access_action(self, cr, uid, id, context=None):
+         """ Override method that generated the link to access the document. Instead
+         of the classic form view, redirect to the online quote if exists. """
+         quote = self.browse(cr, uid, id, context=context)
+         if not quote.template_id:
+             return super(sale_order, self).get_access_action(cr, uid, id, context=context)
+         return {
+             'type': 'ir.actions.act_url',
+             'url': '/quote/%s' % id,
+             'target': 'self',
+             'res_id': id,
+         }
  
  
  class sale_quote_option(osv.osv):
          product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
          vals.update({
              'price_unit': product_obj.list_price,
-             'website_description': product_obj.product_tmpl_id.website_description,
+             'website_description': product_obj.product_tmpl_id.quote_description,
              'name': product_obj.name,
              'uom_id': product_obj.product_tmpl_id.uom_id.id,
          })
@@@ -243,10 -272,12 +281,12 @@@ class sale_order_option(osv.osv)
      }
      def on_change_product_id(self, cr, uid, ids, product, context=None):
          vals = {}
+         if not product:
+             return vals
          product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
          vals.update({
              'price_unit': product_obj.list_price,
-             'website_description': product_obj.product_tmpl_id.website_description,
+             'website_description': product_obj and (product_obj.quote_description or product_obj.website_description),
              'name': product_obj.name,
              'uom_id': product_obj.product_tmpl_id.uom_id.id,
          })
  
  class product_template(osv.Model):
      _inherit = "product.template"
      _columns = {
-         'website_description': fields.html('Description for the website'),
+         'website_description': fields.html('Description for the website'), # hack, if website_sale is not installed
+         'quote_description': fields.html('Description for the quote'),
      }
@@@ -2,7 -2,7 +2,7 @@@
  <openerp>
  <data>
    <template id="pricing" name="Price">
 -      <section data-snippet-id="title">
 +      <section>
            <h1 class="page-header">Pricing</h1>
        </section>
        <section id="quote">
@@@ -92,7 -92,7 +92,7 @@@
        </section>
    </template>
  
-   <template id="change_quantity" inherit_id="website_quote.pricing" optional="disabled" name="Change Quantity">
+   <template id="change_quantity" inherit_id="website_quote.pricing" active="False" customize_show="True" name="Change Quantity">
        <xpath expr="//div[@id='quote_qty']" position="replace">
            <div class="input-group">
                <span class="input-group-addon hidden-print">
            <t t-foreach="quotation.message_ids" t-as="message">
                <li class="media" t-if="message.type &lt;&gt; 'comment' or message.subtype_id">
                    <div class="media-body">
-                       <img class="media-object pull-left" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(message.author_id.id)" style="width: 50px; margin-right: 10px;"/>
+                       <img class="media-object pull-left" t-attf-src="/website/image/res.partner/{{ message.author_id.id }}/image_small" style="width: 50px; margin-right: 10px;"/>
                        <div class="media-body">
                            <h5 class="media-heading">
                                <span t-field="message.author_id"/> <small>on <span t-field="message.date"/></small>
    </template>
  
    <!-- Options:Quotation Chatter: user can reply -->
-   <template id="opt_quotation_chatter_post_complete_comment" name="Allow Comments" optional="enabled" inherit_id="website_quote.chatter">
+   <template id="opt_quotation_chatter_post_complete_comment" name="Allow Comments" customize_show="True" inherit_id="website_quote.chatter">
        <xpath expr="//h1" position="after">
            <section class="mb32 css_editable_mode_hidden hidden-print">
                <form id="comment" t-attf-action="/quote/#{quotation.id}/#{quotation.access_token}/post" method="POST">
-                   <img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
+                   <img class="img pull-left img-rounded" t-attf-src="/website/image/res.partner/{{ user_id.partner_id.id }}/image_small" style="width: 50px; margin-right: 10px;"/>
                    <div class="pull-left mb32" style="width: 75%%">
                        <textarea rows="4" name="comment" class="form-control" placeholder="Send us a note..."></textarea>
                        <button type="submit" class="btn btn-primary mt8">Send</button>
              <link rel='stylesheet' href='/website_quote/static/src/css/website_quotation.css'/>
              <t t-raw="head or ''"/>
          </t>
-         <body data-spy="scroll" data-target=".navspy">
+         <body data-spy="scroll" data-target=".navspy" data-offset="50">
              <div class="container">
                <div class="row mt16">
                    <div class="col-md-3">
                            <em t-esc="quotation.name"/>
                            <small t-field="quotation.state"/>
                            <div groups="base.group_website_publisher" t-ignore="true" class="pull-right css_editable_mode_hidden">
 -                              <a class="btn btn-info hidden-print" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s' % (quotation._name, quotation.id)">Update Quote</a>
 +                              <a class="btn btn-info hidden-print" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s&amp;action=%s&amp;view_type=form' % (quotation._name, quotation.id, action)">Update Quote</a>
                            </div>
                        </h1>
  
                        </div>
  
                        <a id="offer"/>
 -                      <div t-field="quotation.website_description"/>
 +                      <div t-field="quotation.website_description" class="oe_no_empty"/>
  
                        <t t-foreach="quotation.order_line" t-as="line">
                            <a t-att-id="line.id"/>
 -                          <div t-field="line.website_description"/>
 +                          <div t-field="line.website_description" class="oe_no_empty"/>
                        </t>
      
                        <div class="oe_structure"/>
    </template>
  
    <!-- Options:Quotation Signature -->
-   <template id="opt_quotation_signature" name="Ask Signature" optional="enabled" inherit_id="website_quote.so_quotation">
+   <template id="opt_quotation_signature" name="Ask Signature" customize_show="True" inherit_id="website_quote.so_quotation">
        <xpath expr="//div[@id='sign-dialog']" position="inside">
          <div class="panel panel-default mt16 mb0" id="drawsign">
              <div class="panel-heading">
  
    <template id="optional_products">
      <div class="container mt32" t-if="option">
 -        <section data-snippet-id="title">
 +        <section>
              <h1 class="page-header">Options</h1>
          </section>
          <section id="options">
              <link rel='stylesheet' href='/website_quote/static/src/css/website_quotation.css'/>
              <t t-raw="head or ''"/>
          </t>
-         <body data-spy="scroll" data-target=".navspy">
+         <body data-spy="scroll" data-target=".navspy" data-offset="50">
              <div class="container">
                  <div class="row mt16">
                      <div class="col-md-3">
                                  table of content automatically.
                              </p>
                          </div>
 -                        <div id="template_introduction" t-field="template.website_description"/>
 +                        <div id="template_introduction" t-field="template.website_description" class="oe_no_empty"/>
                          <t t-foreach="template.quote_line" t-as="line">
                              <div class="alert alert-info alert-dismissable" t-ignore="True">
                                  <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
                                  this content will appear on the quotation only if this
                                  product is put on the quote.
                              </div>
 -                            <div t-field="line.website_description"/>
 +                            <div t-field="line.website_description" class="oe_no_empty"/>
                          </t>
                          <t t-foreach="template.options" t-as="option_line">
                              <div class="alert alert-info alert-dismissable" t-ignore="True">
                                  this content will appear on the quotation only if this
                                  product is used in the quote.
                              </div>
 -                            <div t-field="option_line.website_description"/>
 +                            <div t-field="option_line.website_description" class="oe_no_empty"/>
                          </t>
                          <section id="terms" class="container" t-if="template.note">
                              <h1 class="page-header" t-ignore="True">Terms &amp; Conditions</h1>
          </t>
    </template>
  
+   <template id="website.layout_footer_copyright" inherit_id="website.layout" name="Footer Copyright">
+       <xpath expr="//footer" position="inside">
+           <div class="container mt16 mb8">
+               <div class="pull-right" t-ignore="true" t-if="not editable">
+                   Powered by <a class="label label-danger" href="http://www.odoo.com/page/website-builder">Odoo</a>,
+                   an awesome <a href="http://www.odoo.com/page/crm">Open Source CRM</a>.
+               </div>
+               <div class="pull-left text-muted">
+                   Copyright &amp;copy; <span t-field="res_company.name">Company name</span>
+               </div>
+           </div>
+       </xpath>
+   </template>
  </data>
  </openerp>
@@@ -6,8 -6,8 +6,8 @@@
              <field name="model">sale.order</field>
              <field name="inherit_id" ref="sale.view_order_form"/>
              <field name="arch" type="xml">
 -                <xpath expr="//header/button[@name='invoice_corrected']" position="after">
 -                    <button name="open_quotation" string="View Quotation" type="object"
 +                <xpath expr="//header/button[@name='action_button_confirm']" position="before">
 +                    <button name="open_quotation" string="Preview Quotation" type="object"
                          attrs="{'invisible': [('template_id','=',False)]}"/>
                  </xpath>
                  <xpath expr="//page[@string='Order Lines']" position="after">
@@@ -28,6 -28,7 +28,6 @@@
                  </xpath>
                  <xpath expr="//field[@name='client_order_ref']" position="after">
                      <field name="template_id" on_change="onchange_template_id(template_id, partner_id, fiscal_position)"/>
 -                    <field name="validity_date"/>
                      <field name="website_description" invisible="1"/>
                  </xpath>
              </field>
                          </page>
                          </notebook>
                          <field name="website_description" invisible="1"/>
-                         <field name="note" placeholder="Terms and conditions..."  nolable="1"/>
+                         <field name="note" placeholder="Terms and conditions..."  nolabel="1"/>
                      </sheet>
                  </form>
              </field>
@@@ -2,6 -2,7 +2,7 @@@
      'name': 'eCommerce',
      'category': 'Website',
      'summary': 'Sell Your Products Online',
+     'website': 'https://www.odoo.com/page/e-commerce',
      'version': '1.0',
      'description': """
  OpenERP E-Commerce
@@@ -16,7 -17,6 +17,7 @@@
          'views/templates.xml',
          'views/payment.xml',
          'views/sale_order.xml',
 +        'views/snippets.xml',
          'security/ir.model.access.csv',
          'security/website_sale.xml',
      ],
@@@ -17,7 -17,7 +17,7 @@@
              <field name="website_sequence">5</field>
              <field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
              <field name="website_description" type="html">
 -                <section data-snippet-id="text-image" class="mt16 mb16 oe_dark">
 +                <section class="mt16 mb16 oe_dark">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt64">
@@@ -37,7 -37,7 +37,7 @@@
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="image-text" class="mt16">
 +                <section class="mt16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1">
              </field>
          </record>
  
-         <record id="product.product_product_5" model="product.product">
+         <record id="product.product_product_5b" model="product.product">
              <field name="website_published" eval="True"/>
              <field name="website_size_x">2</field>
              <field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_promo')])]"/>
              <field name="website_description" type="html">
 -                <section data-snippet-id="text-image" class="mt16 mb16">
 +                <section class="mt16 mb16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt32">
@@@ -77,7 -77,7 +77,7 @@@
                                      experience music just about anywhere.
                                  </p>
  
 -                                <div class="panel panel-default mt48" data-snippet-id="panel">
 +                                <div class="panel panel-default mt48">
                                      <div class="panel-heading">
                                          <h3 class="panel-title">Characteristics</h3>
                                      </div>
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="text-image" class="mt16 mb16 oe_dark">
 +                <section class="mt16 mb16 oe_dark">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt16">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="text-image" class="mt16 mb16">
 +                <section class="mt16 mb16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt32 mb16">
@@@ -179,7 -179,7 +179,7 @@@ Ultrafast wireles
  iOS7
              </field>
              <field name="website_description" type="html">
 -                <section data-snippet-id="text-image" class="mb16 oe_dark">
 +                <section class="mb16 oe_dark">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt64">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="image-text" class="mt16">
 +                <section class="mt16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="text-image" class="mb16 oe_dark">
 +                <section class="mb16 oe_dark">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt32">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="image-text" class="mt16 mb16">
 +                <section class="mt16 mb16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="text-image" class="mt16 mb16 oe_dark">
 +                <section class="mt16 mb16 oe_dark">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt64">
@@@ -290,7 -290,7 +290,7 @@@ Cable length: 1065 m
  Weight: 0.4 ounce
              </field>
              <field name="website_description" type="html">
 -                <section data-snippet-id="text-image" class="mt32">
 +                <section class="mt32">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-8 col-md-offset-2 text-center">
                  Intel Iris Pro graphics
              </field>
              <field name="website_description" type="html">
 -                <section data-snippet-id="image-text">
 +                <section>
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 mt32 col-md-offset-1">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="text-image" class="mb16 oe_dark">
 +                <section class="mb16 oe_dark">
                      <div class="container">
                          <div class="row">
                              <div class="text-center col-md-8 col-md-offset-2 mt16">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="image-text" class="mt16 mb16">
 +                <section class="mt16 mb16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt64">
              </field>
              <field name="website_description" type="html">
                  <hr/>
 -                <section data-snippet-id="image-text" class="mt16 mb16">
 +                <section class="mt16 mb16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-10 col-md-offset-1 mt32 mb32">
@@@ -445,7 -445,7 +445,7 @@@ Width:  1.56 inche
  Depth:  0.21 inch
  Weight: 1.1 ounces</field>
              <field name="website_description" type="html">
 -                <section data-snippet-id="image-text" class="mt16 mb16">
 +                <section class="mt16 mb16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-9 col-md-offset-1 mb16">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="text-image" class="mt16 mb16 oe_dark">
 +                <section class="mt16 mb16 oe_dark">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-5 col-md-offset-1 mt64">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="image-text" class="mt16 mb16">
 +                <section class="mt16 mb16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-10 col-md-offset-1">
                          </div>
                      </div>
                  </section>
 -                <section data-snippet-id="image-text" class="mt16 mb16">
 +                <section class="mt16 mb16">
                      <div class="container">
                          <div class="row">
                              <div class="col-md-12 text-center mb16">
          <record id="product.product_product_4_product_template" model="product.template">
              <field name="public_categ_ids" eval="[(6,0,[ref('product.Computer_all_in_one')])]"/>
          </record>
-         <record id="product.product_product_5_product_template" model="product.template">
+         <record id="product.product_product_5b_product_template" model="product.template">
              <field name="public_categ_ids" eval="[(6,0,[ref('product.Speakers')])]"/>
          </record>
          <record id="product.product_product_6_product_template" model="product.template">
  </template>
  
  <template id="search" name="Search hidden fields">
-   <t t-if="attrib_values">
-     <t t-foreach="attrib_values" t-as="a">
-       <input type="hidden" name="attrib" t-att-value="'%s-%s' % (a[0], a[1])"/>
+   <form t-att-action="keep('/shop'+ ('/category/'+slug(category)) if category else '', search=0)" method="get" t-att-class="search_class">
+     <t t-if="attrib_values">
+       <t t-foreach="attrib_values" t-as="a">
+         <input type="hidden" name="attrib" t-att-value="'%s-%s' % (a[0], a[1])"/>
+       </t>
      </t>
-   </t>
-   <input t-if="category" type="hidden" name="category" t-att-value="int(category or 0)"/>
-   <div class="input-group">
-     <input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search"/>
-     <span class="input-group-btn">
-       <a class="btn btn-default a-submit"><i class="fa fa-search"/></a>
-     </span>
+     <div class="input-group">
+       <input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search"/>
+       <span class="input-group-btn">
+         <a class="btn btn-default a-submit"><i class="fa fa-search"/></a>
+       </span>
    </div>
+   </form>
  </template>
  
  <template id="404">
@@@ -73,7 -76,7 +76,7 @@@
    </div>
    <div class="oe_product_image">
        <a itemprop="url" t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']>1 else None))">
-           <img itemprop="image" class="img img-responsive" t-attf-src="/website/image/product.template/#{product.id}/image#{'' if product_image_big else '?max_width=300&amp;max_height=300'}"/>
+           <img itemprop="image" class="img img-responsive" t-attf-src="/website/image/product.template/#{product.id}/image{{'' if product_image_big else '/300x300' }}"/>
        </a>
    </div>
    <section>
    </form>
  </template>
  
- <template id="products_description" inherit_id="website_sale.products_item" optional="disabled" name="Product Description">
+ <template id="products_description" inherit_id="website_sale.products_item" active="False" customize_show="True" name="Product Description">
    <xpath expr="//div[@class='product_price']" position="before">
        <div class="text-info oe_subdescription" contenteditable="false">
          <div itemprop="description" t-field="product.description_sale"></div>
    </xpath>
  </template>
  
- <template id="products_add_to_cart" inherit_id="website_sale.products_item" optional="disabled" name="Add to Cart">
+ <template id="products_add_to_cart" inherit_id="website_sale.products_item" active="False" customize_show="True" name="Add to Cart">
    <xpath expr="//div[@class='product_price']" position="inside">
      <input name="product_id" t-att-value="product.product_variant_ids[0].id" type="hidden"/>
      <a class="btn btn-default btn-xs fa fa-shopping-cart a-submit"/>
          <div class="container oe_website_sale">
            <div class="products_pager">
              <div class="row">
-               <form t-att-action="keep('/shop',search='')" method="get" class="pagination form-inline col-md-3">
-                 <t t-call="website_sale.search"/>
-               </form>
+               <t t-call="website_sale.search"><t t-set="search_class">pagination form-inline col-md-3</t></t>
                <t t-call="website.pager"/>
              </div>
            </div>
                            t-attf-class="oe_product oe_grid oe-height-#{td_product['y']*2} #{ td_product['class'] }">
  
                            <div class="oe_product_cart" t-att-data-publish="product.website_published and 'on' or 'off'">
 -
 -                            <div class="css_options" t-ignore="true" groups="base.group_website_publisher">
 -                              <div t-attf-class="dropdown js_options" t-att-data-id="product.id">
 -                                <button class="btn btn-default" t-att-id="'dopprod-%s' % product.id" role="button" data-toggle="dropdown">Options <span class="caret"></span></button>
 -                                <ul class="dropdown-menu" role="menu" t-att-aria-labelledby="'dopprod-%s' % product.id">
 -                                  <li class='dropdown-submenu'>
 -                                    <a tabindex="-1" href="#">Size</a>
 -                                    <ul class="dropdown-menu" name="size">
 -                                      <li><a href="#">
 -                                        <table>
 -                                          <tr>
 -                                            <td class="selected"></td>
 -                                            <td t-att-class="product.website_size_x > 1 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_x > 2 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_x > 3 and 'selected'"></td>
 -                                          </tr>
 -                                          <tr>
 -                                            <td t-att-class="product.website_size_y > 1 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 1 and product.website_size_x > 1 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 1 and product.website_size_x > 2 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 1 and product.website_size_x > 3 and 'selected'"></td>
 -                                          </tr>
 -                                          <tr>
 -                                            <td t-att-class="product.website_size_y > 2 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 2 and product.website_size_x > 1 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 2 and product.website_size_x > 2 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 2 and product.website_size_x > 3 and 'selected'"></td>
 -                                          </tr>
 -                                          <tr>
 -                                            <td t-att-class="product.website_size_y > 3 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 3 and product.website_size_x > 1 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 3 and product.website_size_x > 2 and 'selected'"></td>
 -                                            <td t-att-class="product.website_size_y > 3 and product.website_size_x > 3 and 'selected'"></td>
 -                                          </tr>
 -                                        </table>
 -                                      </a></li>
 -                                    </ul>
 -                                  </li>
 -                                  <li class='dropdown-submenu'>
 -                                    <a tabindex="-1" href="#">Styles</a>
 -                                    <ul class="dropdown-menu" name="style">
 -                                      <t t-foreach="styles" t-as="style">
 -                                        <li t-att-class="style_in_product(style, product) and 'active' or ''"><a href="#" t-att-data-id="style.id" t-att-data-class="style.html_class"><t t-esc="style.name"/></a></li>
 -                                      </t>
 -                                    </ul>
 -                                  </li>
 -                                  <li class='dropdown-submenu'>
 -                                      <a tabindex="-1" href="#">Promote</a>
 -                                      <ul class="dropdown-menu" name="sequence">
 -                                          <li><a href="#" class="js_go_to_top">Push to top</a></li>
 -                                          <li><a href="#" class="js_go_up">Push up</a>
 -                                          </li>
 -                                          <li><a href="#" class="js_go_down">Push down</a></li>
 -                                          <li><a href="#" class="js_go_to_bottom">Push to bottom</a></li>
 -                                      </ul>
 -                                  </li>
 -                                </ul>
 -                              </div>
 -                            </div>
                              <t t-set="product_image_big" t-value="td_product['x']+td_product['y'] > 2"/>
                              <t t-call="website_sale.products_item"/>
                            </div>
    </li>
  </template>
  
- <template id="products_categories" inherit_id="website_sale.products" optional="disabled" name="Product Categories">
+ <template id="products_categories" inherit_id="website_sale.products" active="False" customize_show="True" name="Product Categories">
    <xpath expr="//div[@id='products_grid_before']" position="inside">
        <ul class="nav nav-pills nav-stacked mt16">
            <li t-att-class=" '' if category else 'active' "><a t-att-href="keep('/shop',category=0)">All Products</a></li>
    </xpath>
  </template>
  
- <template id="products_attributes" inherit_id="website_sale.products" optional="disabled" name="Product Attribute's Filters">
+ <template id="products_attributes" inherit_id="website_sale.products" active="False" customize_show="True" name="Product Attribute's Filters">
    <xpath expr="//div[@id='products_grid_before']" position="inside">
        <form class="js_attributes" method="get">
            <input type="hidden" name="search" t-att-value="search"/>
    </xpath>
  </template>
  
- <template id="products_list_view" inherit_id="website_sale.products" optional="disabled" name="List View">
+ <template id="products_list_view" inherit_id="website_sale.products" active="False" customize_show="True" name="List View">
    <xpath expr="//div[@id='products_grid']//table" position="replace">
      <t t-foreach="products" t-as="product">
        <div class="oe_product oe_list oe_product_cart" t-att-data-publish="product.website_published and 'on' or 'off'">
                  </ol>
              </div>
              <div class="col-sm-3">
-               <form t-att-action="keep(search=0)" method="get" class="pull-right">
-                   <t t-call="website_sale.search"/>
-               </form>
+               <t t-call="website_sale.search"><t t-set="search_class">pull-right</t></t>
              </div>
              <div class="col-sm-4" groups="base.group_sale_manager">
                  <t t-call="website.publish_management">
    </t>
  </template>
  
- <template id="product_quantity" inherit_id="website_sale.product" optional="enabled" name="Select Quantity">
+ <template id="product_quantity" inherit_id="website_sale.product" customize_show="True" name="Select Quantity">
    <xpath expr="//a[@id='add_to_cart']" position="before">
      <div class="css_quantity input-group" style="width: 108px;">
          <span class="input-group-addon">
    </div>
  </template>
  
- <template id="product_variants" inherit_id="website_sale.product" optional="disabled" name="List View of Variants">
+ <template id="product_variants" inherit_id="website_sale.product" active="False" customize_show="True" name="List View of Variants">
    <xpath expr="//t[@t-placeholder='select']" position="replace">
      <input type="hidden" t-if="len(product.product_variant_ids) == 1" name="product_id" t-att-value="product.product_variant_ids[0].id"/>
      <t t-if="len(product.product_variant_ids) &gt; 1">
    </ul>
  </template>
  
- <template id="recommended_products" inherit_id="website_sale.product" optional="enabled" name="Alternative Products">
+ <template id="recommended_products" inherit_id="website_sale.product" customize_show="True" name="Alternative Products">
    <xpath expr="//div[@id='product_full_description']" position="after">
        <div class="container mt32" t-if="product.alternative_product_ids">
            <h3>Suggested alternatives:</h3>
    </xpath>
  </template>
  
- <template id="product_attributes" inherit_id="website_sale.product" optional="enabled" name="Product attributes">
+ <template id="product_attributes" inherit_id="website_sale.product" customize_show="True" name="Product attributes">
    <xpath expr="//p[@t-field='product.description_sale']" position="after">
      <hr t-if="sum([(1 if len(l.value_ids)==1 else 0) for l in product.attribute_line_ids])"/>
      <p class="text-muted">
  
  <!-- Product options: OpenChatter -->
  
- <template id="product_comment" inherit_id="website_sale.product" optional="disabled" name="Discussion">
+ <template id="product_comment" inherit_id="website_sale.product" active="False" customize_show="True" name="Discussion">
      <xpath expr="//div[@t-field='product.website_description']" position="after">
          <hr class="mb32"/>
          <section class="container">
                  <ul class="media-list" id="comments-list"  t-if="product.website_message_ids">
                      <li t-foreach="product.website_message_ids" t-as="message" class="media">
                          <div class="media-body oe_msg">
-                             <img class="media-object pull-left oe_msg_avatar" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(message.author_id.id)" style="width: 50px; margin-right: 10px;"/>
+                             <img class="media-object pull-left oe_msg_avatar" t-attf-src="/website/image/res.partner/{{ message.author_id.id }}/image_small" style="width: 50px; margin-right: 10px;"/>
                              <div class="media-body oe_msg_content">
                                  <t t-call="website.publish_short">
                                      <t t-set="object" t-value="message"/>
                  </ul>
                  <div class="css_editable_mode_hidden">
                      <form id="comment" t-attf-action="/shop/product/comment/#{product.id}" method="POST">
-                         <img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
+                         <img class="img pull-left img-rounded" t-attf-src="/website/image/res.partner/{{ user_id.partner_id.id }}/image_small" style="width: 50px; margin-right: 10px;"/>
                          <div class="pull-left mb32" style="width: 75%%">
                              <textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea>
                              <a class="btn btn-primary mt8 a-submit">Post</a>
    </t>
  </template>
  
- <template id="suggested_products_list" inherit_id="website_sale.cart" optional="enabled" name="Suggested Products in my cart">
+ <template id="suggested_products_list" inherit_id="website_sale.cart" customize_show="True" name="Suggested Products in my cart">
    <xpath expr="//table[@id='cart_products']" position="after">
        <table t-if="suggested_products" class='table table-striped table-condensed'>
            <colgroup>
    </xpath>
  </template>
  
- <template id="continue_shopping" inherit_id="website_sale.cart" optional="enabled" name="Continue Shopping Button">
+ <template id="continue_shopping" inherit_id="website_sale.cart" customize_show="True" name="Continue Shopping Button">
    <xpath expr="//a[@href='/shop/checkout']" position="before">
      <a href="/shop" class="btn btn-default mb32"><span class="fa fa-long-arrow-left"/> Continue Shopping</a>
    </xpath>
  </template>
  
- <template id="reduction_code" inherit_id="website_sale.cart" optional="disabled" name="Reduction Code">
+ <template id="reduction_code" inherit_id="website_sale.cart" active="False" customize_show="True" name="Reduction Code">
    <xpath expr="//div[@id='right_column']" position="inside">
        <h4>Coupon Code</h4>
        <p>
  
                    <div class="clearfix"/>
  
-                   <div class="form-group col-lg-6">
-                       <label>
-                           <input type="checkbox" name="shipping_different" t-att-checked="checkout.get('shipping_different')"/>
-                           <span>Ship to a different address</span>
-                       </label>
+                   <div class="form-group col-lg-12">
+                       <label>Shipping</label>
+                       <select name="shipping_id" class="form-control">
+                           <option value="0">Ship to the same address</option>
+                           <t t-foreach="shippings" t-as="shipping">
+                               <option t-att-value="shipping.id" t-att-selected="shipping.id == shipping_id"
+                                 t-att-data-shipping_name="shipping.name"
+                                 t-att-data-shipping_phone="shipping.phone"
+                                 t-att-data-shipping_street="shipping.street"
+                                 t-att-data-shipping_city="shipping.city"
+                                 t-att-data-shipping_zip="shipping.zip"
+                                 t-att-data-shipping_state_id="shipping.state_id and shipping.state_id.id"
+                                 t-att-data-shipping_country_id="shipping.country_id and shipping.country_id.id"
+                                 ><t t-esc="', '.join('\n'.join(shipping.name_get()[0][1].split(',')).split('\n')[1:])"/></option>
+                           </t>
+                           <option value="-1">-- Create a new address --</option>
+                       </select>
                    </div>
                </div>
-               <div class="js_shipping row mb16" t-att-style="not checkout.get('shipping_different') and 'display:none' or ''">
+               <div class="js_shipping row mb16" t-att-style="not shipping_id and 'display:none' or ''">
                    <h3 class="oe_shipping col-lg-12 mt16">Shipping Information</h3>
  
                    <div t-attf-class="form-group #{error.get('shipping_name') and 'has-error' or ''} col-lg-6">
                        <label class="control-label" for="shipping_name">Name (Shipping)</label>
-                       <input type="text" name="shipping_name" class="form-control" t-att-value="checkout.get('shipping_name', '')"/>
+                       <input type="text" name="shipping_name" class="form-control" t-att-value="checkout.get('shipping_name', '')" t-att-readonly="'readonly' if shipping_id &gt;= 0 else ''"/>
                    </div>
                    <div t-attf-class="form-group #{error.get('shipping_phone') and 'has-error' or ''} col-lg-6">
                        <label class="control-label" for="shipping_phone">Phone</label>
-                       <input type="tel" name="shipping_phone" class="form-control" t-att-value="checkout.get('shipping_phone', '')"/>
+                       <input type="tel" name="shipping_phone" class="form-control" t-att-value="checkout.get('shipping_phone', '')" t-att-readonly="  'readonly' if shipping_id &gt;= 0 else ''"/>
                    </div>
                    <div t-attf-class="form-group #{error.get('shipping_street') and 'has-error' or ''} col-lg-6">
                        <label class="control-label" for="shipping_street">Street</label>
-                       <input type="text" name="shipping_street" class="form-control" t-att-value="checkout.get('shipping_street', '')"/>
+                       <input type="text" name="shipping_street" class="form-control" t-att-value="checkout.get('shipping_street', '')" t-att-readonly=" 'readonly' if shipping_id &gt;= 0 else ''"/>
                    </div>
                    <div class="clearfix"/>
                    <div t-attf-class="form-group #{error.get('shipping_city') and 'has-error' or ''} col-lg-6">
                        <label class="control-label" for="shipping_city">City</label>
-                       <input type="text" name="shipping_city" class="form-control" t-att-value="checkout.get('shipping_city', '')"/>
+                       <input type="text" name="shipping_city" class="form-control" t-att-value="checkout.get('shipping_city', '')" t-att-readonly=" 'readonly' if shipping_id &gt;= 0 else ''"/>
                    </div>
                    <div t-attf-class="form-group #{error.get('shipping_zip') and 'has-error' or ''} col-lg-6">
                        <label class="control-label" for="shipping_zip">Zip / Postal Code</label>
-                       <input type="text" name="shipping_zip" class="form-control" t-att-value="checkout.get('shipping_zip', '')"/>
+                       <input type="text" name="shipping_zip" class="form-control" t-att-value="checkout.get('shipping_zip', '')" t-att-readonly=" 'readonly' if shipping_id &gt;= 0 else ''"/>
                    </div>
                    <div t-attf-class="form-group #{error.get('shipping_country_id') and 'has-error' or ''} col-lg-6">
                        <label class="control-label" for="shipping_country_id">Country</label>
-                       <select name="shipping_country_id" class="form-control">
+                       <select name="shipping_country_id" class="form-control" t-att-readonly="  'readonly' if shipping_id &gt;= 0 else ''">
                            <option value="">Country...</option>
                            <t t-foreach="countries or []" t-as="country">
                                <option t-att-value="country.id" t-att-selected="country.id == checkout.get('shipping_country_id')"><t t-esc="country.name"/></option>
                    </div>
                    <div t-attf-class="form-group #{error.get('shipping_state_id') and 'has-error' or ''} col-lg-6">
                        <label class="control-label" for="shipping_state_id" style="font-weight: normal">State / Province</label>
-                       <select name="shipping_state_id" class="form-control">
+                       <select name="shipping_state_id" class="form-control" t-att-readonly="  'readonly' if shipping_id &gt;= 0 else ''">
                            <option value="">State / Province...</option>
                            <t t-foreach="states or []" t-as="state">
                                <option t-att-value="state.id" style="display:none;" t-att-data-country_id="state.country_id.id" t-att-selected="state.id == checkout.get('shipping_state_id')"><t t-esc="state.name"/></option>
                        </select>
                    </div>
                </div>
-               <a href="/shop/cart" class="btn btn-default mb32"><span class="fa fa-long-arrow-left"/> Return to Cart</a>
-               <a class="btn btn-default btn-primary pull-right mb32 a-submit">Confirm <span class="fa fa-long-arrow-right"/></a>
+               <div class="clearfix">
+                 <a href="/shop/cart" class="btn btn-default mb32"><span class="fa fa-long-arrow-left"/> Return to Cart</a>
+                 <a class="btn btn-default btn-primary pull-right mb32 a-submit">Confirm <span class="fa fa-long-arrow-right"/></a>
+               </div>
            </div>
            <div class="col-lg-offset-1 col-lg-3 col-md-3 text-muted">
                <h3 class="page-header mt16">Your Order <small><a href="/shop/cart"><span class="fa fa-arrow-right"/> change</a></small></h3>
      </table>
  </template>
  
+ <template id="website.layout_footer_copyright" inherit_id="website.layout" name="Footer Copyright">
+     <xpath expr="//footer" position="inside">
+         <div class="container mt16 mb8">
+             <div class="pull-right" t-ignore="true" t-if="not editable">
+                 Powered by <a class="label label-danger" href="http://www.odoo.com/page/website-builder">Odoo</a>,
+                 the #1 <a href="http://www.odoo.com/page/e-commerce">Open Source eCommerce</a>.
+             </div>
+             <div class="pull-left text-muted">
+                 Copyright &amp;copy; <span t-field="res_company.name">Company name</span>
+             </div>
+         </div>
+     </xpath>
+ </template>
  <!-- User Navbar -->
  <template id="content_new_product" inherit_id="website.user_navbar" groups="base.group_sale_manager">
      <xpath expr="//ul[@id='oe_systray']/li/ul[@class='dropdown-menu oe_content_menu']" position="inside">
@@@ -250,14 -250,6 +250,14 @@@ class QWeb(orm.AbstractModel)
          generated_attributes = ""
          t_render = None
          template_attributes = {}
 +
 +        debugger = element.get('t-debug')
 +        if debugger is not None:
 +            if openerp.tools.config['dev_mode']:
 +                __import__(debugger).set_trace()  # pdb, ipdb, pudb, ...
 +            else:
 +                _logger.warning("@t-debug in template '%s' is only available in --dev mode" % qwebcontext['__template__'])
 +
          for (attribute_name, attribute_value) in element.attrib.iteritems():
              attribute_name = str(attribute_name)
              if attribute_name == "groups":
              else:
                  generated_attributes += ' %s="%s"' % (attribute_name, escape(attribute_value))
  
 -        if 'debug' in template_attributes:
 -            debugger = template_attributes.get('debug', 'pdb')
 -            __import__(debugger).set_trace()  # pdb, ipdb, pudb, ...
          if t_render:
              result = self._render_tag[t_render](self, element, template_attributes, generated_attributes, qwebcontext)
          else:
  
      def render_tag_esc(self, element, template_attributes, generated_attributes, qwebcontext):
          options = json.loads(template_attributes.get('esc-options') or '{}')
-         widget = self.get_widget_for(options.get('widget', ''))
+         widget = self.get_widget_for(options.get('widget'))
          inner = widget.format(template_attributes['esc'], options, qwebcontext)
          return self.render_element(element, template_attributes, generated_attributes, qwebcontext, inner)
  
          return self.pool.get('ir.qweb.field.' + field_type, self.pool['ir.qweb.field'])
  
      def get_widget_for(self, widget):
-         return self.pool.get('ir.qweb.widget.' + widget, self.pool['ir.qweb.widget'])
+         widget_model = ('ir.qweb.widget.' + widget) if widget else 'ir.qweb.widget'
+         return self.pool.get(widget_model) or self.pool['ir.qweb.widget']
  
      def get_attr_bool(self, attr, default=False):
          if attr:
@@@ -696,7 -692,7 +697,7 @@@ class SelectionConverter(osv.AbstractMo
          value = record[field_name]
          if not value: return ''
          selection = dict(fields.selection.reify(
-             cr, uid, record._model, column))
+             cr, uid, record._model, column, context=context))
          return self.value_to_html(
              cr, uid, selection[value], column, options=options)
  
@@@ -892,7 -888,7 +893,7 @@@ class Contact(orm.AbstractModel)
  
          val = {
              'name': value.split("\n")[0],
 -            'address': escape("\n".join(value.split("\n")[1:])),
 +            'address': escape("\n".join(value.split("\n")[1:])).strip(),
              'phone': field_browse.phone,
              'mobile': field_browse.mobile,
              'fax': field_browse.fax,
@@@ -1010,8 -1006,17 +1011,8 @@@ class AssetNotFound(AssetError)
      pass
  
  class AssetsBundle(object):
 -    # Sass installation:
 -    #
 -    #       sudo gem install sass compass bootstrap-sass
 -    #
 -    # If the following error is encountered:
 -    #       'ERROR: Cannot load compass.'
 -    # Use this:
 -    #       sudo gem install compass --pre
 -    cmd_sass = ['sass', '--stdin', '-t', 'compressed', '--unix-newlines', '--compass', '-r', 'bootstrap-sass']
      rx_css_import = re.compile("(@import[^;{]+;?)", re.M)
 -    rx_sass_import = re.compile("""(@import\s?['"]([^'"]+)['"])""")
 +    rx_preprocess_imports = re.compile("""(@import\s?['"]([^'"]+)['"](;?))""")
      rx_css_split = re.compile("\/\*\! ([a-f0-9-]+) \*\/")
  
      def __init__(self, xmlid, debug=False, cr=None, uid=None, context=None, registry=None):
                  media = el.get('media')
                  if el.tag == 'style':
                      if atype == 'text/sass' or src.endswith('.sass'):
 -                        self.stylesheets.append(SassAsset(self, inline=el.text, media=media))
 +                        self.stylesheets.append(SassStylesheetAsset(self, inline=el.text, media=media))
 +                    elif atype == 'text/less' or src.endswith('.less'):
 +                        self.stylesheets.append(LessStylesheetAsset(self, inline=el.text, media=media))
                      else:
                          self.stylesheets.append(StylesheetAsset(self, inline=el.text, media=media))
                  elif el.tag == 'link' and el.get('rel') == 'stylesheet' and self.can_aggregate(href):
                      if href.endswith('.sass') or atype == 'text/sass':
 -                        self.stylesheets.append(SassAsset(self, url=href, media=media))
 +                        self.stylesheets.append(SassStylesheetAsset(self, url=href, media=media))
 +                    elif href.endswith('.less') or atype == 'text/less':
 +                        self.stylesheets.append(LessStylesheetAsset(self, url=href, media=media))
                      else:
                          self.stylesheets.append(StylesheetAsset(self, url=href, media=media))
                  elif el.tag == 'script' and not src:
          response = []
          if debug:
              if css and self.stylesheets:
 -                self.compile_sass()
 +                self.preprocess_css()
 +                if self.css_errors:
 +                    msg = '\n'.join(self.css_errors)
 +                    self.stylesheets.append(StylesheetAsset(self, inline=self.css_message(msg)))
                  for style in self.stylesheets:
                      response.append(style.to_html())
              if js:
          return content
  
      def css(self):
 +        """Generate css content from given bundle"""
          content = self.get_cache('css')
          if content is None:
 -            self.compile_sass()
 -            content = '\n'.join(asset.minify() for asset in self.stylesheets)
 +            content = self.preprocess_css()
  
              if self.css_errors:
                  msg = '\n'.join(self.css_errors)
 -                content += self.css_message(msg.replace('\n', '\\A '))
 +                content += self.css_message(msg)
  
              # move up all @import rules to the top
              matches = []
  
      def set_cache(self, type, content):
          ira = self.registry['ir.attachment']
 -        url_prefix = '/web/%s/%s/' % (type, self.xmlid)
 -        # Invalidate previous caches
 -        oids = ira.search(self.cr, self.uid, [('url', '=like', url_prefix + '%')], context=self.context)
 -        if oids:
 -            ira.unlink(self.cr, openerp.SUPERUSER_ID, oids, context=self.context)
 -        url = url_prefix + self.version
 +        ira.invalidate_bundle(self.cr, openerp.SUPERUSER_ID, type=type, xmlid=self.xmlid)
 +        url = '/web/%s/%s/%s' % (type, self.xmlid, self.version)
          ira.create(self.cr, openerp.SUPERUSER_ID, dict(
                      datas=content.encode('utf8').encode('base64'),
                      type='binary',
                  ), context=self.context)
  
      def css_message(self, message):
 +        # '\A' == css content carriage return
 +        message = message.replace('\n', '\\A ').replace('"', '\\"')
          return """
              body:before {
                  background: #ffc;
                  white-space: pre;
                  content: "%s";
              }
 -        """ % message.replace('"', '\\"')
 +        """ % message
  
 -    def compile_sass(self):
 +    def preprocess_css(self):
          """
 -            Checks if the bundle contains any sass content, then compiles it to css.
 -            Css compilation is done at the bundle level and not in the assets
 -            because they are potentially interdependant.
 +            Checks if the bundle contains any sass/less content, then compiles it to css.
 +            Returns the bundle's flat css.
          """
 -        sass = [asset for asset in self.stylesheets if isinstance(asset, SassAsset)]
 -        if not sass:
 -            return
 -        source = '\n'.join([asset.get_source() for asset in sass])
 -
 -        # move up all @import rules to the top and exclude file imports
 +        for atype in (SassStylesheetAsset, LessStylesheetAsset):
 +            assets = [asset for asset in self.stylesheets if isinstance(asset, atype)]
 +            if assets:
 +                cmd = assets[0].get_command()
 +                source = '\n'.join([asset.get_source() for asset in assets])
 +                compiled = self.compile_css(cmd, source)
 +
 +                fragments = self.rx_css_split.split(compiled)
 +                at_rules = fragments.pop(0)
 +                if at_rules:
 +                    # Sass and less moves @at-rules to the top in order to stay css 2.1 compatible
 +                    self.stylesheets.insert(0, StylesheetAsset(self, inline=at_rules))
 +                while fragments:
 +                    asset_id = fragments.pop(0)
 +                    asset = next(asset for asset in self.stylesheets if asset.id == asset_id)
 +                    asset._content = fragments.pop(0)
 +
 +        return '\n'.join(asset.minify() for asset in self.stylesheets)
 +
 +    def compile_css(self, cmd, source):
 +        """Sanitizes @import rules, remove duplicates @import rules, then compile"""
          imports = []
 -        def push(matchobj):
 +        def sanitize(matchobj):
              ref = matchobj.group(2)
 -            line = '@import "%s"' % ref
 +            line = '@import "%s"%s' % (ref, matchobj.group(3))
              if '.' not in ref and line not in imports and not ref.startswith(('.', '/', '~')):
                  imports.append(line)
 +                return line
 +            msg = "Local import '%s' is forbidden for security reasons." % ref
 +            _logger.warning(msg)
 +            self.css_errors.append(msg)
              return ''
 -        source = re.sub(self.rx_sass_import, push, source)
 -        imports.append(source)
 -        source = u'\n'.join(imports)
 +        source = re.sub(self.rx_preprocess_imports, sanitize, source)
  
          try:
 -            compiler = Popen(self.cmd_sass, stdin=PIPE, stdout=PIPE, stderr=PIPE)
 +            compiler = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
          except Exception:
 -            msg = "Could not find 'sass' program needed to compile sass/scss files"
 +            msg = "Could not execute command %r" % cmd[0]
              _logger.error(msg)
              self.css_errors.append(msg)
 -            return
 +            return ''
          result = compiler.communicate(input=source.encode('utf-8'))
          if compiler.returncode:
 -            error = self.get_sass_error(''.join(result), source=source)
 +            error = self.get_preprocessor_error(''.join(result), source=source)
              _logger.warning(error)
              self.css_errors.append(error)
 -            return
 +            return ''
          compiled = result[0].strip().decode('utf8')
 -        fragments = self.rx_css_split.split(compiled)[1:]
 -        while fragments:
 -            asset_id = fragments.pop(0)
 -            asset = next(asset for asset in sass if asset.id == asset_id)
 -            asset._content = fragments.pop(0)
 -
 -    def get_sass_error(self, stderr, source=None):
 -        # TODO: try to find out which asset the error belongs to
 +        return compiled
 +
 +    def get_preprocessor_error(self, stderr, source=None):
 +        """Improve and remove sensitive information from sass/less compilator error messages"""
          error = stderr.split('Load paths')[0].replace('  Use --trace for backtrace.', '')
 +        if 'Cannot load compass' in error:
 +            error += "Maybe you should install the compass gem using this extra argument:\n\n" \
 +                     "    $ sudo gem install compass --pre\n"
          error += "This error occured while compiling the bundle '%s' containing:" % self.xmlid
          for asset in self.stylesheets:
 -            if isinstance(asset, SassAsset):
 +            if isinstance(asset, PreprocessedCSS):
                  error += '\n    - %s' % (asset.url if asset.url else '<inline sass>')
          return error
  
@@@ -1308,7 -1293,7 +1309,7 @@@ class WebAsset(object)
  
      @property
      def content(self):
 -        if not self._content:
 +        if self._content is None:
              self._content = self.inline or self._fetch_content()
          return self._content
  
@@@ -1374,26 -1359,22 +1375,26 @@@ class StylesheetAsset(WebAsset)
              content = super(StylesheetAsset, self)._fetch_content()
              web_dir = os.path.dirname(self.url)
  
 -            content = self.rx_import.sub(
 -                r"""@import \1%s/""" % (web_dir,),
 -                content,
 -            )
 +            if self.rx_import:
 +                content = self.rx_import.sub(
 +                    r"""@import \1%s/""" % (web_dir,),
 +                    content,
 +                )
  
 -            content = self.rx_url.sub(
 -                r"url(\1%s/" % (web_dir,),
 -                content,
 -            )
 +            if self.rx_url:
 +                content = self.rx_url.sub(
 +                    r"url(\1%s/" % (web_dir,),
 +                    content,
 +                )
  
 -            # remove charset declarations, we only support utf-8
 -            content = self.rx_charset.sub('', content)
 +            if self.rx_charset:
 +                # remove charset declarations, we only support utf-8
 +                content = self.rx_charset.sub('', content)
 +
 +            return content
          except AssetError, e:
              self.bundle.css_errors.append(e.message)
              return ''
 -        return content
  
      def minify(self):
          # remove existing sourcemaps, make no sense after re-mini
          else:
              return '<style type="text/css"%s>%s</style>' % (media, self.with_header())
  
 -class SassAsset(StylesheetAsset):
 +class PreprocessedCSS(StylesheetAsset):
      html_url = '%s.css'
 -    rx_indent = re.compile(r'^( +|\t+)', re.M)
 -    indent = None
 -    reindent = '    '
 +    rx_import = None
  
      def minify(self):
          return self.with_header()
          if self.url:
              ira = self.registry['ir.attachment']
              url = self.html_url % self.url
 -            domain = [('type', '=', 'binary'), ('url', '=', self.url)]
 +            domain = [('type', '=', 'binary'), ('url', '=', url)]
              ira_id = ira.search(self.cr, self.uid, domain, context=self.context)
 +            datas = self.content.encode('utf8').encode('base64')
              if ira_id:
                  # TODO: update only if needed
 -                ira.write(self.cr, openerp.SUPERUSER_ID, [ira_id], {'datas': self.content}, context=self.context)
 +                ira.write(self.cr, openerp.SUPERUSER_ID, ira_id, {'datas': datas}, context=self.context)
              else:
                  ira.create(self.cr, openerp.SUPERUSER_ID, dict(
 -                    datas=self.content.encode('utf8').encode('base64'),
 +                    datas=datas,
                      mimetype='text/css',
                      type='binary',
                      name=url,
                      url=url,
                  ), context=self.context)
 -        return super(SassAsset, self).to_html()
 +        return super(PreprocessedCSS, self).to_html()
 +
 +    def get_source(self):
 +        content = self.inline or self._fetch_content()
 +        return "/*! %s */\n%s" % (self.id, content)
 +
 +    def get_command(self):
 +        raise NotImplementedError
 +
 +class SassStylesheetAsset(PreprocessedCSS):
 +    rx_indent = re.compile(r'^( +|\t+)', re.M)
 +    indent = None
 +    reindent = '    '
  
      def get_source(self):
          content = textwrap.dedent(self.inline or self._fetch_content())
  
          def fix_indent(m):
 +            # Indentation normalization
              ind = m.group()
              if self.indent is None:
                  self.indent = ind
              pass
          return "/*! %s */\n%s" % (self.id, content)
  
 +    def get_command(self):
 +        return ['sass', '--stdin', '-t', 'compressed', '--unix-newlines', '--compass',
 +               '-r', 'bootstrap-sass']
 +
 +class LessStylesheetAsset(PreprocessedCSS):
 +    def get_command(self):
 +        webpath = openerp.http.addons_manifest['web']['addons_path']
 +        lesspath = os.path.join(webpath, 'web', 'static', 'lib', 'bootstrap', 'less')
 +        return ['lessc', '-', '--clean-css', '--no-js', '--no-color', '--include-path=%s' % lesspath]
 +
  def rjsmin(script):
      """ Minify js with a clever regex.
      Taken from http://opensource.perlig.de/rjsmin
@@@ -37,10 -37,8 +37,10 @@@ from lxml import etre
  import openerp
  from openerp import tools, api
  from openerp.http import request
 +from openerp.modules.module import get_resource_path, get_resource_from_path
  from openerp.osv import fields, osv, orm
- from openerp.tools import config, graph, SKIPPED_ELEMENT_TYPES
 -from openerp.tools import graph, SKIPPED_ELEMENT_TYPES, SKIPPED_ELEMENTS
++from openerp.tools import config, graph, SKIPPED_ELEMENT_TYPES, SKIPPED_ELEMENTS
 +from openerp.tools.convert import _fix_multiple_roots
  from openerp.tools.parse_version import parse_version
  from openerp.tools.safe_eval import safe_eval as eval
  from openerp.tools.view_validation import valid_view
@@@ -49,7 -47,7 +49,7 @@@ from openerp.tools.translate import 
  
  _logger = logging.getLogger(__name__)
  
- MOVABLE_BRANDING = ['data-oe-model', 'data-oe-id', 'data-oe-field', 'data-oe-xpath']
+ MOVABLE_BRANDING = ['data-oe-model', 'data-oe-id', 'data-oe-field', 'data-oe-xpath', 'data-oe-source-id']
  
  def keep_query(*keep_params, **additional_params):
      """
@@@ -107,30 -105,6 +107,30 @@@ def _hasclass(context, *cls)
  
      return node_classes.issuperset(cls)
  
 +def get_view_arch_from_file(filename, xmlid):
 +    doc = etree.parse(filename)
 +    node = None
 +    for n in doc.xpath('//*[@id="%s"] | //*[@id="%s"]' % (xmlid, xmlid.split('.')[1])):
 +        if n.tag in ('template', 'record'):
 +            node = n
 +            break
 +    if node is not None:
 +        if node.tag == 'record':
 +            field = node.find('field[@name="arch"]')
 +            _fix_multiple_roots(field)
 +            inner = ''.join([etree.tostring(child) for child in field.iterchildren()])
 +            return field.text + inner
 +        elif node.tag == 'template':
 +            # The following dom operations has been copied from convert.py's _tag_template()
 +            if not node.get('inherit_id'):
 +                node.set('t-name', xmlid)
 +                node.tag = 't'
 +            else:
 +                node.tag = 'data'
 +            node.attrib.pop('id', None)
 +            return etree.tostring(node)
 +    raise ValueError("Could not find view arch definition in file '%s' for xmlid '%s'" % (filename, xmlid))
 +
  xpath_utils = etree.FunctionNamespace(None)
  xpath_utils['hasclass'] = _hasclass
  
@@@ -149,33 -123,6 +149,33 @@@ class view(osv.osv)
          data_ids = IMD.search_read(cr, uid, [('id', 'in', ids), ('model', '=', 'ir.ui.view')], ['res_id'], context=context)
          return map(itemgetter('res_id'), data_ids)
  
 +    def _arch_get(self, cr, uid, ids, name, arg, context=None):
 +        result = {}
 +        for view in self.browse(cr, uid, ids, context=context):
 +            arch_fs = None
 +            if config['dev_mode'] and view.arch_fs and view.xml_id:
 +                # It is safe to split on / herebelow because arch_fs is explicitely stored with '/'
 +                fullpath = get_resource_path(*view.arch_fs.split('/'))
 +                arch_fs = get_view_arch_from_file(fullpath, view.xml_id)
 +            result[view.id] = arch_fs or view.arch_db
 +        return result
 +
 +    def _arch_set(self, cr, uid, ids, field_name, field_value, args, context=None):
 +        if not isinstance(ids, list):
 +            ids = [ids]
 +        if field_value:
 +            for view in self.browse(cr, uid, ids, context=context):
 +                data = dict(arch_db=field_value)
 +                key = 'install_mode_data'
 +                if context and key in context:
 +                    imd = context[key]
 +                    if self._model._name == imd['model'] and (not view.xml_id or view.xml_id == imd['xml_id']):
 +                        # we store the relative path to the resource instead of the absolute path
 +                        data['arch_fs'] = '/'.join(get_resource_from_path(imd['xml_file'])[0:2])
 +                self.write(cr, uid, ids, data, context=context)
 +
 +        return True
 +
      _columns = {
          'name': fields.char('View Name', required=True),
          'model': fields.char('Object', select=True),
              ('kanban', 'Kanban'),
              ('search','Search'),
              ('qweb', 'QWeb')], string='View Type'),
 -        'arch': fields.text('View Architecture', required=True),
 +        'arch': fields.function(_arch_get, fnct_inv=_arch_set, string='View Architecture', type="text", nodrop=True),
 +        'arch_db': fields.text('Arch Blob'),
 +        'arch_fs': fields.char('Arch Filename'),
          'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade', select=True),
          'inherit_children_ids': fields.one2many('ir.ui.view','inherit_id', 'Inherit Views'),
          'field_parent': fields.char('Child Field'),
    (<xpath/>) are applied, and the result is used as if it were this view's
    actual arch.
  """),
-         'application': fields.selection([
-                 ('always', "Always applied"),
-                 ('enabled', "Optional, enabled"),
-                 ('disabled', "Optional, disabled"),
-             ],
-             required=True, string="Application status",
+         'active': fields.boolean("Active", required=True,
              help="""If this view is inherited,
- * if always, the view always extends its parent
- * if enabled, the view currently extends its parent but can be disabled
- * if disabled, the view currently does not extend its parent but can be enabled
+ * if True, the view always extends its parent
+ * if False, the view currently does not extend its parent but can be enabled
               """),
      }
      _defaults = {
          'mode': 'primary',
-         'application': 'always',
+         'active': True,
          'priority': 16,
      }
      _order = "priority,name"
          if context is None:
              context = {}
  
 +        # If view is modified we remove the arch_fs information thus activating the arch_db
 +        # version. An `init` of the view will restore the arch_fs for the --dev mode
 +        if 'arch' in vals and 'install_mode_data' not in context:
 +            vals['arch_fs'] = False
 +
          # drop the corresponding view customizations (used for dashboards for example), otherwise
          # not all users would see the updated views
          custom_view_ids = self.pool.get('ir.ui.view.custom').search(cr, uid, [('ref_id', 'in', ids)])
          return ret
  
      def toggle(self, cr, uid, ids, context=None):
-         """ Switches between enabled and disabled application statuses
+         """ Switches between enabled and disabled statuses
          """
-         for view in self.browse(cr, uid, ids, context=context):
-             if view.application == 'enabled':
-                 view.write({'application': 'disabled'})
-             elif view.application == 'disabled':
-                 view.write({'application': 'enabled'})
-             else:
-                 raise ValueError(_("Can't toggle view %d with application %r") % (
-                     view.id,
-                     view.application,
-                 ))
+         for view in self.browse(cr, uid, ids, context=dict(context or {}, active_test=False)):
+             view.write({'active': not view.active})
  
      # default view selection
      def default_view(self, cr, uid, model, view_type, context=None):
              ['inherit_id', '=', view_id],
              ['model', '=', model],
              ['mode', '=', 'extension'],
-             ['application', 'in', ['always', 'enabled']],
+             ['active', '=', True],
          ]
          if self.pool._init:
              # Module init currently in progress, only consider views from
      #------------------------------------------------------
      # QWeb template views
      #------------------------------------------------------
 -    @tools.ormcache_context(accepted_keys=('lang','inherit_branding', 'editable', 'translatable'))
 +    read_template_cache = dict(accepted_keys=('lang','inherit_branding', 'editable', 'translatable'))
 +    if config['dev_mode']:
 +        read_template_cache['size'] = 0
 +    @tools.ormcache_context(**read_template_cache)
      def read_template(self, cr, uid, xml_id, context=None):
          if isinstance(xml_id, (int, long)):
              view_id = xml_id
                  return None
              return Translations._get_source(cr, uid, 'website', 'view', lang, text, id_)
  
-         if arch.tag not in ['script']:
+         if type(arch) not in SKIPPED_ELEMENT_TYPES and arch.tag not in SKIPPED_ELEMENTS:
              text = get_trans(arch.text)
              if text:
                  arch.text = arch.text.replace(arch.text.strip(), text)
              if tail:
                  arch.tail = arch.tail.replace(arch.tail.strip(), tail)
  
-             for attr_name in ('title', 'alt', 'placeholder'):
+             for attr_name in ('title', 'alt', 'label', 'placeholder'):
                  attr = get_trans(arch.get(attr_name))
                  if attr:
                      arch.set(attr_name, attr)
@@@ -234,7 -234,7 +234,7 @@@ class res_partner(osv.Model, format_add
          'title': fields.many2one('res.partner.title', 'Title'),
          'parent_id': fields.many2one('res.partner', 'Related Company', select=True),
          'child_ids': fields.one2many('res.partner', 'parent_id', 'Contacts', domain=[('active','=',True)]), # force "active_test" domain to bypass _search() override
 -        'ref': fields.char('Contact Reference', select=1),
 +        'ref': fields.char('Internal Reference', select=1),
          'lang': fields.selection(_lang_get, 'Language',
              help="If the selected language is loaded in the system, all documents related to this contact will be printed in this language. If not, it will be English."),
          'tz': fields.selection(_tz_get,  'Timezone', size=64,
  
      @api.multi
      def onchange_type(self, is_company):
-         value = {}
-         value['title'] = False
+         value = {'title': False}
          if is_company:
              value['use_parent_address'] = False
              domain = {'title': [('domain', '=', 'partner')]}
      def _commercial_sync_from_company(self, cr, uid, partner, context=None):
          """ Handle sync of commercial fields when a new parent commercial entity is set,
          as if they were related fields """
-         if partner.commercial_partner_id != partner:
+         commercial_partner = partner.commercial_partner_id
+         if not commercial_partner:
+             # On child partner creation of a parent partner,
+             # the commercial_partner_id is not yet computed
+             commercial_partner_id = self._commercial_partner_compute(
+                 cr, uid, [partner.id], 'commercial_partner_id', [], context=context)[partner.id]
+             commercial_partner = self.browse(cr, uid, commercial_partner_id, context=context)
+         if commercial_partner != partner:
              commercial_fields = self._commercial_fields(cr, uid, context=context)
-             sync_vals = self._update_fields_values(cr, uid, partner.commercial_partner_id,
-                                                         commercial_fields, context=context)
+             sync_vals = self._update_fields_values(cr, uid, commercial_partner,
+                                                    commercial_fields, context=context)
              partner.write(sync_vals)
  
      def _commercial_sync_to_children(self, cr, uid, partner, context=None):
          """ Handle sync of commercial fields to descendants """
          commercial_fields = self._commercial_fields(cr, uid, context=context)
-         sync_vals = self._update_fields_values(cr, uid, partner.commercial_partner_id,
-                                                    commercial_fields, context=context)
+         commercial_partner = partner.commercial_partner_id
+         if not commercial_partner:
+             # On child partner creation of a parent partner,
+             # the commercial_partner_id is not yet computed
+             commercial_partner_id = self._commercial_partner_compute(
+                 cr, uid, [partner.id], 'commercial_partner_id', [], context=context)[partner.id]
+             commercial_partner = self.browse(cr, uid, commercial_partner_id, context=context)
+         sync_vals = self._update_fields_values(cr, uid, commercial_partner,
+                                                commercial_fields, context=context)
          sync_children = [c for c in partner.child_ids if not c.is_company]
          for child in sync_children:
              self._commercial_sync_to_children(cr, uid, child, context=context)
@@@ -31,7 -31,6 +31,6 @@@
                      <group col="4">
                          <field name="name"/>
                          <field name="shortcut"/>
-                         <field name="domain"/>
                      </group>
                  </form>
              </field>
                              placeholder="Company"
                              domain="[('is_company', '=', True)]" context="{'default_is_company': True}"
                              attrs="{'invisible': [('is_company','=', True)]}"/>
-                         <group>
-                             <field name="category_id" widget="many2many_tags" placeholder="Tags..."/>
-                             <field name="function" placeholder="e.g. Sales Director"/>
-                             <field name="user_ids" invisible="1"/>
-                             <field name="email" widget="email" required="context.get('force_email', False)" attrs="{'required': [('user_ids','!=', [])]}"/>
-                             <field name="phone"/>
-                             <field name="mobile"/>
-                         </group>
                      </div>
+                     <group>
+                         <field name="category_id" widget="many2many_tags" placeholder="Tags..."/>
+                         <field name="function" placeholder="e.g. Sales Director"/>
+                         <field name="user_ids" invisible="1"/>
+                         <field name="email" widget="email" required="context.get('force_email', False)" attrs="{'required': [('user_ids','!=', [])]}"/>
+                         <field name="phone"/>
+                         <field name="mobile"/>
+                     </group>
                  </form>
              </field>
          </record>
  
                      <group>
                          <group>
 -                            <label for="type" attrs="{'invisible': [('parent_id','=', False)]}"/>
 -                            <div attrs="{'invisible': [('parent_id','=', False)]}" name="div_type">
 +                            <label for="type" attrs="{'invisible': [('parent_id','=', False)]}" groups="base.group_no_one"/>
 +                            <div attrs="{'invisible': [('parent_id','=', False)]}" name="div_type" groups="base.group_no_one">
                                  <field class="oe_inline"
                                      name="type"/>
                              </div>
                              <field name="fax"/>
                              <field name="user_ids" invisible="1"/>
                              <field name="email" widget="email" attrs="{'required': [('user_ids','!=', [])]}"/>
-                             <field name="title" domain="[('domain','=','contact')]" options='{"no_open": True}' attrs="{'invisible': [('is_company','=',True)]}" context="{'default_domain': 'contact'}"/>
-                             <field name="title" domain="[('domain','=','partner')]" options='{"no_open": True}' attrs="{'invisible': [('is_company','=',False)]}" context="{'default_domain': 'partner'}"/>
+                             <field name="title" options='{"no_open": True}'/>
                          </group>
                      </group>
  
                                              <field name="use_parent_address"/><label for="use_parent_address"/>
                                          </div>
                                          <group>
 -                                            <label for="type"/>
 -                                            <div name="div_type">
 +                                            <label for="type" groups="base.group_no_one"/>
 +                                            <div name="contact_div_type" groups="base.group_no_one">
                                                  <field class="oe_inline" name="type"/>
                                              </div>
                                              <label for="street" string="Address" attrs="{'invisible': [('use_parent_address','=', True)]}"/>
                                  <group>
                                      <field name="ref"/>
                                      <field name="lang"/>
 -                                    <field name="date"/>
                                  </group>
 -                                <group>
 +                            </group>
 +                            <group>
 +                                <group string="Mailing" name="mailing">
                                      <field name="active"/>
                                  </group>
                              </group>
 +                            <group>
 +                                <group name="invoicing"/>
 +                                <group name="point_of_sale"/>
 +                            </group>
                          </page>
                      </notebook>
                  </sheet>
@@@ -64,23 -64,23 +64,23 @@@ OERP_WEBSITE_HTML_1 = ""
  <div>
      <div class="container">
          <div class="row">
 -            <div class="col-md-12 text-center mt16 mb16" data-snippet-id="colmd">
 +            <div class="col-md-12 text-center mt16 mb16">
                  <h2>OpenERP HR Features</h2>
                  <h3 class="text-muted">Manage your company most important asset: People</h3>
              </div>
 -            <div class="col-md-4" data-snippet-id="colmd">
 +            <div class="col-md-4">
                  <img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg">
                  <h4 class="mt16">Streamline Recruitments</h4>
                  <p>Post job offers and keep track of each application received. Follow applicants in your recruitment process with the smart kanban view.</p>
                  <p>Save time by automating some communications with email templates. Resumes are indexed automatically, allowing you to easily find for specific profiles.</p>
              </div>
 -            <div class="col-md-4" data-snippet-id="colmd">
 +            <div class="col-md-4">
                  <img class="img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg">
                  <h4 class="mt16">Enterprise Social Network</h4>
                  <p>Break down information silos. Share knowledge and best practices amongst all employees. Follow specific people or documents and join groups of interests to share expertise and documents.</p>
                  <p>Interact with your collegues in real time with live chat.</p>
              </div>
 -            <div class="col-md-4" data-snippet-id="colmd">
 +            <div class="col-md-4">
                  <img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg">
                  <h4 class="mt16">Leaves Management</h4>
                  <p>Keep track of the vacation days accrued by each employee. Employees enter their requests (paid holidays, sick leave, etc), for managers to approve and validate. It's all done in just a few clicks. The agenda of each employee is updated accordingly.</p>
@@@ -101,16 -101,16 +101,16 @@@ OERP_WEBSITE_HTML_1_OUT = 
  
  OERP_WEBSITE_HTML_2 = """
  <div class="mt16 cke_widget_editable cke_widget_element oe_editable oe_dirty" data-oe-model="blog.post" data-oe-id="6" data-oe-field="content" data-oe-type="html" data-oe-translate="0" data-oe-expression="blog_post.content" data-cke-widget-data="{}" data-cke-widget-keep-attr="0" data-widget="oeref" contenteditable="true" data-cke-widget-editable="text">
 -    <section class="mt16 mb16" data-snippet-id="text-block">
 +    <section class="mt16 mb16">
          <div class="container">
              <div class="row">
 -                <div class="col-md-12 text-center mt16 mb32" data-snippet-id="colmd">
 +                <div class="col-md-12 text-center mt16 mb32">
                      <h2>
                          OpenERP Project Management
                      </h2>
                      <h3 class="text-muted">Infinitely flexible. Incredibly easy to use.</h3>
                  </div>
 -                <div class="col-md-12 mb16 mt16" data-snippet-id="colmd">
 +                <div class="col-md-12 mb16 mt16">
                      <p>
                          OpenERP's <b>collaborative and realtime</b> project
                          management helps your team get work done. Keep
              </div>
          </div>
      </section>
 -    <section class="" data-snippet-id="image-text">
 +    <section class="">
          <div class="container">
              <div class="row">
 -                <div class="col-md-6 mt16 mb16" data-snippet-id="colmd">
 +                <div class="col-md-6 mt16 mb16">
                      <img class="img-responsive shadow" src="/website/static/src/img/image_text.jpg">
                  </div>
 -                <div class="col-md-6 mt32" data-snippet-id="colmd">
 +                <div class="col-md-6 mt32">
                      <h3>Manage Your Shops</h3>
                      <p>
                          OpenERP's Point of Sale introduces a super clean
              </div>
          </div>
      </section>
 -    <section class="" data-snippet-id="text-image">
 +    <section class="">
          <div class="container">
              <div class="row">
 -                <div class="col-md-6 mt32" data-snippet-id="colmd">
 +                <div class="col-md-6 mt32">
                      <h3>Enterprise Social Network</h3>
                      <p>
                          Make every employee feel more connected and engaged
                          and business applications.
                      </p>
                  </div>
 -                <div class="col-md-6 mt16 mb16" data-snippet-id="colmd">
 +                <div class="col-md-6 mt16 mb16">
                      <img class="img-responsive shadow" src="/website/static/src/img/text_image.png">
                  </div>
              </div>
          </div>
 -    </section><section class="" data-snippet-id="portfolio">
 +    </section><section class="">
          <div class="container">
              <div class="row">
 -                <div class="col-md-12 text-center mt16 mb32" data-snippet-id="colmd">
 +                <div class="col-md-12 text-center mt16 mb32">
                      <h2>Our Porfolio</h2>
                      <h4 class="text-muted">More than 500 successful projects</h4>
                  </div>
 -                <div class="col-md-4" data-snippet-id="colmd">
 +                <div class="col-md-4">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
                  </div>
 -                <div class="col-md-4" data-snippet-id="colmd">
 +                <div class="col-md-4">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg">
                  </div>
 -                <div class="col-md-4" data-snippet-id="colmd">
 +                <div class="col-md-4">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/landscape.jpg">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg">
                      <img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg">
@@@ -1104,3 -1104,72 +1104,72 @@@ BUG_2_OUT = 
      'Fwd: TR: OpenERP S.A'
      'fait un mois'
  ]
+ # BUG 20/08/2014: READ MORE NOT APPEARING
+ BUG3 = """<div class="oe_msg_body_long" style="/* display: none; */"><p>OpenERP has been upgraded to version 8.0.</p>
+ <h2>What's new in this upgrade?</h2>
+ <div class="document">
+ <ul>
+ <li><p class="first">New Warehouse Management System:</p>
+ <blockquote>
+ <p>Schedule your picking, packing, receptions and internal moves automatically with Odoo using
+ your own routing rules. Define push and pull rules to organize a warehouse or to manage
+ product moves between several warehouses. Track in detail all stock moves, not only in your
+ warehouse but wherever else it's taken as well (customers, suppliers or manufacturing
+ locations).</p>
+ </blockquote>
+ </li>
+ <li><p class="first">New Product Configurator</p>
+ </li>
+ <li><p class="first">Documentation generation from website forum:</p>
+ <blockquote>
+ <p>New module to generate a documentation from questions and responses from your forum.
+ The documentation manager can define a table of content and any user, depending their karma,
+ can link a question to an entry of this TOC.</p>
+ </blockquote>
+ </li>
+ <li><p class="first">New kanban view of documents (resumes and letters in recruitement, project documents...)</p>
+ </li>
+ <li><p class="first">E-Commerce:</p>
+ <blockquote>
+ <ul class="simple">
+ <li>Manage TIN in contact form for B2B.</li>
+ <li>Dedicated salesteam to easily manage leads and orders.</li>
+ </ul>
+ </blockquote>
+ </li>
+ <li><p class="first">Better Instant Messaging.</p>
+ </li>
+ <li><p class="first">Faster and Improved Search view: Search drawer now appears on top of the results, and is open
+ by default in reporting views</p>
+ </li>
+ <li><p class="first">Improved User Interface:</p>
+ <blockquote>
+ <ul class="simple">
+ <li>Popups has changed to be more responsive on tablets and smartphones.</li>
+ <li>New Stat Buttons: Forms views have now dynamic buttons showing some statistics abouts linked models.</li>
+ <li>Color code to check in one look availability of components in an MRP order.</li>
+ <li>Unified menu bar allows you to switch easily between the frontend (website) and backend</li>
+ <li>Results panel is now scrollable independently of the menu bars, keeping the navigation,
+ search bar and view switcher always within reach.</li>
+ </ul>
+ </blockquote>
+ </li>
+ <li><p class="first">User signature is now in HTML.</p>
+ </li>
+ <li><p class="first">New development API.</p>
+ </li>
+ <li><p class="first">Remove support for Outlook and Thunderbird plugins</p>
+ </li>
+ </ul>
+ </div>
+ <p>Enjoy the new OpenERP Online!</p><span class="oe_mail_reduce"><a href="#">read less</a></span></div>"""
+ BUG_3_IN = [
+     'read more',
+     '...',
+ ]
+ BUG_3_OUT = [
+     'New kanban view of documents'
+ ]
@@@ -544,9 -544,9 +544,9 @@@ class TestTemplating(ViewCase)
                          'data-oe-id': str(id2),
                          'data-oe-field': 'arch',
                          'data-oe-xpath': '/xpath/item/content[1]',
+                         'data-oe-source-id': str(id)
                      }), {
                          'order': '2',
-                         'data-oe-source-id': str(id)
                      }),
                  E.item({
                      'order': '1',
                      {'t-ignore': 'true', 'order': '1'},
                      E.t({'t-esc': 'foo'}),
                      E.item(
-                         {'order': '2', 'data-oe-source-id': str(id)},
+                         {'order': '2'},
                          E.content(
                              {'t-att-href': 'foo'},
                              "bar")
@@@ -642,7 -642,7 +642,7 @@@ class test_views(ViewCase)
          """Insert view into database via a query to passtrough validation"""
          kw.pop('id', None)
          kw.setdefault('mode', 'extension' if kw.get('inherit_id') else 'primary')
-         kw.setdefault('application', 'always')
+         kw.setdefault('active', True)
  
          keys = sorted(kw.keys())
          fields = ','.join('"%s"' % (k.replace('"', r'\"'),) for k in keys)
              name='base view',
              model=model,
              priority=1,
 -            arch="""<?xml version="1.0"?>
 +            arch_db="""<?xml version="1.0"?>
                          <tree string="view">
                            <field name="url"/>
                          </tree>
              model=model,
              priority=1,
              inherit_id=vid,
 -            arch="""<?xml version="1.0"?>
 +            arch_db="""<?xml version="1.0"?>
                          <xpath expr="//field[@name='url']" position="before">
                            <field name="name"/>
                          </xpath>
@@@ -1095,21 -1095,21 +1095,21 @@@ class TestOptionalViews(ViewCase)
          self.v1 = self.create({
              'model': 'a',
              'inherit_id': self.v0,
-             'application': 'always',
+             'active': True,
              'priority': 10,
              'arch': '<xpath expr="//base" position="after"><v1/></xpath>',
          })
          self.v2 = self.create({
              'model': 'a',
              'inherit_id': self.v0,
-             'application': 'enabled',
+             'active': True,
              'priority': 9,
              'arch': '<xpath expr="//base" position="after"><v2/></xpath>',
          })
          self.v3 = self.create({
              'model': 'a',
              'inherit_id': self.v0,
-             'application': 'disabled',
+             'active': False,
              'priority': 8,
              'arch': '<xpath expr="//base" position="after"><v3/></xpath>'
          })
          )
  
      def test_applied_state_toggle(self):
-         """ Change application states of v2 and v3, check that the results
+         """ Change active states of v2 and v3, check that the results
          are as expected
          """
-         self.browse(self.v2).write({'application': 'disabled'})
+         self.browse(self.v2).toggle()
          arch = self.read_combined(self.v0)['arch']
          self.assertEqual(
              ET.fromstring(arch),
              )
          )
  
-         self.browse(self.v3).write({'application': 'enabled'})
+         self.browse(self.v3).toggle()
          arch = self.read_combined(self.v0)['arch']
          self.assertEqual(
              ET.fromstring(arch),
              )
          )
  
-         self.browse(self.v2).write({'application': 'enabled'})
+         self.browse(self.v2).toggle()
          arch = self.read_combined(self.v0)['arch']
          self.assertEqual(
              ET.fromstring(arch),
diff --combined openerp/models.py
@@@ -62,10 -62,10 +62,10 @@@ from . import SUPERUSER_I
  from . import api
  from . import tools
  from .api import Environment
- from .exceptions import except_orm, AccessError, MissingError
+ from .exceptions import except_orm, AccessError, MissingError, ValidationError
  from .osv import fields
  from .osv.query import Query
- from .tools import lazy_property
+ from .tools import lazy_property, ormcache
  from .tools.config import config
  from .tools.misc import CountingStream, DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
  from .tools.safe_eval import safe_eval as eval
@@@ -246,6 -246,9 +246,9 @@@ class NewId(object)
  IdType = (int, long, basestring, NewId)
  
  
+ # maximum number of prefetched records
+ PREFETCH_MAX = 200
  # special columns automatically created by the ORM
  LOG_ACCESS_COLUMNS = ['create_uid', 'create_date', 'write_uid', 'write_date']
  MAGIC_COLUMNS = ['id'] + LOG_ACCESS_COLUMNS
@@@ -820,7 -823,7 +823,7 @@@ class BaseModel(object)
          # check defaults
          for k in cls._defaults:
              assert k in cls._fields, \
 -                "Model %s has a default for nonexiting field %s" % (cls._name, k)
 +                "Model %s has a default for non-existing field %s" % (cls._name, k)
  
          # restart columns
          for column in cls._columns.itervalues():
          # prepare ormcache, which must be shared by all instances of the model
          cls._ormcache = {}
  
+     @api.model
+     @ormcache()
+     def _is_an_ordinary_table(self):
+         self.env.cr.execute("""\
+             SELECT  1
+             FROM    pg_class
+             WHERE   relname = %s
+             AND     relkind = %s""", [self._table, 'r'])
+         return bool(self.env.cr.fetchone())
      def __export_xml_id(self):
          """ Return a valid xml_id for the record `self`. """
+         if not self._is_an_ordinary_table():
+             raise Exception(
+                 "You can not export the column ID of model %s, because the "
+                 "table %s is not an ordinary table."
+                 % (self._name, self._table))
          ir_model_data = self.sudo().env['ir.model.data']
          data = ir_model_data.search([('model', '=', self._name), ('res_id', '=', self.id)])
          if data:
-             if data.module:
-                 return '%s.%s' % (data.module, data.name)
+             if data[0].module:
+                 return '%s.%s' % (data[0].module, data[0].name)
              else:
-                 return data.name
+                 return data[0].name
          else:
              postfix = 0
              name = '%s_%s' % (self._table, self.id)
                          (', '.join(names), res_msg)
                  )
          if errors:
-             raise except_orm('ValidateError', '\n'.join(errors))
+             raise ValidationError('\n'.join(errors))
  
          # new-style constraint methods
          for check in self._constraint_methods:
              if set(check._constrains) & field_names:
-                 check(self)
+                 try:
+                     check(self)
+                 except ValidationError, e:
+                     raise
+                 except Exception, e:
+                     raise ValidationError("Error while validating constraint\n\n%s" % tools.ustr(e))
  
      def default_get(self, cr, uid, fields_list, context=None):
-         """ Return default values for the fields in `fields_list`. Default
-             values are determined by the context, user defaults, and the model
-             itself.
-             :param fields_list: a list of field names
-             :return: a dictionary mapping each field name to its corresponding
-                 default value; the keys of the dictionary are the fields in
-                 `fields_list` that have a default value different from ``False``.
-             This method should not be overridden. In order to change the
-             mechanism for determining default values, you should override method
-             :meth:`add_default_value` instead.
+         """ default_get(fields) -> default_values
+         Return default values for the fields in `fields_list`. Default
+         values are determined by the context, user defaults, and the model
+         itself.
+         :param fields_list: a list of field names
+         :return: a dictionary mapping each field name to its corresponding
+             default value; the keys of the dictionary are the fields in
+             `fields_list` that have a default value different from ``False``.
+         This method should not be overridden. In order to change the
+         mechanism for determining default values, you should override method
+         :meth:`add_default_value` instead.
          """
          # trigger view init hook
          self.view_init(cr, uid, fields_list, context)
  
-         # use a new record to determine default values
+         # use a new record to determine default values; evaluate fields on the
+         # new record and put default values in result
          record = self.new(cr, uid, {}, context=context)
+         result = {}
          for name in fields_list:
              if name in self._fields:
-                 record[name]            # force evaluation of defaults
+                 value = record[name]
+                 if name in record._cache:
+                     result[name] = value        # it really is a default value
  
-         # retrieve defaults from record's cache
-         return self._convert_to_write(record._cache)
+         # convert default values to the expected format
+         result = self._convert_to_write(result)
+         return result
  
      def add_default_value(self, field):
          """ Set the default value of `field` to the new record `self`.
          return view
  
      def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
-         """
+         """ fields_view_get([view_id | view_type='form'])
          Get the detailed composition of the requested view like fields, model, view architecture
  
          :param view_id: id of the view or None
          """
          view_id = self.get_formview_id(cr, uid, id, context=context)
          return {
-                 'type': 'ir.actions.act_window',
-                 'res_model': self._name,
-                 'view_type': 'form',
-                 'view_mode': 'form',
-                 'views': [(view_id, 'form')],
-                 'target': 'current',
-                 'res_id': id,
-             }
+             'type': 'ir.actions.act_window',
+             'res_model': self._name,
+             'view_type': 'form',
+             'view_mode': 'form',
+             'views': [(view_id, 'form')],
+             'target': 'current',
+             'res_id': id,
+         }
+     def get_access_action(self, cr, uid, id, context=None):
+         """ Return an action to open the document. This method is meant to be
+         overridden in addons that want to give specific access to the document.
+         By default it opens the formview of the document.
+         :paramt int id: id of the document to open
+         """
+         return self.get_formview_action(cr, uid, id, context=context)
  
      def _view_look_dom_arch(self, cr, uid, node, view_id, context=None):
          return self.pool['ir.ui.view'].postprocess_and_fields(
              cr, uid, self._name, node, view_id, context=context)
  
      def search_count(self, cr, user, args, context=None):
+         """ search_count(args) -> int
+         Returns the number of records in the current model matching :ref:`the
+         provided domain <reference/orm/domains>`.
+         """
          res = self.search(cr, user, args, context=context, count=True)
          if isinstance(res, list):
              return len(res)
  
      @api.returns('self')
      def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
-         """
-         Search for records based on a search domain.
+         """ search(args[, offset=0][, limit=None][, order=None][, count=False])
  
-         :param cr: database cursor
-         :param user: current user id
-         :param args: list of tuples specifying the search domain [('field_name', 'operator', value), ...]. Pass an empty list to match all records.
-         :param offset: optional number of results to skip in the returned values (default: 0)
-         :param limit: optional max number of records to return (default: **None**)
-         :param order: optional columns to sort by (default: self._order=id )
-         :param context: optional context arguments, like lang, time zone
-         :type context: dictionary
-         :param count: optional (default: **False**), if **True**, returns only the number of records matching the criteria, not their ids
-         :return: id or list of ids of records matching the criteria
-         :rtype: integer or list of integers
-         :raise AccessError: * if user tries to bypass access rules for read on the requested object.
-         **Expressing a search domain (args)**
-         Each tuple in the search domain needs to have 3 elements, in the form: **('field_name', 'operator', value)**, where:
-             * **field_name** must be a valid name of field of the object model, possibly following many-to-one relationships using dot-notation, e.g 'street' or 'partner_id.country' are valid values.
-             * **operator** must be a string with a valid comparison operator from this list: ``=, !=, >, >=, <, <=, like, ilike, in, not in, child_of, parent_left, parent_right``
-               The semantics of most of these operators are obvious.
-               The ``child_of`` operator will look for records who are children or grand-children of a given record,
-               according to the semantics of this model (i.e following the relationship field named by
-               ``self._parent_name``, by default ``parent_id``.
-             * **value** must be a valid value to compare with the values of **field_name**, depending on its type.
-         Domain criteria can be combined using 3 logical operators than can be added between tuples:  '**&**' (logical AND, default), '**|**' (logical OR), '**!**' (logical NOT).
-         These are **prefix** operators and the arity of the '**&**' and '**|**' operator is 2, while the arity of the '**!**' is just 1.
-         Be very careful about this when you combine them the first time.
-         Here is an example of searching for Partners named *ABC* from Belgium and Germany whose language is not english ::
-             [('name','=','ABC'),'!',('language.code','=','en_US'),'|',('country_id.code','=','be'),('country_id.code','=','de'))
+         Searches for records based on the ``args``
+         :ref:`search domain <reference/orm/domains>`.
  
-         The '&' is omitted as it is the default, and of course we could have used '!=' for the language, but what this domain really represents is::
-             (name is 'ABC' AND (language is NOT english) AND (country is Belgium OR Germany))
+         :param args: :ref:`A search domain <reference/orm/domains>`. Use an empty
+                      list to match all records.
+         :param int offset: number of results to ignore (default: none)
+         :param int limit: maximum number of records to return (default: all)
+         :param str order: sort string
+         :param bool count: if ``True``, the call should return the number of
+                            records matching ``args`` rather than the records
+                            themselves.
+         :returns: at most ``limit`` records matching the search criteria
  
+         :raise AccessError: * if user tries to bypass access rules for read on the requested object.
          """
          return self._search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count)
  
  
      @api.multi
      def name_get(self):
-         """ Return a textual representation for the records in `self`.
-             By default this is the value of field ``display_name``.
+         """ name_get() -> [(id, name), ...]
+         Returns a textual representation for the records in ``self``.
+         By default this is the value of the ``display_name`` field.
  
-             :rtype: list(tuple)
-             :return: list of pairs ``(id, text_repr)`` for all records
+         :return: list of pairs ``(id, text_repr)`` for each records
+         :rtype: list(tuple)
          """
          result = []
          name = self._rec_name
  
      @api.model
      def name_create(self, name):
-         """ Create a new record by calling :meth:`~.create` with only one value
-             provided: the display name of the new record.
+         """ name_create(name) -> record
  
-             The new record will be initialized with any default values
-             applicable to this model, or provided through the context. The usual
-             behavior of :meth:`~.create` applies.
+         Create a new record by calling :meth:`~.create` with only one value
+         provided: the display name of the new record.
  
-             :param name: display name of the record to create
-             :rtype: tuple
-             :return: the :meth:`~.name_get` pair value of the created record
+         The new record will be initialized with any default values
+         applicable to this model, or provided through the context. The usual
+         behavior of :meth:`~.create` applies.
+         :param name: display name of the record to create
+         :rtype: tuple
+         :return: the :meth:`~.name_get` pair value of the created record
          """
          if self._rec_name:
              record = self.create({self._rec_name: name})
  
      @api.model
      def name_search(self, name='', args=None, operator='ilike', limit=100):
-         """ Search for records that have a display name matching the given
-             `name` pattern when compared with the given `operator`, while also
-             matching the optional search domain (`args`).
-             This is used for example to provide suggestions based on a partial
-             value for a relational field. Sometimes be seen as the inverse
-             function of :meth:`~.name_get`, but it is not guaranteed to be.
-             This method is equivalent to calling :meth:`~.search` with a search
-             domain based on `display_name` and then :meth:`~.name_get` on the
-             result of the search.
-             :param name: the name pattern to match
-             :param list args: optional search domain (see :meth:`~.search` for
-                 syntax), specifying further restrictions
-             :param str operator: domain operator for matching `name`, such as
-                 ``'like'`` or ``'='``.
-             :param int limit: optional max number of records to return
-             :rtype: list
-             :return: list of pairs ``(id, text_repr)`` for all matching records.
+         """ name_search(name='', args=None, operator='ilike', limit=100) -> records
+         Search for records that have a display name matching the given
+         `name` pattern when compared with the given `operator`, while also
+         matching the optional search domain (`args`).
+         This is used for example to provide suggestions based on a partial
+         value for a relational field. Sometimes be seen as the inverse
+         function of :meth:`~.name_get`, but it is not guaranteed to be.
+         This method is equivalent to calling :meth:`~.search` with a search
+         domain based on ``display_name`` and then :meth:`~.name_get` on the
+         result of the search.
+         :param str name: the name pattern to match
+         :param list args: optional search domain (see :meth:`~.search` for
+                           syntax), specifying further restrictions
+         :param str operator: domain operator for matching `name`, such as
+                              ``'like'`` or ``'='``.
+         :param int limit: optional max number of records to return
+         :rtype: list
+         :return: list of pairs ``(id, text_repr)`` for all matching records.
          """
-         args = list(args or [])
-         if not self._rec_name:
-             _logger.warning("Cannot execute name_search, no _rec_name defined on %s", self._name)
-         elif not (name == '' and operator == 'ilike'):
-             args += [(self._rec_name, operator, name)]
-         return self.search(args, limit=limit).name_get()
+         return self._name_search(name, args, operator, limit=limit)
  
      def _name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100, name_get_uid=None):
          # private implementation of name_search, allows passing a dedicated user
              f for f in fields
              if f not in ('id', 'sequence')
              if f not in groupby_fields
+             if f in self._all_columns
              if self._all_columns[f].column._type in ('integer', 'float')
              if getattr(self._all_columns[f].column, '_classic_write')]
  
              if column_name in defaults:
                  default = field.convert_to_write(defaults[column_name])
  
-         ss = self._columns[column_name]._symbol_set
-         store_default = ss[1](default)
-         if store_default is not None:
+         column = self._columns[column_name]
+         ss = column._symbol_set
+         db_default = ss[1](default)
+         # Write default if non-NULL, except for booleans for which False means
+         # the same as NULL - this saves us an expensive query on large tables.
+         write_default = (db_default is not None if column._type != 'boolean'
+                             else db_default)
+         if write_default:
              _logger.debug("Table '%s': setting default value of new column %s to %r",
                            self._table, column_name, default)
              query = 'UPDATE "%s" SET "%s"=%s WHERE "%s" is NULL' % (
                  self._table, column_name, ss[0], column_name)
-             cr.execute(query, (store_default,))
+             cr.execute(query, (db_default,))
              # this is a disgrace
              cr.commit()
  
                  self._create_table(cr)
                  has_rows = False
              else:
-                 cr.execute('SELECT COUNT(1) FROM "%s"' % (self._table,))
-                 has_rows = cr.fetchone()[0]
+                 cr.execute('SELECT min(id) FROM "%s"' % (self._table,))
+                 has_rows = cr.fetchone()[0] is not None
  
              cr.commit()
              if self._parent_store:
  
                              if isinstance(f, fields.many2one) or (isinstance(f, fields.function) and f._type == 'many2one' and f.store):
                                  dest_model = self.pool[f._obj]
-                                 if dest_model._table != 'ir_actions':
+                                 if dest_model._auto and dest_model._table != 'ir_actions':
                                      self._m2o_fix_foreign_key(cr, self._table, k, dest_model, f.ondelete)
  
                      # The field doesn't exist in database. Create it if necessary.
                                  dest_model = self.pool[f._obj]
                                  ref = dest_model._table
                                  # ir_actions is inherited so foreign key doesn't work on it
-                                 if ref != 'ir_actions':
+                                 if dest_model._auto and ref != 'ir_actions':
                                      self._m2o_add_foreign_key_checked(k, dest_model, f.ondelete)
                              if f.select:
                                  cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k))
          for parent_model, parent_field in reversed(cls._inherits.items()):
              for attr, field in cls.pool[parent_model]._fields.iteritems():
                  if attr not in cls._fields:
-                     new_field = field.copy(related=(parent_field, attr), _origin=field)
-                     cls._add_field(attr, new_field)
+                     cls._add_field(attr, field.copy(
+                         related=(parent_field, attr),
+                         related_sudo=False,
+                         _origin=field,
+                     ))
  
          cls._inherits_reload_src()
  
              field.reset()
  
      @api.model
-     def _setup_fields(self):
+     def _setup_fields(self, partial=False):
          """ Setup the fields (dependency triggers, etc). """
          for field in self._fields.itervalues():
+             if partial and field.manual and \
+                     field.relational and field.comodel_name not in self.pool:
+                 # do not set up manual fields that refer to unknown models
+                 continue
              field.setup(self.env)
  
          # group fields by compute to determine field.computed_fields
                  field.computed_fields = []
  
      def fields_get(self, cr, user, allfields=None, context=None, write_access=True):
-         """ Return the definition of each field.
+         """ fields_get([fields])
+         Return the definition of each field.
  
          The returned value is a dictionary (indiced by field name) of
          dictionaries. The _inherits'd fields are included. The string, help,
  
          return fields
  
-     # new-style implementation of read(); old-style is defined below
+     # add explicit old-style implementation to read()
+     @api.v7
+     def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
+         records = self.browse(cr, user, ids, context)
+         result = BaseModel.read(records, fields, load=load)
+         return result if isinstance(ids, list) else (bool(result) and result[0])
+     # new-style implementation of read()
      @api.v8
      def read(self, fields=None, load='_classic_read'):
-         """ Read the given fields for the records in `self`.
-             :param fields: optional list of field names to return (default is
-                     all fields)
-             :param load: deprecated, this argument is ignored
-             :return: a list of dictionaries mapping field names to their values,
-                     with one dictionary per record
-             :raise AccessError: if user has no read rights on some of the given
-                     records
+         """ read([fields])
+         Reads the requested fields for the records in `self`, low-level/RPC
+         method. In Python code, prefer :meth:`~.browse`.
+         :param fields: list of field names to return (default is all fields)
+         :return: a list of dictionaries mapping field names to their values,
+                  with one dictionary per record
+         :raise AccessError: if user has no read rights on some of the given
+                 records
          """
          # check access rights
          self.check_access_rights('read')
  
          return result
  
-     # add explicit old-style implementation to read()
-     @api.v7
-     def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
-         records = self.browse(cr, user, ids, context)
-         result = BaseModel.read(records, fields, load=load)
-         return result if isinstance(ids, list) else (bool(result) and result[0])
      @api.multi
      def _prefetch_field(self, field):
          """ Read from the database in order to fetch `field` (:class:`Field`
              instance) for `self` in cache.
          """
          # fetch the records of this model without field_name in their cache
-         records = self
+         records = self._in_cache_without(field)
+         if len(records) > PREFETCH_MAX:
+             records = records[:PREFETCH_MAX] | self
  
          # by default, simply fetch field
          fnames = {field.name}
          if self.env.in_draft:
              # we may be doing an onchange, do not prefetch other fields
              pass
-         elif field in self.env.todo:
+         elif self.env.field_todo(field):
              # field must be recomputed, do not prefetch records to recompute
-             records -= self.env.todo[field]
+             records -= self.env.field_todo(field)
          elif self._columns[field.name]._prefetch:
              # here we can optimize: prefetch all classic and many2one fields
              fnames = set(fname
                  for fname, fcolumn in self._columns.iteritems()
-                 if fcolumn._prefetch)
+                 if fcolumn._prefetch
+                 if not fcolumn.groups or self.user_has_groups(fcolumn.groups)
+             )
  
          # fetch records with read()
          assert self in records and field.name in fnames
              pass
  
          # check the cache, and update it if necessary
-         if field not in self._cache:
+         if not self._cache.contains(field):
              for values in result:
                  record = self.browse(values.pop('id'))
                  record._cache.update(record._convert_to_cache(values, validate=False))
-             if field not in self._cache:
+             if not self._cache.contains(field):
                  e = AccessError("No value found for %s.%s" % (self, field.name))
                  self._cache[field] = FailedValue(e)
  
          env = self.env
          cr, user, context = env.args
  
+         # FIXME: The query construction needs to be rewritten using the internal Query
+         # object, as in search(), to avoid ambiguous column references when
+         # reading/sorting on a table that is auto_joined to another table with
+         # common columns (e.g. the magical columns)
          # Construct a clause for the security rules.
          # 'tables' holds the list of tables necessary for the SELECT, including
          # the ir.rule clauses, and contains at least self._table.
                      'order': self._parent_order or self._order,
                  }
  
-         empty = self.browse()
-         prefetch = set()
-         todo = set()
-         for field in (self._fields[name] for name in field_names):
-             prefetch.update(self._in_cache_without(field).ids)
-             todo.update(self.env.todo.get(field, empty).ids)
-         records = self.browse(prefetch - todo | set(self.ids))
          result = []
-         for sub_ids in cr.split_for_in_conditions(records.ids):
+         for sub_ids in cr.split_for_in_conditions(self.ids):
              cr.execute(query, [tuple(sub_ids)] + rule_params)
              result.extend(cr.dictfetchall())
  
  
          # store failed values in cache for the records that could not be read
          fetched = self.browse(ids)
-         missing = records - fetched
+         missing = self - fetched
          if missing:
-             extras = fetched - records
+             extras = fetched - self
              if extras:
                  raise AccessError(
                      _("Database fetch misses ids ({}) and has extra ids ({}), may be caused by a type incoherence in a previous request").format(
          return True
  
      def unlink(self, cr, uid, ids, context=None):
-         """
-         Delete records with given ids
+         """ unlink()
+         Deletes the records of the current set
  
-         :param cr: database cursor
-         :param uid: current user id
-         :param ids: id or list of ids
-         :param context: (optional) context arguments, like lang, time zone
-         :return: True
          :raise AccessError: * if user has no unlink rights on the requested object
                              * if user tries to bypass access rules for unlink on the requested object
          :raise UserError: if the record is default property for other records
      #
      @api.multi
      def write(self, vals):
-         """
-         Update records in `self` with the given field values.
-         :param vals: field values to update, e.g {'field_name': new_field_value, ...}
-         :type vals: dictionary
-         :return: True
-         :raise AccessError: * if user has no write rights on the requested object
-                             * if user tries to bypass access rules for write on the requested object
-         :raise ValidateError: if user tries to enter invalid value for a field that is not in selection
-         :raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
+         """ write(vals)
  
-         **Note**: The type of field values to pass in ``vals`` for relationship fields is specific:
+         Updates all records in the current set with the provided values.
  
-             + For a many2many field, a list of tuples is expected.
-               Here is the list of tuple that are accepted, with the corresponding semantics ::
+         :param dict vals: fields to update and the value to set on them e.g::
  
-                  (0, 0,  { values })    link to a new record that needs to be created with the given values dictionary
-                  (1, ID, { values })    update the linked record with id = ID (write *values* on it)
-                  (2, ID)                remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
-                  (3, ID)                cut the link to the linked record with id = ID (delete the relationship between the two objects but does not delete the target object itself)
-                  (4, ID)                link to existing record with id = ID (adds a relationship)
-                  (5)                    unlink all (like using (3,ID) for all linked records)
-                  (6, 0, [IDs])          replace the list of linked IDs (like using (5) then (4,ID) for each ID in the list of IDs)
+                 {'foo': 1, 'bar': "Qux"}
  
-                  Example:
-                     [(6, 0, [8, 5, 6, 4])] sets the many2many to ids [8, 5, 6, 4]
+             will set the field ``foo`` to ``1`` and the field ``bar`` to
+             ``"Qux"`` if those are valid (otherwise it will trigger an error).
  
-             + For a one2many field, a lits of tuples is expected.
-               Here is the list of tuple that are accepted, with the corresponding semantics ::
-                  (0, 0,  { values })    link to a new record that needs to be created with the given values dictionary
-                  (1, ID, { values })    update the linked record with id = ID (write *values* on it)
-                  (2, ID)                remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
-                  Example:
-                     [(0, 0, {'field_name':field_value_record1, ...}), (0, 0, {'field_name':field_value_record2, ...})]
-             + For a many2one field, simply use the ID of target record, which must already exist, or ``False`` to remove the link.
-             + For a reference field, use a string with the model name, a comma, and the target object id (example: ``'product.product, 5'``)
+         :raise AccessError: * if user has no write rights on the requested object
+                             * if user tries to bypass access rules for write on the requested object
+         :raise ValidateError: if user tries to enter invalid value for a field that is not in selection
+         :raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
  
+         .. _openerp/models/relationals/format:
+         .. note:: Relational fields use a special "commands" format to manipulate their values
+             This format is a list of command triplets executed sequentially,
+             possible command triplets are:
+             ``(0, _, values: dict)``
+                 links to a new record created from the provided values
+             ``(1, id, values: dict)``
+                 updates the already-linked record of id ``id`` with the
+                 provided ``values``
+             ``(2, id, _)``
+                 unlinks and deletes the linked record of id ``id``
+             ``(3, id, _)``
+                 unlinks the linked record of id ``id`` without deleting it
+             ``(4, id, _)``
+                 links to an existing record of id ``id``
+             ``(5, _, _)``
+                 unlinks all records in the relation, equivalent to using
+                 the command ``3`` on every linked record
+             ``(6, _, ids)``
+                 replaces the existing list of linked records by the provoded
+                 ones, equivalent to using ``5`` then ``4`` for each id in
+                 ``ids``)
+             (in command triplets, ``_`` values are ignored and can be
+             anything, generally ``0`` or ``False``)
+             Any command can be used on :class:`~openerp.fields.Many2many`,
+             only ``0``, ``1`` and ``2`` can be used on
+             :class:`~openerp.fields.One2many`.
          """
          if not self:
              return True
  
          # put the values of pure new-style fields into cache, and inverse them
          if new_vals:
-             self._cache.update(self._convert_to_cache(new_vals))
+             for record in self:
+                 record._cache.update(record._convert_to_cache(new_vals, update=True))
              for key in new_vals:
                  self._fields[key].determine_inverse(self)
  
      @api.model
      @api.returns('self', lambda value: value.id)
      def create(self, vals):
-         """ Create a new record for the model.
-             The values for the new record are initialized using the dictionary
-             `vals`, and if necessary the result of :meth:`default_get`.
-             :param vals: field values like ``{'field_name': field_value, ...}``,
-                 see :meth:`write` for details about the values format
-             :return: new record created
-             :raise AccessError: * if user has no create rights on the requested object
-                                 * if user tries to bypass access rules for create on the requested object
-             :raise ValidateError: if user tries to enter invalid value for a field that is not in selection
-             :raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
+         """ create(vals) -> record
+         Creates a new record for the model.
+         The new record is initialized using the values from ``vals`` and
+         if necessary those from :meth:`~.default_get`.
+         :param dict vals:
+             values for the model's fields, as a dictionary::
+                 {'field_name': field_value, ...}
+             see :meth:`~.write` for details
+         :return: new record created
+         :raise AccessError: * if user has no create rights on the requested object
+                             * if user tries to bypass access rules for create on the requested object
+         :raise ValidateError: if user tries to enter invalid value for a field that is not in selection
+         :raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
          """
          self.check_access_rights('create')
  
  
              record_id = tocreate[table].pop('id', None)
  
-             if isinstance(record_id, dict):
-                 # Shit happens: this possibly comes from a new record
-                 tocreate[table] = dict(record_id, **tocreate[table])
-                 record_id = None
-             # When linking/creating parent records, force context without 'no_store_function' key that
-             # defers stored functions computing, as these won't be computed in batch at the end of create().
-             parent_context = dict(context)
-             parent_context.pop('no_store_function', None)
              if record_id is None or not record_id:
-                 record_id = self.pool[table].create(cr, user, tocreate[table], context=parent_context)
+                 record_id = self.pool[table].create(cr, user, tocreate[table], context=context)
              else:
-                 self.pool[table].write(cr, user, [record_id], tocreate[table], context=parent_context)
+                 self.pool[table].write(cr, user, [record_id], tocreate[table], context=context)
  
              updates.append((self._inherits[table], '%s', record_id))
  
          # check Python constraints
          recs._validate_fields(vals)
  
-         if not context.get('no_store_function', False):
+         # invalidate and mark new-style fields to recompute
+         modified_fields = list(vals)
+         if self._log_access:
+             modified_fields += ['create_uid', 'create_date', 'write_uid', 'write_date']
+         recs.modified(modified_fields)
+         if context.get('recompute', True):
              result += self._store_get_values(cr, user, [id_new],
                  list(set(vals.keys() + self._inherits.values())),
                  context)
                  if not (model_name, ids, fields2) in done:
                      self.pool[model_name]._store_set_values(cr, user, ids, fields2, context)
                      done.append((model_name, ids, fields2))
              # recompute new-style fields
-             modified_fields = list(vals)
-             if self._log_access:
-                 modified_fields += ['create_uid', 'create_date', 'write_uid', 'write_date']
-             recs.modified(modified_fields)
              recs.recompute()
  
-         if self._log_create and not (context and context.get('no_store_function', False)):
+         if self._log_create and context.get('recompute', True):
              message = self._description + \
                  " '" + \
                  self.name_get(cr, user, [id_new], context=context)[0][1] + \
                          cr.execute('update "' + self._table + '" set ' + \
                              '"'+f+'"='+self._columns[f]._symbol_set[0] + ' where id = %s', (self._columns[f]._symbol_set[1](value), id))
  
-         # invalidate the cache for the modified fields
+         # invalidate and mark new-style fields to recompute
          self.browse(cr, uid, ids, context).modified(fields)
  
          return True
                  order_split = order_part.strip().split(' ')
                  order_field = order_split[0].strip()
                  order_direction = order_split[1].strip() if len(order_split) == 2 else ''
+                 order_column = None
                  inner_clause = None
                  if order_field == 'id':
                      order_by_elements.append('"%s"."%s" %s' % (self._table, order_field, order_direction))
                          continue  # ignore non-readable or "non-joinable" fields
                  else:
                      raise ValueError( _("Sorting field %s not found on model %s") %( order_field, self._name))
+                 if order_column and order_column._type == 'boolean':
+                     inner_clause = "COALESCE(%s, false)" % inner_clause
                  if inner_clause:
                      if isinstance(inner_clause, list):
                          for clause in inner_clause:
  
      @api.returns('self', lambda value: value.id)
      def copy(self, cr, uid, id, default=None, context=None):
-         """
+         """ copy(default=None)
          Duplicate record with given id updating it with default values
  
-         :param cr: database cursor
-         :param uid: current user id
-         :param id: id of the record to copy
-         :param default: dictionary of field values to override in the original values of the copied record, e.g: ``{'field_name': overriden_value, ...}``
-         :type default: dictionary
-         :param context: context arguments, like lang, time zone
-         :type context: dictionary
-         :return: id of the newly created record
+         :param dict default: dictionary of field values to override in the
+                original values of the copied record, e.g: ``{'field_name': overriden_value, ...}``
+         :returns: new record
  
          """
          if context is None:
      @api.multi
      @api.returns('self')
      def exists(self):
-         """ Return the subset of records in `self` that exist, and mark deleted
-             records as such in cache. It can be used as a test on records::
+         """  exists() -> records
+         Returns the subset of records in `self` that exist, and marks deleted
+         records as such in cache. It can be used as a test on records::
  
-                 if record.exists():
-                     ...
+             if record.exists():
+                 ...
  
-             By convention, new records are returned as existing.
+         By convention, new records are returned as existing.
          """
          ids = filter(None, self._ids)           # ids to check in database
          if not ids:
          """ stuff to do right after the registry is built """
          pass
  
-     def _patch_method(self, name, method):
+     @classmethod
+     def _patch_method(cls, name, method):
          """ Monkey-patch a method for all instances of this model. This replaces
-             the method called `name` by `method` in `self`'s class.
+             the method called `name` by `method` in the given class.
              The original method is then accessible via ``method.origin``, and it
              can be restored with :meth:`~._revert_method`.
  
                  # restore the original method
                  model._revert_method('write')
          """
-         cls = type(self)
          origin = getattr(cls, name)
          method.origin = origin
          # propagate decorators from origin to method, and apply api decorator
          wrapped.origin = origin
          setattr(cls, name, wrapped)
  
-     def _revert_method(self, name):
-         """ Revert the original method of `self` called `name`.
+     @classmethod
+     def _revert_method(cls, name):
+         """ Revert the original method called `name` in the given class.
              See :meth:`~._patch_method`.
          """
-         cls = type(self)
          method = getattr(cls, name)
          setattr(cls, name, method.origin)
  
          env.prefetch[cls._name].update(ids)
          return records
  
+     @api.v7
+     def browse(self, cr, uid, arg=None, context=None):
+         ids = _normalize_ids(arg)
+         #assert all(isinstance(id, IdType) for id in ids), "Browsing invalid ids: %s" % ids
+         return self._browse(Environment(cr, uid, context or {}), ids)
      @api.v8
      def browse(self, arg=None):
-         """ Return an instance corresponding to `arg` and attached to
-             `self.env`; `arg` is either a record id, or a collection of record ids.
+         """ browse([ids]) -> records
+         Returns a recordset for the ids provided as parameter in the current
+         environment.
+         Can take no ids, a single id or a sequence of ids.
          """
          ids = _normalize_ids(arg)
          #assert all(isinstance(id, IdType) for id in ids), "Browsing invalid ids: %s" % ids
          return self._browse(self.env, ids)
  
-     @api.v7
-     def browse(self, cr, uid, arg=None, context=None):
-         ids = _normalize_ids(arg)
-         #assert all(isinstance(id, IdType) for id in ids), "Browsing invalid ids: %s" % ids
-         return self._browse(Environment(cr, uid, context or {}), ids)
      #
      # Internal properties, for manipulating the instance's implementation
      #
  
      @property
      def ids(self):
-         """ Return the list of non-false record ids of this instance. """
+         """ List of actual record ids in this recordset (ignores placeholder
+         ids for records to create)
+         """
          return filter(None, list(self._ids))
  
      # backward-compatibility with former browse records
      #
  
      def ensure_one(self):
-         """ Return `self` if it is a singleton instance, otherwise raise an
-             exception.
+         """ Verifies that the current recorset holds a single record. Raises
+         an exception otherwise.
          """
          if len(self) == 1:
              return self
          raise except_orm("ValueError", "Expected singleton: %s" % self)
  
      def with_env(self, env):
-         """ Return an instance equivalent to `self` attached to `env`.
+         """ Returns a new version of this recordset attached to the provided
+         environment
+         :type env: :class:`~openerp.api.Environment`
          """
          return self._browse(env, self._ids)
  
      def sudo(self, user=SUPERUSER_ID):
-         """ Return an instance equivalent to `self` attached to an environment
-             based on `self.env` with the given `user`.
+         """ sudo([user=SUPERUSER])
+         Returns a new version of this recordset attached to the provided
+         user.
          """
          return self.with_env(self.env(user=user))
  
      def with_context(self, *args, **kwargs):
-         """ Return an instance equivalent to `self` attached to an environment
-             based on `self.env` with another context. The context is given by
-             `self._context` or the positional argument if given, and modified by
-             `kwargs`.
+         """ with_context([context][, **overrides]) -> records
+         Returns a new version of this recordset attached to an extended
+         context.
+         The extended context is either the provided ``context`` in which
+         ``overrides`` are merged or the *current* context in which
+         ``overrides`` are merged e.g.::
+             # current context is {'key1': True}
+             r2 = records.with_context({}, key2=True)
+             # -> r2._context is {'key2': True}
+             r2 = records.with_context(key2=True)
+             # -> r2._context is {'key1': True, 'key2': True}
          """
          context = dict(args[0] if args else self._context, **kwargs)
          return self.with_env(self.env(context=context))
  
-     def _convert_to_cache(self, values, validate=True):
-         """ Convert the `values` dictionary into cached values. """
+     def _convert_to_cache(self, values, update=False, validate=True):
+         """ Convert the `values` dictionary into cached values.
+             :param update: whether the conversion is made for updating `self`;
+                 this is necessary for interpreting the commands of *2many fields
+             :param validate: whether values must be checked
+         """
          fields = self._fields
+         target = self if update else self.browse()
          return {
-             name: fields[name].convert_to_cache(value, self.env, validate=validate)
+             name: fields[name].convert_to_cache(value, target, validate=validate)
              for name, value in values.iteritems()
              if name in fields
          }
      def _convert_to_write(self, values):
          """ Convert the `values` dictionary into the format of :meth:`write`. """
          fields = self._fields
-         return dict(
-             (name, fields[name].convert_to_write(value))
-             for name, value in values.iteritems()
-             if name in self._fields
-         )
+         result = {}
+         for name, value in values.iteritems():
+             if name in fields:
+                 value = fields[name].convert_to_write(value)
+                 if not isinstance(value, NewId):
+                     result[name] = value
+         return result
  
      #
      # Record traversal and update
  
      @api.model
      def new(self, values={}):
-         """ Return a new record instance attached to `self.env`, and
-             initialized with the `values` dictionary. Such a record does not
-             exist in the database.
+         """ new([values]) -> record
+         Return a new record instance attached to the current environment and
+         initialized with the provided ``value``. The record is *not* created
+         in database, it only exists in memory.
          """
          record = self.browse([NewId()])
-         record._cache.update(self._convert_to_cache(values))
+         record._cache.update(record._convert_to_cache(values, update=True))
  
          if record.env.in_onchange:
              # The cache update does not set inverse fields, so do it manually.
              # records, if that field depends on the main record.
              for name in values:
                  field = self._fields.get(name)
-                 if field and field.inverse_field:
-                     field.inverse_field._update(record[name], record)
+                 if field:
+                     for invf in field.inverse_fields:
+                         invf._update(record[name], record)
  
          return record
  
              yield self._browse(self.env, (id,))
  
      def __contains__(self, item):
-         """ Test whether `item` is a subset of `self` or a field name. """
-         if isinstance(item, BaseModel):
-             if self._name == item._name:
-                 return set(item._ids) <= set(self._ids)
-             raise except_orm("ValueError", "Mixing apples and oranges: %s in %s" % (item, self))
-         if isinstance(item, basestring):
+         """ Test whether `item` (record or field name) is an element of `self`.
+             In the first case, the test is fully equivalent to::
+                 any(item == record for record in self)
+         """
+         if isinstance(item, BaseModel) and self._name == item._name:
+             return len(item) == 1 and item.id in self._ids
+         elif isinstance(item, basestring):
              return item in self._fields
-         return item in self.ids
+         else:
+             raise except_orm("ValueError", "Mixing apples and oranges: %s in %s" % (item, self))
  
      def __add__(self, other):
          """ Return the concatenation of two recordsets. """
  
          # invalidate fields and inverse fields, too
          spec = [(f, ids) for f in fields] + \
-                [(f.inverse_field, None) for f in fields if f.inverse_field]
+                [(invf, None) for f in fields for invf in f.inverse_fields]
          self.env.invalidate(spec)
  
      @api.multi
          """ If `field` must be recomputed on some record in `self`, return the
              corresponding records that must be recomputed.
          """
-         for env in [self.env] + list(iter(self.env.all)):
-             if env.todo.get(field) and env.todo[field] & self:
-                 return env.todo[field]
+         return self.env.check_todo(field, self)
  
      def _recompute_todo(self, field):
          """ Mark `field` to be recomputed. """
-         todo = self.env.todo
-         todo[field] = (todo.get(field) or self.browse()) | self
+         self.env.add_todo(field, self)
  
      def _recompute_done(self, field):
-         """ Mark `field` as being recomputed. """
-         todo = self.env.todo
-         if field in todo:
-             recs = todo.pop(field) - self
-             if recs:
-                 todo[field] = recs
+         """ Mark `field` as recomputed. """
+         self.env.remove_todo(field, self)
  
      @api.model
      def recompute(self):
          """ Recompute stored function fields. The fields and records to
              recompute have been determined by method :meth:`modified`.
          """
-         for env in list(iter(self.env.all)):
-             while env.todo:
-                 field, recs = next(env.todo.iteritems())
-                 # evaluate the fields to recompute, and save them to database
-                 for rec, rec1 in zip(recs, recs.with_context(recompute=False)):
-                     try:
-                         values = rec._convert_to_write({
-                             f.name: rec[f.name] for f in field.computed_fields
-                         })
-                         rec1._write(values)
-                     except MissingError:
-                         pass
-                 # mark the computed fields as done
-                 map(recs._recompute_done, field.computed_fields)
+         while self.env.has_todo():
+             field, recs = self.env.get_todo()
+             # evaluate the fields to recompute, and save them to database
+             for rec, rec1 in zip(recs, recs.with_context(recompute=False)):
+                 try:
+                     values = rec._convert_to_write({
+                         f.name: rec[f.name] for f in field.computed_fields
+                     })
+                     rec1._write(values)
+                 except MissingError:
+                     pass
+             # mark the computed fields as done
+             map(recs._recompute_done, field.computed_fields)
  
      #
      # Generic onchange method
  
          # dummy assignment: trigger invalidations on the record
          for name in todo:
-             record[name] = record[name]
+             value = record[name]
+             field = self._fields[name]
+             if not field_name and field.type == 'many2one' and field.delegate and not value:
+                 # do not nullify all fields of parent record for new records
+                 continue
+             record[name] = value
  
          result = {'value': {}}
  
                      if newval != oldval or getattr(newval, '_dirty', False):
                          field = self._fields[name]
                          result['value'][name] = field.convert_to_write(
-                             newval, record._origin, subfields[name],
+                             newval, record._origin, subfields.get(name),
                          )
                          todo.add(name)
  
@@@ -5639,12 -5714,20 +5714,20 @@@ class RecordCache(MutableMapping)
      def __init__(self, records):
          self._recs = records
  
-     def __contains__(self, field):
+     def contains(self, field):
          """ Return whether `records[0]` has a value for `field` in cache. """
          if isinstance(field, basestring):
              field = self._recs._fields[field]
          return self._recs.id in self._recs.env.cache[field]
  
+     def __contains__(self, field):
+         """ Return whether `records[0]` has a regular value for `field` in cache. """
+         if isinstance(field, basestring):
+             field = self._recs._fields[field]
+         dummy = SpecialValue(None)
+         value = self._recs.env.cache[field].get(self._recs.id, dummy)
+         return not isinstance(value, SpecialValue)
      def __getitem__(self, field):
          """ Return the cached value of `field` for `records[0]`. """
          if isinstance(field, basestring):
@@@ -154,7 -154,7 +154,7 @@@ def get_module_filetree(module, dir='.'
  
      return tree
  
 -def get_module_resource(module, *args):
 +def get_resource_path(module, *args):
      """Return the full path of a resource of the given module.
  
      :param module: module name
      :rtype: str
      :return: absolute path to the resource
  
 -    TODO name it get_resource_path
      TODO make it available inside on osv object (self.get_resource_path)
      """
      mod_path = get_module_path(module)
              return resource_path
      return False
  
 +# backwards compatibility
 +get_module_resource = get_resource_path
 +
 +def get_resource_from_path(path):
 +    """Tries to extract the module name and the resource's relative path
 +    out of an absolute resource path.
 +
 +    If operation is successfull, returns a tuple containing the module name, the relative path
 +    to the resource using '/' as filesystem seperator[1] and the same relative path using
 +    os.path.sep seperators.
 +
 +    [1] same convention as the resource path declaration in manifests
 +
 +    :param path: absolute resource path
 +
 +    :rtype: tuple
 +    :return: tuple(module_name, relative_path, os_relative_path) if possible, else None
 +    """
 +    resource = [path.replace(adpath, '') for adpath in ad_paths if path.startswith(adpath)]
 +    if resource:
 +        relative = resource[0].split(os.path.sep)
 +        if not relative[0]:
 +            relative.pop(0)
 +        module = relative.pop(0)
 +        return (module, '/'.join(relative), os.path.sep.join(relative))
 +    return None
 +
  def get_module_icon(module):
      iconpath = ['static', 'description', 'icon.png']
      if get_module_resource(module, *iconpath):
@@@ -257,7 -231,6 +257,6 @@@ def load_information_from_description_f
                  'icon': get_module_icon(module),
                  'installable': True,
                  'license': 'AGPL-3',
-                 'name': False,
                  'post_load': None,
                  'version': '1.0',
                  'web': False,
diff --combined openerp/release.py
@@@ -30,10 -30,11 +30,11 @@@ RELEASE_LEVELS_DISPLAY = {ALPHA: ALPHA
  # properly comparable using normal operarors, for example:
  #  (6,1,0,'beta',0) < (6,1,0,'candidate',1) < (6,1,0,'candidate',2)
  #  (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0)
 -version_info = (8, 0, 0, RELEASE_CANDIDATE, 1)
 +version_info = (9, 0, 0, ALPHA, 1)
  version = '.'.join(map(str, version_info[:2])) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '')
  series = serie = major_version = '.'.join(map(str, version_info[:2]))
  
+ product_name = 'Odoo'
  description = 'Odoo Server'
  long_desc = '''Odoo is a complete ERP and CRM. The main features are accounting (analytic
  and financial), stock management, sales and purchases management, tasks
@@@ -45,9 -46,9 +46,9 @@@ classifiers = """Development Status :: 
  License :: OSI Approved :: GNU Affero General Public License v3
  Programming Language :: Python
  """
- url = 'http://www.openerp.com'
+ url = 'https://www.odoo.com'
  author = 'OpenERP S.A.'
- author_email = 'info@openerp.com'
+ author_email = 'info@odoo.com'
  license = 'AGPL-3'
  
  nt_service_name = "openerp-server-" + series
diff --combined openerp/tools/config.py
@@@ -62,9 -62,15 +62,15 @@@ DEFAULT_LOG_HANDLER = [':INFO'
  
  def _get_default_datadir():
      home = os.path.expanduser('~')
-     func = appdirs.user_data_dir if os.path.isdir(home) else appdirs.site_data_dir
 -    if os.path.exists(home):
++    if os.path.isdir(home):
+         func = appdirs.user_data_dir
+     else:
+         if sys.platform in ['win32', 'darwin']:
+             func = appdirs.site_data_dir
+         else:
+             func = lambda **kwarg: "/var/lib/%s" % kwarg['appname'].lower()
      # No "version" kwarg as session and filestore paths are shared against series
-     return func(appname='Odoo', appauthor=release.author)
+     return func(appname=release.product_name, appauthor=release.author)
  
  class configmanager(object):
      def __init__(self, fname=None):
  
          # Advanced options
          group = optparse.OptionGroup(parser, "Advanced options")
 -        if os.name == 'posix':
 -            group.add_option('--auto-reload', dest='auto_reload', action='store_true', my_default=False, help='enable auto reload')
 +        group.add_option('--dev', dest='dev_mode', action='store_true', my_default=False, help='enable developper mode')
          group.add_option('--debug', dest='debug_mode', action='store_true', my_default=False, help='enable debug mode')
          group.add_option("--stop-after-init", action="store_true", dest="stop_after_init", my_default=False,
                            help="stop the server after its initialization")
          # if defined but None take the configfile value
          keys = [
              'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
 -            'debug_mode', 'smtp_ssl', 'load_language',
 +            'debug_mode', 'dev_mode', 'smtp_ssl', 'load_language',
              'stop_after_init', 'logrotate', 'without_demo', 'xmlrpc', 'syslog',
              'list_db', 'xmlrpcs', 'proxy_mode',
              'test_file', 'test_enable', 'test_commit', 'test_report_directory',
          ]
  
          posix_keys = [
 -            'auto_reload', 'workers',
 +            'workers',
              'limit_memory_hard', 'limit_memory_soft',
              'limit_time_cpu', 'limit_time_real', 'limit_request',
          ]
diff --combined openerp/tools/convert.py
@@@ -60,6 -60,8 +60,8 @@@ from misc import SKIPPED_ELEMENT_TYPE
  
  from misc import unquote
  
+ from openerp import SUPERUSER_ID
  # Import of XML records requires the unsafe eval as well,
  # almost everywhere, which is ok because it supposedly comes
  # from trusted data, but at least we make it obvious now.
@@@ -212,18 -214,10 +214,10 @@@ def _eval_xml(self, node, pool, cr, uid
              if t=='tuple':
                  return tuple(res)
              return res
      elif node.tag == "function":
          args = []
          a_eval = node.get('eval','')
+         # FIXME: should probably be exclusive
          if a_eval:
              idref['ref'] = lambda x: self.id_get(cr, x)
              args = unsafe_eval(a_eval, idref)
              return_val = _eval_xml(self,n, pool, cr, uid, idref, context)
              if return_val is not None:
                  args.append(return_val)
-         model = pool[node.get('model','')]
-         method = node.get('name','')
+         model = pool[node.get('model', '')]
+         method = node.get('name')
          res = getattr(model, method)(cr, uid, *args)
          return res
      elif node.tag == "test":
@@@ -297,10 -291,10 +291,10 @@@ form: module.record_id""" % (xml_id,
          if len(id) > 64:
              _logger.error('id: %s is to long (max: 64)', id)
  
-     def _tag_delete(self, cr, rec, data_node=None):
-         d_model = rec.get("model",'')
+     def _tag_delete(self, cr, rec, data_node=None, mode=None):
+         d_model = rec.get("model")
          d_search = rec.get("search",'').encode('utf-8')
-         d_id = rec.get("id",'')
+         d_id = rec.get("id")
          ids = []
  
          if d_search:
  
          return True
  
-     def _tag_report(self, cr, rec, data_node=None):
+     def _tag_report(self, cr, rec, data_node=None, mode=None):
          res = {}
          for dest,f in (('name','string'),('model','model'),('report_name','name')):
              res[dest] = rec.get(f,'').encode('utf8')
              self._remove_ir_values(cr, res['name'], value, res['model'])
          return id
  
-     def _tag_function(self, cr, rec, data_node=None):
+     def _tag_function(self, cr, rec, data_node=None, mode=None):
          if self.isnoupdate(data_node) and self.mode != 'init':
              return
          context = self.get_context(data_node, rec, {'ref': _ref(self, cr)})
          _eval_xml(self,rec, self.pool, cr, uid, self.idref, context=context)
          return
  
-     def _tag_wizard(self, cr, rec, data_node=None):
-         string = rec.get("string",'').encode('utf8')
-         model = rec.get("model",'').encode('utf8')
-         name = rec.get("name",'').encode('utf8')
-         xml_id = rec.get('id','').encode('utf8')
-         self._test_xml_id(xml_id)
-         multi = rec.get('multi','') and eval(rec.get('multi','False'))
-         res = {'name': string, 'wiz_name': name, 'multi': multi, 'model': model}
-         if rec.get('groups'):
-             g_names = rec.get('groups','').split(',')
-             groups_value = []
-             for group in g_names:
-                 if group.startswith('-'):
-                     group_id = self.id_get(cr, group[1:])
-                     groups_value.append((3, group_id))
-                 else:
-                     group_id = self.id_get(cr, group)
-                     groups_value.append((4, group_id))
-             res['groups_id'] = groups_value
-         id = self.pool['ir.model.data']._update(cr, self.uid, "ir.actions.wizard", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
-         self.idref[xml_id] = int(id)
-         # ir_set
-         if (not rec.get('menu') or eval(rec.get('menu','False'))) and id:
-             keyword = str(rec.get('keyword','') or 'client_action_multi')
-             value = 'ir.actions.wizard,'+str(id)
-             replace = rec.get("replace",'') or True
-             self.pool['ir.model.data'].ir_set(cr, self.uid, 'action', keyword, string, [model], value, replace=replace, isobject=True, xml_id=xml_id)
-         elif self.mode=='update' and (rec.get('menu') and eval(rec.get('menu','False'))==False):
-             # Special check for wizard having attribute menu=False on update
-             value = 'ir.actions.wizard,'+str(id)
-             self._remove_ir_values(cr, string, value, model)
-     def _tag_url(self, cr, rec, data_node=None):
+     def _tag_url(self, cr, rec, data_node=None, mode=None):
          url = rec.get("url",'').encode('utf8')
          target = rec.get("target",'').encode('utf8')
          name = rec.get("name",'').encode('utf8')
          id = self.pool['ir.model.data']._update(cr, self.uid, "ir.actions.act_url", self.module, res, xml_id, noupdate=self.isnoupdate(data_node), mode=self.mode)
          self.idref[xml_id] = int(id)
  
-     def _tag_act_window(self, cr, rec, data_node=None):
+     def _tag_act_window(self, cr, rec, data_node=None, mode=None):
          name = rec.get('name','').encode('utf-8')
          xml_id = rec.get('id','').encode('utf8')
          self._test_xml_id(xml_id)
              self.pool['ir.model.data'].ir_set(cr, self.uid, 'action', keyword, xml_id, [src_model], value, replace=replace, isobject=True, xml_id=xml_id)
          # TODO add remove ir.model.data
  
-     def _tag_ir_set(self, cr, rec, data_node=None):
+     def _tag_ir_set(self, cr, rec, data_node=None, mode=None):
          if self.mode != 'init':
              return
          res = {}
              res[f_name] = f_val
          self.pool['ir.model.data'].ir_set(cr, self.uid, res['key'], res['key2'], res['name'], res['models'], res['value'], replace=res.get('replace',True), isobject=res.get('isobject', False), meta=res.get('meta',None))
  
-     def _tag_workflow(self, cr, rec, data_node=None):
+     def _tag_workflow(self, cr, rec, data_node=None, mode=None):
          if self.isnoupdate(data_node) and self.mode != 'init':
              return
-         model = str(rec.get('model',''))
-         w_ref = rec.get('ref','')
+         model = rec.get('model').encode('ascii')
+         w_ref = rec.get('ref')
          if w_ref:
              id = self.id_get(cr, w_ref)
          else:
              id = _eval_xml(self, rec[0], self.pool, cr, self.uid, self.idref)
  
          uid = self.get_uid(cr, self.uid, data_node, rec)
-         openerp.workflow.trg_validate(uid, model,
-             id,
-             str(rec.get('action','')), cr)
+         openerp.workflow.trg_validate(
+             uid, model, id, rec.get('action').encode('ascii'), cr)
  
      #
      # Support two types of notation:
      #   action="action_id"
      #   parent="parent_id"
      #
-     def _tag_menuitem(self, cr, rec, data_node=None):
+     def _tag_menuitem(self, cr, rec, data_node=None, mode=None):
          rec_id = rec.get("id",'').encode('ascii')
          self._test_xml_id(rec_id)
          m_l = map(escape, escape_re.split(rec.get("name",'').encode('utf8')))
              a_action = rec.get('action','').encode('utf8')
  
              # determine the type of action
-             a_type, a_id = self.model_id_get(cr, a_action)
-             a_type = a_type.split('.')[-1] # keep only type part
-             icons = {
-                 "act_window": 'STOCK_NEW',
-                 "report.xml": 'STOCK_PASTE',
-                 "wizard": 'STOCK_EXECUTE',
-                 "url": 'STOCK_JUMP_TO',
-                 "client": 'STOCK_EXECUTE',
-                 "server": 'STOCK_EXECUTE',
-             }
-             values['icon'] = icons.get(a_type,'STOCK_NEW')
-             if a_type=='act_window':
-                 cr.execute('select view_type,view_mode,name,view_id,target from ir_act_window where id=%s', (int(a_id),))
-                 rrres = cr.fetchone()
-                 assert rrres, "No window action defined for this id %s !\n" \
-                     "Verify that this is a window action or add a type argument." % (a_action,)
-                 action_type,action_mode,action_name,view_id,target = rrres
-                 if view_id:
-                     view_arch = self.pool['ir.ui.view'].read(cr, 1, [view_id], ['arch'])
-                     action_mode = etree.fromstring(view_arch[0]['arch'].encode('utf8')).tag
-                 cr.execute('SELECT view_mode FROM ir_act_window_view WHERE act_window_id=%s ORDER BY sequence LIMIT 1', (int(a_id),))
-                 if cr.rowcount:
-                     action_mode, = cr.fetchone()
-                 if action_type=='tree':
-                     values['icon'] = 'STOCK_INDENT'
-                 elif action_mode and action_mode.startswith(('tree','kanban','gantt')):
-                     values['icon'] = 'STOCK_JUSTIFY_FILL'
-                 elif action_mode and action_mode.startswith('graph'):
-                     values['icon'] = 'terp-graph'
-                 elif action_mode and action_mode.startswith('calendar'):
-                     values['icon'] = 'terp-calendar'
-                 if target=='new':
-                     values['icon'] = 'STOCK_EXECUTE'
-                 if not values.get('name', False):
-                     values['name'] = action_name
-             elif a_type in ['wizard', 'url', 'client', 'server'] and not values.get('name'):
-                 a_table = 'ir_act_%s' % a_type
-                 cr.execute('select name from %s where id=%%s' % a_table, (int(a_id),))
+             action_type, action_id = self.model_id_get(cr, a_action)
+             action_type = action_type.split('.')[-1] # keep only type part
+             if not values.get('name') and action_type in ('act_window', 'wizard', 'url', 'client', 'server'):
+                 a_table = 'ir_act_%s' % action_type.replace('act_', '')
+                 cr.execute('select name from "%s" where id=%%s' % a_table, (int(action_id),))
                  resw = cr.fetchone()
                  if resw:
                      values['name'] = resw[0]
  
          if rec.get('sequence'):
              values['sequence'] = int(rec.get('sequence'))
-         if rec.get('icon'):
-             values['icon'] = str(rec.get('icon'))
-         if rec.get('web_icon'):
-             values['web_icon'] = "%s,%s" %(self.module, str(rec.get('web_icon')))
-         if rec.get('web_icon_hover'):
-             values['web_icon_hover'] = "%s,%s" %(self.module, str(rec.get('web_icon_hover')))
  
          if rec.get('groups'):
              g_names = rec.get('groups','').split(',')
              self.idref[rec_id] = int(pid)
  
          if rec.get('action') and pid:
-             action = "ir.actions.%s,%d" % (a_type, a_id)
+             action = "ir.actions.%s,%d" % (action_type, action_id)
              self.pool['ir.model.data'].ir_set(cr, self.uid, 'action', 'tree_but_open', 'Menuitem', [('ir.ui.menu', int(pid))], action, True, True, xml_id=rec_id)
          return 'ir.ui.menu', pid
  
      def _assert_equals(self, f1, f2, prec=4):
          return not round(f1 - f2, prec)
  
-     def _tag_assert(self, cr, rec, data_node=None):
+     def _tag_assert(self, cr, rec, data_node=None, mode=None):
          if self.isnoupdate(data_node) and self.mode != 'init':
              return
  
          else: # all tests were successful for this assertion tag (no break)
              self.assertion_report.record_success()
  
-     def _tag_record(self, cr, rec, data_node=None):
+     def _tag_record(self, cr, rec, data_node=None, mode=None):
          rec_model = rec.get("model").encode('ascii')
          model = self.pool[rec_model]
          rec_id = rec.get("id",'').encode('ascii')
 -        rec_context = rec.get("context", None)
 +        rec_context = rec.get("context", {})
          if rec_context:
              rec_context = unsafe_eval(rec_context)
 +
 +        if self.xml_filename and rec_id:
 +            rec_context['install_mode_data'] = dict(
 +                xml_file=self.xml_filename,
 +                xml_id=rec_id,
 +                model=rec_model,
 +            )
 +
          self._test_xml_id(rec_id)
          # in update mode, the record won't be updated if the data node explicitely
          # opt-out using @noupdate="1". A second check will be performed in
          # ir.model.data#_update() using the record's ir.model.data `noupdate` field.
          if self.isnoupdate(data_node) and self.mode != 'init':
-             # check if the xml record has an id string
-             if rec_id:
-                 if '.' in rec_id:
-                     module,rec_id2 = rec_id.split('.')
-                 else:
-                     module = self.module
-                     rec_id2 = rec_id
-                 id = self.pool['ir.model.data']._update_dummy(cr, self.uid, rec_model, module, rec_id2)
-                 # check if the resource already existed at the last update
-                 if id:
-                     # if it existed, we don't update the data, but we need to
-                     # know the id of the existing record anyway
-                     self.idref[rec_id] = int(id)
-                     return None
-                 else:
-                     # if the resource didn't exist
-                     if not self.nodeattr2bool(rec, 'forcecreate', True):
-                         # we don't want to create it, so we skip it
-                         return None
-                     # else, we let the record to be created
+             # check if the xml record has no id, skip
+             if not rec_id:
+                 return None
  
+             if '.' in rec_id:
+                 module,rec_id2 = rec_id.split('.')
              else:
-                 # otherwise it is skipped
+                 module = self.module
+                 rec_id2 = rec_id
+             id = self.pool['ir.model.data']._update_dummy(cr, self.uid, rec_model, module, rec_id2)
+             if id:
+                 # if the resource already exists, don't update it but store
+                 # its database id (can be useful)
+                 self.idref[rec_id] = int(id)
                  return None
+             elif not self.nodeattr2bool(rec, 'forcecreate', True):
+                 # if it doesn't exist and we shouldn't create it, skip it
+                 return None
+             # else create it normally
          res = {}
          for field in rec.findall('./field'):
- #TODO: most of this code is duplicated above (in _eval_xml)...
-             f_name = field.get("name",'').encode('utf-8')
+             #TODO: most of this code is duplicated above (in _eval_xml)...
+             f_name = field.get("name").encode('utf-8')
              f_ref = field.get("ref",'').encode('utf-8')
              f_search = field.get("search",'').encode('utf-8')
              f_model = field.get("model",'').encode('utf-8')
-             if not f_model and model._all_columns.get(f_name,False):
+             if not f_model and model._all_columns.get(f_name):
                  f_model = model._all_columns[f_name].column._obj
              f_use = field.get("use",'').encode('utf-8') or 'id'
              f_val = False
  
              if f_search:
                  q = unsafe_eval(f_search, self.idref)
-                 field = []
                  assert f_model, 'Define an attribute model="..." in your .XML file !'
                  f_obj = self.pool[f_model]
                  # browse the objects searched
                      # take the first element of the search
                      f_val = s[0][f_use]
              elif f_ref:
-                 if f_ref=="null":
-                     f_val = False
+                 if f_name in model._all_columns \
+                           and model._all_columns[f_name].column._type == 'reference':
+                     val = self.model_id_get(cr, f_ref)
+                     f_val = val[0] + ',' + str(val[1])
                  else:
-                     if f_name in model._all_columns \
-                               and model._all_columns[f_name].column._type == 'reference':
-                         val = self.model_id_get(cr, f_ref)
-                         f_val = val[0] + ',' + str(val[1])
-                     else:
-                         f_val = self.id_get(cr, f_ref)
+                     f_val = self.id_get(cr, f_ref)
              else:
                  f_val = _eval_xml(self,field, self.pool, cr, self.uid, self.idref)
                  if f_name in model._all_columns:
          id = self.pool['ir.model.data']._update(cr, self.uid, rec_model, self.module, res, rec_id or False, not self.isnoupdate(data_node), noupdate=self.isnoupdate(data_node), mode=self.mode, context=rec_context )
          if rec_id:
              self.idref[rec_id] = int(id)
-         if config.get('import_partial', False):
+         if config.get('import_partial'):
              cr.commit()
          return rec_model, id
  
-     def _tag_template(self, cr, el, data_node=None):
+     def _tag_template(self, cr, el, data_node=None, mode=None):
          # This helper transforms a <template> element into a <record> and forwards it
          tpl_id = el.get('id', el.get('t-name', '')).encode('ascii')
          full_tpl_id = tpl_id
          record.append(Field(name, name='name'))
          record.append(Field("qweb", name='type'))
          record.append(Field(el.get('priority', "16"), name='priority'))
-         record.append(Field(el, name="arch", type="xml"))
          if 'inherit_id' in el.attrib:
              record.append(Field(name='inherit_id', ref=el.get('inherit_id')))
+         if el.get('active') in ("True", "False") and mode != "update":
+             record.append(Field(name='active', eval=el.get('active')))
+         if el.get('customize_show') in ("True", "False"):
+             record.append(Field(name='customize_show', eval=el.get('customize_show')))
          groups = el.attrib.pop('groups', None)
          if groups:
              grp_lst = map(lambda x: "ref('%s')" % x, groups.split(','))
                  )
              )
              record.append(Field('primary', name='mode'))
-         if el.get('optional'):
-             record.append(Field(el.get('optional'), name='application'))
+         # inject complete <template> element (after changing node name) into
+         # the ``arch`` field
+         record.append(Field(el, name="arch", type="xml"))
  
          return self._tag_record(cr, record, data_node)
  
              mod,id_str = id_str.split('.')
          return model_data_obj.get_object_reference(cr, self.uid, mod, id_str)
  
-     def parse(self, de):
+     def parse(self, de, mode=None):
          if de.tag != 'openerp':
              raise Exception("Mismatch xml format: root tag must be `openerp`.")
  
              for rec in n:
                  if rec.tag in self._tags:
                      try:
-                         self._tags[rec.tag](self.cr, rec, n)
+                         self._tags[rec.tag](self.cr, rec, n, mode=mode)
                      except Exception, e:
                          self.cr.rollback()
                          exc_info = sys.exc_info()
                          raise ParseError, (misc.ustr(e), etree.tostring(rec).rstrip(), rec.getroottree().docinfo.URL, rec.sourceline), exc_info[2]
          return True
  
 -    def __init__(self, cr, module, idref, mode, report=None, noupdate=False):
 +    def __init__(self, cr, module, idref, mode, report=None, noupdate=False, xml_filename=None):
  
          self.mode = mode
          self.module = module
              report = assertion_report.assertion_report()
          self.assertion_report = report
          self.noupdate = noupdate
 +        self.xml_filename = xml_filename
          self._tags = {
-             'menuitem': self._tag_menuitem,
              'record': self._tag_record,
-             'template': self._tag_template,
-             'assert': self._tag_assert,
-             'report': self._tag_report,
-             'wizard': self._tag_wizard,
              'delete': self._tag_delete,
-             'ir_set': self._tag_ir_set,
              'function': self._tag_function,
+             'menuitem': self._tag_menuitem,
+             'template': self._tag_template,
              'workflow': self._tag_workflow,
+             'report': self._tag_report,
+             'ir_set': self._tag_ir_set,
              'act_window': self._tag_act_window,
-             'url': self._tag_url
+             'url': self._tag_url,
+             'assert': self._tag_assert,
          }
  
  def convert_file(cr, module, filename, idref, mode='update', noupdate=False, kind=None, report=None, pathname=None):
          pathname = os.path.join(module, filename)
      fp = misc.file_open(pathname)
      ext = os.path.splitext(filename)[1].lower()
      try:
          if ext == '.csv':
              convert_csv_import(cr, module, pathname, fp.read(), idref, mode, noupdate)
@@@ -1075,12 -981,8 +990,12 @@@ def convert_xml_import(cr, module, xmlf
  
      if idref is None:
          idref={}
 -    obj = xml_import(cr, module, idref, mode, report=report, noupdate=noupdate)
 +    if isinstance(xmlfile, file):
 +        xml_filename = xmlfile.name
 +    else:
 +        xml_filename = xmlfile
 +    obj = xml_import(cr, module, idref, mode, report=report, noupdate=noupdate, xml_filename=xml_filename)
-     obj.parse(doc.getroot())
+     obj.parse(doc.getroot(), mode=mode)
      return True
  
  
diff --combined openerp/tools/func.py
@@@ -20,7 -20,7 +20,7 @@@
  #
  ##############################################################################
  
 -__all__ = ['synchronized', 'lazy_property']
 +__all__ = ['synchronized', 'lazy_property', 'conditional']
  
  from functools import wraps
  from inspect import getsourcefile
@@@ -42,6 -42,10 +42,10 @@@ class lazy_property(object)
          setattr(obj, self.fget.__name__, value)
          return value
  
+     @property
+     def __doc__(self):
+         return self.fget.__doc__
      @staticmethod
      def reset_all(obj):
          """ Reset all lazy properties on the instance `obj`. """
              if isinstance(getattr(cls, name, None), lazy_property):
                  obj_dict.pop(name)
  
 +def conditional(condition, decorator):
 +    """ Decorator for a conditionally applied decorator.
 +
 +        Example:
 +
 +           @conditional(get_config('use_cache'), ormcache)
 +           def fn():
 +               pass
 +    """
 +    if condition:
 +        return decorator
 +    else:
 +        return lambda fn: fn
  
  def synchronized(lock_attr='_lock'):
      def decorator(func):
diff --combined openerp/tools/mail.py
@@@ -49,7 -49,7 +49,7 @@@ allowed_tags = clean.defs.tags | frozen
  safe_attrs = clean.defs.safe_attrs | frozenset(
      ['style',
       'data-oe-model', 'data-oe-id', 'data-oe-field', 'data-oe-type', 'data-oe-expression', 'data-oe-translate', 'data-oe-nodeid',
 -     'data-snippet-id', 'data-publish', 'data-id', 'data-res_id', 'data-member_id', 'data-view-id'
 +     'data-publish', 'data-id', 'data-res_id', 'data-member_id', 'data-view-id'
       ])
  
  
@@@ -63,6 -63,9 +63,9 @@@ def html_sanitize(src, silent=True, str
      # html encode email tags
      part = re.compile(r"(<(([^a<>]|a[^<>\s])[^<>]*)@[^<>]+>)", re.IGNORECASE | re.DOTALL)
      src = part.sub(lambda m: cgi.escape(m.group(1)), src)
+     # html encode mako tags <% ... %> to decode them later and keep them alive, otherwise they are stripped by the cleaner
+     src = src.replace('<%', cgi.escape('<%'))
+     src = src.replace('%>', cgi.escape('%>'))
  
      kwargs = {
          'page_structure': True,
@@@ -71,7 -74,7 +74,7 @@@
          'remove_unknown_tags': False,
          'allow_tags': allowed_tags,
          'comments': False,
-         'processing_instructions' : False
+         'processing_instructions': False
      }
      if etree.LXML_VERSION >= (2, 3, 1):
          # kill_tags attribute has been added in version 2.3.1
          cleaned = cleaned.replace('%20', ' ')
          cleaned = cleaned.replace('%5B', '[')
          cleaned = cleaned.replace('%5D', ']')
+         cleaned = cleaned.replace('&lt;%', '<%')
+         cleaned = cleaned.replace('%&gt;', '%>')
      except etree.ParserError, e:
          if 'empty' in str(e):
              return ""
@@@ -381,6 -386,11 +386,11 @@@ def html_email_clean(html, remove=False
              node.set('in_quote', '1')
              node.set('tail_remove', '1')
          if node.tag == 'blockquote' or node.get('text_quote') or node.get('text_signature'):
+             # here no quote_begin because we want to be able to remove some quoted
+             # text without removing all the remaining context
+             node.set('in_quote', '1')
+         if node.getparent() is not None and node.getparent().get('in_quote'):
+             # inside a block of removed text but not in quote_begin (see above)
              node.set('in_quote', '1')
  
          # shorten:
@@@ -500,6 -510,7 +510,7 @@@ def html2plaintext(html, body_id=None, 
      html = html.replace(' ' * 2, ' ')
      html = html.replace('&gt;', '>')
      html = html.replace('&lt;', '<')
+     html = html.replace('&amp;', '&')
  
      # strip all lines
      html = '\n'.join([x.strip() for x in html.splitlines()])
@@@ -598,10 -609,6 +609,6 @@@ command_re = re.compile("^Set-([a-z]+) 
  # group(1) = the record ID ; group(2) = the model (if any) ; group(3) = the domain
  reference_re = re.compile("<.*-open(?:object|erp)-(\\d+)(?:-([\w.]+))?.*@(.*)>", re.UNICODE)
  
- # Bounce regex
- # Typical form of bounce is bounce-128-crm.lead-34@domain
- # group(1) = the mail ID; group(2) = the model (if any); group(3) = the record ID
- bounce_re = re.compile("[\w]+-(\d+)-?([\w.]+)?-?(\d+)?", re.UNICODE)
  
  def generate_tracking_message_id(res_id):
      """Returns a string that can be used in the Message-ID RFC822 header field
@@@ -665,4 -672,4 +672,4 @@@ def email_split(text)
                  # sometimes returns emails without at least '@'. The '@'
                  # is strictly required in RFC2822's `addr-spec`.
                  if addr[1]
-                 if '@' in addr[1]]
+                 if '@' in addr[1]]
diff --combined openerp/tools/misc.py
@@@ -278,6 -278,36 +278,36 @@@ def reverse_enumerate(l)
      """
      return izip(xrange(len(l)-1, -1, -1), reversed(l))
  
+ def topological_sort(elems):
+     """ Return a list of elements sorted so that their dependencies are listed
+     before them in the result.
+     :param elems: specifies the elements to sort with their dependencies; it is
+         a dictionary like `{element: dependencies}` where `dependencies` is a
+         collection of elements that must appear before `element`. The elements
+         of `dependencies` are not required to appear in `elems`; they will
+         simply not appear in the result.
+     :returns: a list with the keys of `elems` sorted according to their
+         specification.
+     """
+     # the algorithm is inspired by [Tarjan 1976],
+     # http://en.wikipedia.org/wiki/Topological_sorting#Algorithms
+     result = []
+     visited = set()
+     def visit(n):
+         if n not in visited:
+             visited.add(n)
+             if n in elems:
+                 # first visit all dependencies of n, then append n to result
+                 map(visit, elems[n])
+                 result.append(n)
+     map(visit, elems)
+     return result
  
  class UpdateableStr(local):
      """ Class that stores an updateable string (used in wizards)
@@@ -1131,7 -1161,7 +1161,7 @@@ class CountingStream(object)
  
  def stripped_sys_argv(*strip_args):
      """Return sys.argv with some arguments stripped, suitable for reexecution or subprocesses"""
 -    strip_args = sorted(set(strip_args) | set(['-s', '--save', '-d', '--database', '-u', '--update', '-i', '--init']))
 +    strip_args = sorted(set(strip_args) | set(['-s', '--save', '-u', '--update', '-i', '--init']))
      assert all(config.parser.has_option(s) for s in strip_args)
      takes_value = dict((s, config.parser.get_option(s).takes_value()) for s in strip_args)