[TEST]
authorArnaud Pineux <api@openerp.com>
Thu, 15 Nov 2012 12:30:05 +0000 (13:30 +0100)
committerArnaud Pineux <api@openerp.com>
Thu, 15 Nov 2012 12:30:05 +0000 (13:30 +0100)
bzr revid: api@openerp.com-20121115123005-0cwt6402sj1hbecm

169 files changed:
addons/account/account.py
addons/account/account_analytic_line.py
addons/account/account_bank_statement.py
addons/account/account_invoice.py
addons/account/account_move_line.py
addons/account/account_report.xml
addons/account/account_view.xml
addons/account/edi/invoice_action_data.xml
addons/account/partner_view.xml
addons/account/report/account_financial_report.rml
addons/account/report/account_general_ledger.py
addons/account/report/common_report_header.py
addons/account/static/src/js/account_move_reconciliation.js
addons/account/test/account_report.yml
addons/account/wizard/account_fiscalyear_close.py
addons/account/wizard/account_report_common.py
addons/account_analytic_plans/account_analytic_plans_view.xml
addons/account_anglo_saxon/invoice.py
addons/account_asset/account_asset.py
addons/account_budget/account_budget_view.xml
addons/account_check_writing/i18n/es.po
addons/account_sequence/account_sequence_data.xml
addons/account_voucher/account_voucher.py
addons/account_voucher/account_voucher_view.xml
addons/account_voucher/i18n/fr.po
addons/account_voucher/voucher_payment_receipt_view.xml
addons/account_voucher/voucher_sales_purchase_view.xml
addons/analytic/analytic.py
addons/analytic/analytic_view.xml
addons/audittrail/audittrail.py
addons/audittrail/audittrail_view.xml
addons/auth_anonymous/static/src/js/auth_anonymous.js
addons/auth_oauth/static/src/js/auth_oauth.js
addons/auth_openid/static/src/js/auth_openid.js
addons/base_action_rule/base_action_rule_view.xml
addons/base_action_rule/i18n/es.po
addons/base_crypt/crypt.py
addons/base_iban/base_iban.py
addons/base_import/static/src/js/import.js
addons/base_setup/res_config.py
addons/board/static/src/js/dashboard.js
addons/crm/crm.py
addons/crm/crm_action_rule.py
addons/crm/crm_lead.py
addons/crm/crm_segmentation.py
addons/crm/crm_view.xml
addons/crm/i18n/es.po
addons/crm/report/crm_phonecall_report_view.xml
addons/crm/wizard/crm_lead_to_opportunity.py
addons/crm/wizard/crm_lead_to_partner.py
addons/crm_claim/crm_claim.py
addons/crm_claim/crm_claim_data.xml
addons/crm_claim/crm_claim_menu.xml
addons/crm_claim/crm_claim_view.xml
addons/crm_partner_assign/i18n/es.po
addons/crm_profiling/crm_profiling.py
addons/document/nodes.py
addons/document_ftp/ftpserver/abstracted_fs.py
addons/document_page/wizard/document_page_show_diff.py
addons/document_webdav/document_webdav.py
addons/edi/static/src/js/edi.js
addons/email_template/email_template.py
addons/email_template/email_template_view.xml
addons/email_template/tests/test_mail.py
addons/email_template/wizard/email_template_preview_view.xml
addons/email_template/wizard/mail_compose_message.py
addons/email_template/wizard/mail_compose_message_view.xml
addons/event/event_view.xml
addons/event_sale/event_sale.py
addons/google_docs/static/src/js/gdocs.js
addons/hr/hr_view.xml
addons/hr/i18n/es_EC.po
addons/hr_attendance/report/attendance_by_month.py
addons/hr_attendance/static/src/js/attendance.js
addons/hr_evaluation/hr_evaluation.py
addons/hr_evaluation/hr_evaluation_view.xml
addons/hr_expense/hr_expense.py
addons/hr_expense/hr_expense_view.xml
addons/hr_holidays/hr_holidays.py
addons/hr_holidays/hr_holidays_demo.xml
addons/hr_holidays/report/holidays_summary_report.py
addons/hr_payroll/hr_payroll.py
addons/hr_payroll/hr_payroll_demo.xml
addons/hr_recruitment/hr_recruitment_view.xml
addons/hr_recruitment/static/src/js/hr_recruitment.js
addons/hr_timesheet/report/user_timesheet.py
addons/hr_timesheet/wizard/hr_timesheet_print_employee.py
addons/hr_timesheet/wizard/hr_timesheet_print_users.py
addons/hr_timesheet_invoice/hr_timesheet_invoice_demo.xml
addons/hr_timesheet_sheet/static/src/js/timesheet.js
addons/l10n_be/wizard/l10n_be_partner_vat_listing.py
addons/l10n_ch/bank.py
addons/l10n_fr/fr_tax.xml
addons/l10n_fr/plan_comptable_general.xml
addons/l10n_it/data/account.tax.code.template.csv
addons/l10n_it/data/account.tax.template.csv
addons/l10n_nl/account_chart_netherlands.xml
addons/mail/__init__.py
addons/mail/data/mail_demo.xml
addons/mail/data/mail_group_data.xml
addons/mail/mail_followers.py
addons/mail/mail_group_view.xml
addons/mail/mail_message.py
addons/mail/mail_message_view.xml
addons/mail/mail_thread.py
addons/mail/mail_thread_view.xml
addons/mail/static/src/css/mail.css
addons/mail/static/src/css/mail_group.css
addons/mail/static/src/js/mail.js
addons/mail/static/src/js/mail_followers.js
addons/mail/static/src/js/many2many_tags_email.js
addons/mail/static/src/xml/mail.xml
addons/mail/tests/test_mail.py
addons/mail/wizard/invite.py
addons/mail/wizard/mail_compose_message.py
addons/mail/wizard/mail_compose_message_view.xml
addons/marketing_campaign/marketing_campaign_view.xml
addons/mrp/i18n/sl.po
addons/mrp/report/price.py
addons/mrp/res_config.py
addons/mrp/res_config_view.xml
addons/mrp_repair/mrp_repair.py
addons/note/note_view.xml
addons/note/static/src/css/note.css
addons/note/static/src/css/note.sass
addons/pad/pad.py
addons/pad/pad_demo.xml
addons/pad/static/src/js/pad.js
addons/plugin_outlook/plugin_outlook.xml
addons/plugin_thunderbird/plugin_thunderbird.py
addons/plugin_thunderbird/plugin_thunderbird.xml
addons/point_of_sale/report/pos_receipt.py
addons/point_of_sale/static/src/js/devices.js
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/widgets.js
addons/point_of_sale/wizard/pos_payment.py
addons/portal/wizard/share_wizard.py
addons/portal_claim/portal_claim_view.xml
addons/portal_event/portal_event_view.xml
addons/portal_sale/portal_sale_view.xml
addons/portal_sale/security/portal_security.xml
addons/process/static/src/js/process.js
addons/product/product.py
addons/product_expiry/product_expiry.py
addons/project/project.py
addons/project/project_view.xml
addons/project/static/src/js/project.js
addons/project_timesheet/project_timesheet.py
addons/purchase/edi/purchase_order_action_data.xml
addons/purchase/purchase.py
addons/purchase/purchase_view.xml
addons/purchase/res_config.py
addons/purchase/stock_view.xml
addons/sale/edi/sale_order_action_data.xml
addons/sale/res_config.py
addons/sale/sale.py
addons/sale/sale_view.xml
addons/sale/wizard/sale_make_invoice_advance.py
addons/sale_crm/wizard/crm_make_sale.py
addons/share/static/src/js/share.js
addons/share/wizard/share_wizard.py
addons/stock/product_view.xml
addons/stock/stock.py
addons/stock/stock_sequence.xml
addons/stock/stock_view.xml
addons/stock/wizard/stock_fill_inventory.py
addons/web_linkedin/static/src/js/linkedin.js
addons/web_shortcuts/static/src/js/web_shortcuts.js

index c9dd195..cfba308 100644 (file)
@@ -541,10 +541,18 @@ class account_account(osv.osv):
                 return False
         return True
 
+    def _check_company_account(self, cr, uid, ids, context=None):
+        for account in self.browse(cr, uid, ids, context=context):
+            if account.parent_id:
+                if account.company_id != account.parent_id.company_id:
+                    return False
+        return True
+
     _constraints = [
         (_check_recursion, 'Error!\nYou cannot create recursive accounts.', ['parent_id']),
         (_check_type, 'Configuration Error!\nYou cannot define children to an account with internal type different of "View".', ['type']),
         (_check_account_type, 'Configuration Error!\nYou cannot select an account type with a deferral method different of "Unreconciled" for accounts with internal type "Payable/Receivable".', ['user_type','type']),
+        (_check_company_account, 'Error!\nYou cannot create an account which has parent account of different company.', ['parent_id']),
     ]
     _sql_constraints = [
         ('code_company_uniq', 'unique (code,company_id)', 'The code of the account must be unique per company !')
index f0a420f..066f8d1 100644 (file)
@@ -107,7 +107,7 @@ class account_analytic_line(osv.osv):
         if journal_id:
             journal = analytic_journal_obj.browse(cr, uid, journal_id, context=context)
             if journal.type == 'sale':
-                product_price_type_ids = product_price_type_obj.search(cr, uid, [('field','=','list_price')], context)
+                product_price_type_ids = product_price_type_obj.search(cr, uid, [('field','=','list_price')], context=context)
                 if product_price_type_ids:
                     pricetype = product_price_type_obj.browse(cr, uid, product_price_type_ids, context=context)[0]
         # Take the company currency as the reference one
index 8e79376..4422537 100644 (file)
@@ -486,6 +486,19 @@ class account_bank_statement(osv.osv):
         default['move_line_ids'] = []
         return super(account_bank_statement, self).copy(cr, uid, id, default, context=context)
 
+    def button_journal_entries(self, cr, uid, ids, context=None):
+      ctx = (context or {}).copy()
+      ctx['journal_id'] = self.browse(cr, uid, ids[0], context=context).journal_id.id
+      return {
+        'view_type':'form',
+        'view_mode':'tree',
+        'res_model':'account.move.line',
+        'view_id':False,
+        'type':'ir.actions.act_window',
+        'domain':[('statement_id','in',ids)],
+        'context':ctx,
+      }
+
 account_bank_statement()
 
 class account_bank_statement_line(osv.osv):
index b01fc97..4095515 100644 (file)
@@ -402,6 +402,7 @@ class account_invoice(osv.osv):
             'default_res_id': ids[0],
             'default_use_template': True,
             'default_template_id': template_id,
+            'default_composition_mode': 'comment',
             })
         return {
             'view_type': 'form',
@@ -983,13 +984,13 @@ class account_invoice(osv.osv):
                 for i in line:
                     i[2]['period_id'] = period_id
 
+            ctx.update(invoice=inv)
             move_id = move_obj.create(cr, uid, move, context=ctx)
             new_move_name = move_obj.browse(cr, uid, move_id, context=ctx).name
             # make the invoice point to that move
             self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name}, context=ctx)
             # Pass invoice in context in method post: used if you want to get the same
             # account move reference when creating the same invoice after a cancelled one:
-            ctx.update({'invoice':inv})
             move_obj.post(cr, uid, [move_id], context=ctx)
         self._log_event(cr, uid, ids)
         return True
index 67a55d8..302a74c 100644 (file)
@@ -208,7 +208,7 @@ class account_move_line(osv.osv):
             if type(period_id) == str:
                 ids = period_obj.search(cr, uid, [('name', 'ilike', period_id)])
                 context.update({
-                    'period_id': ids[0]
+                    'period_id': ids and ids[0] or False
                 })
         return context
 
@@ -582,7 +582,7 @@ class account_move_line(osv.osv):
         lines = self.browse(cr, uid, ids, context=context)
         for l in lines:
             if l.account_id.type == 'view':
-                raise osv.except_osv(_('Error!'), _('You cannot create journal items on “View” type account %s %s.') % (l.account_id.code, l.account_id.name))
+                return False
         return True
 
     def _check_no_closed(self, cr, uid, ids, context=None):
@@ -917,7 +917,7 @@ class account_move_line(osv.osv):
 
         if lines and lines[0]:
             partner_id = lines[0].partner_id and lines[0].partner_id.id or False
-            if not partner_obj.has_something_to_reconcile(cr, uid, partner_id, context=context):
+            if partner_id and not partner_obj.has_something_to_reconcile(cr, uid, partner_id, context=context):
                 partner_obj.mark_as_reconciled(cr, uid, [partner_id], context=context)
         return r_id
 
@@ -975,7 +975,7 @@ class account_move_line(osv.osv):
         if context is None:
             context = {}
         result = super(account_move_line, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
-        if view_type != 'tree':
+        if (view_type != 'tree') or view_id:
             #Remove the toolbar from the form view
             if view_type == 'form':
                 if result.get('toolbar', False):
index d258bb0..0017409 100644 (file)
             groups="group_account_user,group_account_manager"
             parent="account.menu_finance_generic_reporting" sequence="3"/>
 
-        <report id="account_account_balance_landscape"
-            string="Account balance"
-            model="account.account"
-            name="account.account.balance.landscape"
-            rml="account/report/account_balance_landscape.rml"
-            auto="False"
-            menu="False"/>
-
     </data>
 </openerp>
index 19128b4..f7d64f4 100644 (file)
             <field name="domain">[('parent_id','=',False)]</field>
         </record>
 
-         <record id="view_account_gain_loss_tree" model="ir.ui.view">
+        <record id="view_account_gain_loss_tree" model="ir.ui.view">
            <field name="name">Unrealized Gain or Loss</field>
             <field name="model">account.account</field>
             <field name="arch" type="xml">
-                <tree string="Unrealized Gains and losses">
+                <tree string="Unrealized Gains and losses" create="false">
                     <field name="code"/>
                     <field name="name"/>
                     <field name="parent_id" invisible="1"/>
             </field>
         </record>
 
-          <record id="action_account_gain_loss" model="ir.actions.act_window">
+        <record id="action_account_gain_loss" model="ir.actions.act_window">
             <field name="name">Unrealized Gain or Loss</field>
             <field name="res_model">account.account</field>
             <field name="view_type">form</field>
                                     <field name="ref"/>
                                     <field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
                                     <field name="type" on_change="onchange_type(partner_id, type)"/>
-                                    <field domain="[('journal_id','=',parent.journal_id), ('company_id', '=', parent.company_id)]" name="account_id"/>
+                                    <field name="account_id" options='{"no_open":True}' domain="[('journal_id','=',parent.journal_id), ('company_id', '=', parent.company_id)]"/>
                                     <field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('company_id', '=', parent.company_id), ('type', '&lt;&gt;', 'view')]"/>
                                     <field name="amount"/>
                                 </tree>
                                         <field name="ref"/>
                                         <field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
                                         <field name="type" on_change="onchange_type(partner_id, type)"/>
-                                        <field domain="[('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view'), ('company_id', '=', parent.company_id)]" name="account_id"/>
+                                        <field name="account_id" domain="[('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view'), ('company_id', '=', parent.company_id)]"/>
                                         <field name="analytic_account_id" groups="analytic.group_analytic_accounting" domain="[('company_id', '=', parent.company_id), ('type', '&lt;&gt;', 'view')]"/>
                                         <field name="amount"/>
                                         <field name="sequence" readonly="0"/>
                                 </form>
                             </field>
                         </page>
-                        <page string="Journal Entries" name="move_live_ids">
-                            <field name="move_line_ids"/>
-                        </page>
                     </notebook>
                 </sheet>
                 </form>
             </field>
         </record>
 
+        <record id="view_bank_statement_form_journal_items" model="ir.ui.view">
+            <field name="name">account.bank.statement.journal.items.form.inherit</field>
+            <field name="model">account.bank.statement</field>
+            <field name="inherit_id" ref="view_bank_statement_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//div[@name='import_buttons']" position="inside">
+                    <button name="button_journal_entries"
+                            string="Journal Items" type="object"
+                            attrs="{'invisible':[('state','!=','confirm')]}"/>
+                </xpath>
+            </field>
+        </record>
 
         <record id="action_bank_statement_tree" model="ir.actions.act_window">
             <field name="name">Bank Statements</field>
             </field>
         </record>
 
-         <record id="view_move_line_tree" model="ir.ui.view">
+        <record id="view_move_line_tree" model="ir.ui.view">
             <field name="name">account.move.line.tree</field>
             <field name="model">account.move.line</field>
             <field eval="4" name="priority"/>
                         <field name="name"/>
                         <field name="active"/>
                     </group>
-                    <field name="note" placeholder="Note fo the invoice..."/>
+                    <field name="note" placeholder="Note for the invoice..."/>
                     <separator string="Computation"/>
                     <field name="line_ids"/>
                 </form>
             </field>
         </record>
 
-         <record id="view_subscription_search" model="ir.ui.view">
+        <record id="view_subscription_search" model="ir.ui.view">
             <field name="name">account.subscription.search</field>
             <field name="model">account.subscription</field>
             <field name="arch" type="xml">
                         <field name="visible" />
                         <field name="complete_tax_set" />
                     </group>
-                    <field name="tax_template_ids" colspan="4" readonly="1" nolabel="1"/>
+                    <separator string="Default Taxes" colspan="4"/>
+                    <field name="tax_template_ids" colspan="4"  nolabel="1"/>
                     <separator string="Properties" colspan="4"/>
                     <group col="4">
                         <field name="property_account_receivable" domain="[('id', 'child_of', [account_root_id])]"/>
index 53fa03a..d86ebca 100644 (file)
@@ -40,7 +40,7 @@
             <field name="name">Automated Invoice Notification Mail</field>
             <field name="email_from">${object.user_id.email or object.company_id.email or 'noreply@localhost'}</field>
             <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })</field>
-            <field name="email_to">${object.partner_id.email or ''}</field>
+            <field name="email_recipients">${object.partner_id.id}</field>
             <field name="model_id" ref="account.model_account_invoice"/>
             <field name="auto_delete" eval="True"/>
             <field name="body_html"><![CDATA[
index 4bee200..d17f8c1 100644 (file)
@@ -6,7 +6,7 @@
             <field name="name">account.fiscal.position.form</field>
             <field name="model">account.fiscal.position</field>
             <field name="arch" type="xml">
-                <form string="Fiscal Position">
+                <form string="Fiscal Position" version="7.0">
                     <group col="4">
                         <field name="name"/>
                         <field name="active"/>
             context="{'search_default_partner_id':[active_id], 'default_partner_id': active_id}"
             src_model="res.partner"
             view_type="form"
-            view_mode="tree,form,calendar"/>
+            view_mode="tree,form"/>
 
     </data>
 </openerp>
index 6f0ddd0..1ee8d5b 100644 (file)
       <tr>
         <td><para style="terp_tblheader_General_Centre">Chart of Accounts</para></td>
         <td><para style="terp_tblheader_General_Centre">Fiscal Year</para></td>
-        <td><para style="terp_tblheader_General_Centre">Filter By [[ get_filter(data)!='No Filter' and get_filter(data) ]]</para></td>
+        <td><para style="terp_tblheader_General_Centre">Filter By [[ get_filter(data)!='No Filters' and get_filter(data) ]]</para></td>
       </tr>
     <tr>
        <td><para style="terp_default_Centre_8">[[ get_account(data) or removeParentNode('para') ]]</para></td>
        <td><para style="terp_default_Centre_8">[[ get_fiscalyear(data)  or '' ]]</para></td>
-        <td><para style="terp_default_Centre_8">[[ get_filter(data)=='No Filter' and get_filter(data) or removeParentNode('para') ]] </para>
+        <td><para style="terp_default_Centre_8">[[ get_filter(data)=='No Filters' and get_filter(data) or removeParentNode('para') ]] </para>
             <blockTable colWidths="60.0,60.0" style="Table3">[[ get_filter(data)=='Date' or removeParentNode('blockTable') ]]
                 <tr>
                    <td><para style="terp_tblheader_General_Centre">Start Date</para></td>
index 294c7fa..38f2392 100644 (file)
@@ -299,10 +299,10 @@ class general_ledger(report_sxw.rml_parse, common_report_header):
 
     def _get_sortby(self, data):
         if self.sortby == 'sort_date':
-            return 'Date'
+            return self._translate('Date')
         elif self.sortby == 'sort_journal_partner':
-            return 'Journal & Partner'
-        return 'Date'
+            return self._translate('Journal & Partner')
+        return self._translate('Date')
 
 report_sxw.report_sxw('report.account.general.ledger', 'account.account', 'addons/account/report/account_general_ledger.rml', parser=general_ledger, header='internal')
 report_sxw.report_sxw('report.account.general.ledger_landscape', 'account.account', 'addons/account/report/account_general_ledger_landscape.rml', parser=general_ledger, header='internal landscape')
index c93c3e0..c5719d9 100644 (file)
@@ -94,10 +94,10 @@ class common_report_header(object):
     def _get_filter(self, data):
         if data.get('form', False) and data['form'].get('filter', False):
             if data['form']['filter'] == 'filter_date':
-                return 'Date'
+                return self._translate('Date')
             elif data['form']['filter'] == 'filter_period':
-                return 'Periods'
-        return 'No Filter'
+                return self._translate('Periods')
+        return self._translate('No Filters')
 
     def _sum_debit_period(self, period_id, journal_id=None):
         journals = journal_id or self.journal_ids
index 8e72e24..22fe9b7 100644 (file)
@@ -48,7 +48,7 @@ openerp.account = function (instance) {
             this.last_group_by = group_by;
             this.old_search = _.bind(this._super, this);
             var mod = new instance.web.Model("account.move.line", context, domain);
-            return mod.call("list_partners_to_reconcile", []).pipe(function(result) {
+            return mod.call("list_partners_to_reconcile", []).then(function(result) {
                 var current = self.current_partner !== null ? self.partners[self.current_partner][0] : null;
                 self.partners = result;
                 var index = _.find(_.range(self.partners.length), function(el) {
@@ -74,7 +74,7 @@ openerp.account = function (instance) {
                 return fct();
             } else {
                 return new instance.web.Model("res.partner").call("read",
-                    [self.partners[self.current_partner][0], ["last_reconciliation_date"]]).pipe(function(res) {
+                    [self.partners[self.current_partner][0], ["last_reconciliation_date"]]).then(function(res) {
                     self.last_reconciliation_date = 
                         instance.web.format_value(res.last_reconciliation_date, {"type": "datetime"}, _t("Never"));
                     return fct();
@@ -92,7 +92,7 @@ openerp.account = function (instance) {
                 return false;
             }
 
-            new instance.web.Model("ir.model.data").call("get_object_reference", ["account", "action_view_account_move_line_reconcile"]).pipe(function(result) {
+            new instance.web.Model("ir.model.data").call("get_object_reference", ["account", "action_view_account_move_line_reconcile"]).then(function(result) {
                 var additional_context = _.extend({
                     active_id: ids[0],
                     active_ids: ids,
@@ -101,7 +101,7 @@ openerp.account = function (instance) {
                 return self.rpc("/web/action/load", {
                     action_id: result[1],
                     context: additional_context
-                }).then(function (result) {
+                }).done(function (result) {
                     result.context = _.extend(result.context || {}, additional_context);
                     result.flags = result.flags || {};
                     result.flags.new_window = true;
@@ -116,7 +116,7 @@ openerp.account = function (instance) {
         mark_as_reconciled: function() {
             var self = this;
             var id = self.partners[self.current_partner][0];
-            new instance.web.Model("res.partner").call("mark_as_reconciled", [[id]]).pipe(function() {
+            new instance.web.Model("res.partner").call("mark_as_reconciled", [[id]]).then(function() {
                 self.do_search(self.last_domain, self.last_context, self.last_group_by);
             });
         },
index 64f8ccf..7fe3095 100644 (file)
@@ -31,7 +31,6 @@
 -
   !python {model: account.account}: |
     ctx={}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')],'active_id':ref('account.chart0')})
     data_dict = {'chart_account_id':ref('account.chart0')}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_aged_balance_view',wiz_data=data_dict, context=ctx, our_module='account')
@@ -40,7 +39,6 @@
 -
   !python {model: account.account}: |
     ctx={}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
     data_dict = {'chart_account_id':ref('account.chart0'), 'account_report_id': ref('account_financial_report_balancesheet0')}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_report',wiz_data=data_dict, context=ctx, our_module='account')
@@ -49,7 +47,6 @@
 -
   !python {model: account.account}: |
     ctx={}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
     data_dict = {'chart_account_id':ref('account.chart0')}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_balance_menu',wiz_data=data_dict, context=ctx, our_module='account')
@@ -60,7 +57,6 @@
     journal_ids = [ref('account.sales_journal'),ref('account.refund_sales_journal'),ref('account.expenses_journal'),ref('account.refund_expenses_journal'),
                          ref('account.bank_journal'),ref('account.check_journal'),ref('account.cash_journal')]
     ctx={}
-    ctx.update({'model': 'account.journal.period','active_ids':journal_ids})
     data_dict = {'chart_account_id':ref('account.chart0')}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_central_journal',wiz_data=data_dict, context=ctx, our_module='account')
@@ -71,7 +67,6 @@
     journal_ids = [ref('account.sales_journal'),ref('account.refund_sales_journal'),ref('account.expenses_journal'),ref('account.refund_expenses_journal'),
                          ref('account.bank_journal'),ref('account.check_journal'),ref('account.cash_journal')]
     ctx={}
-    ctx.update({'model': 'account.journal.period','active_ids':journal_ids})
     data_dict = {'chart_account_id':ref('account.chart0')}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_general_journal',wiz_data=data_dict, context=ctx, our_module='account')
@@ -80,7 +75,6 @@
 -
   !python {model: account.account}: |
     ctx={}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
     data_dict = {'chart_account_id':ref('account.chart0'),'landscape':False}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_general_ledger_menu',wiz_data=data_dict, context=ctx, our_module='account')
@@ -89,7 +83,6 @@
 -
   !python {model: account.account}: |
     ctx={}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
     data_dict = {'chart_account_id':ref('account.chart0'),'landscape':True}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_general_ledger_menu',wiz_data=data_dict, context=ctx, our_module='account')
@@ -99,7 +92,6 @@
   !python {model: account.journal.period}: |
     journal_ids = [ref('account.sales_journal'),ref('account.refund_sales_journal'),ref('account.expenses_journal'),ref('account.refund_expenses_journal'),ref('account.bank_journal'),ref('account.check_journal'),ref('account.cash_journal')]
     ctx={}
-    ctx.update({'model': 'account.journal.period','active_ids':journal_ids})
     data_dict = {'chart_account_id':ref('account.chart0'), 'period_from':ref('period_1'), 'period_to':ref('period_12')}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_print_journal',wiz_data=data_dict, context=ctx, our_module='account')
   !python {model: account.account}: |
     ctx={}
     data_dict = {'chart_account_id':ref('account.chart0')}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')],'active_id':ref('account.chart0')})
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_partner_balance',wiz_data=data_dict, context=ctx, our_module='account')
 -
   !python {model: account.account}: |
     ctx={}
     data_dict = {'chart_account_id':ref('account.chart0'),'page_split': True}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')],'active_id':ref('account.chart0')})
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_partner_ledger',wiz_data=data_dict, context=ctx, our_module='account')
 -
   !python {model: res.partner}: |
     ctx={}
     data_dict = {'chart_account_id':ref('account.chart0'),'page_split': False}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')],'active_id':ref('account.chart0')})
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_partner_ledger',wiz_data=data_dict, context=ctx, our_module='account')
 -
 -
   !python {model: account.account}: |
     ctx={}
-    ctx.update({'model': 'account.account','active_ids':[ref('account.chart0')]})
     data_dict = {'chart_account_id':ref('account.chart0'), 'target_move': 'all', 'account_report_id': ref('account_financial_report_balancesheet0')}
     from tools import test_reports
     test_reports.try_report_action(cr, uid, 'action_account_report',wiz_data=data_dict, context=ctx, our_module='account')
index 196c14d..dbf815d 100644 (file)
@@ -226,7 +226,7 @@ class account_fiscalyear_close(osv.osv_memory):
         for account in obj_acc_account.browse(cr, uid, account_ids, context={'fiscalyear': fy_id}):
             balance_in_currency = 0.0
             if account.currency_id:
-                cr.execute('SELECT sum(amount_currency) as balance_in_currency FROM account_move_line ' \
+                cr.execute('SELECT sum(COALESCE(amount_currency,0.0)) as balance_in_currency FROM account_move_line ' \
                         'WHERE account_id = %s ' \
                             'AND ' + query_line + ' ' \
                             'AND currency_id = %s', (account.id, account.currency_id.id))
index 9acc09e..4438484 100644 (file)
@@ -119,7 +119,11 @@ class account_common_report(osv.osv_memory):
 
     def _get_fiscalyear(self, cr, uid, context=None):
         now = time.strftime('%Y-%m-%d')
-        fiscalyears = self.pool.get('account.fiscalyear').search(cr, uid, [('date_start', '<', now), ('date_stop', '>', now)], limit=1 )
+        company_id = False
+        ids = context.get('active_ids', [])
+        if ids:
+            company_id = self.browse(cr, uid, ids[0], context=context).company_id.id
+        fiscalyears = self.pool.get('account.fiscalyear').search(cr, uid, [('date_start', '<', now), ('date_stop', '>', now), ('company_id', '=', company_id)], limit=1)
         return fiscalyears and fiscalyears[0] or False
 
     def _get_all_journal(self, cr, uid, context=None):
index 76d6aee..e5c02c8 100644 (file)
        <field name="inherit_id" ref="account.view_move_form"/>
        <field name="arch" type="xml">
            <xpath expr="/form/sheet/notebook/page/field[@name='line_id']/tree/field[@name='analytic_account_id']" position="replace">
-               <field name="analytics_id" context="{'journal_id':journal_id}" groups="analytic.group_analytic_accounting"/>
+               <field name="analytics_id" context="{'journal_id':parent.journal_id}" groups="analytic.group_analytic_accounting"/>
            </xpath>
            <xpath expr="/form/sheet/notebook/page/field[@name='line_id']/form/notebook/page/group/group/field[@name='analytic_account_id']" position="replace">
-               <field name="analytics_id" context="{'journal_id':journal_id}" groups="analytic.group_analytic_accounting"/>
+               <field name="analytics_id" context="{'journal_id':parent.journal_id}" groups="analytic.group_analytic_accounting"/>
            </xpath>
        </field>
     </record>
 
     <record id="journal_col11" model="account.journal.column">
         <field eval="&quot;&quot;&quot;Analytic Distribution&quot;&quot;&quot;" name="name"/>
-        <field eval="11" name="sequence"/>
+        <field eval="15" name="sequence"/>
         <field name="view_id" ref="account.account_journal_view"/>
         <field eval="0" name="required"/>
         <field eval="&quot;&quot;&quot;analytics_id&quot;&quot;&quot;" name="field"/>
                 </field>
             </field>
         </record>
-         <record model="ir.ui.view" id="view_default_inherit_tree">
+        <record model="ir.ui.view" id="view_default_inherit_tree">
             <field name="name">account.analytic.default.tree.plans</field>
             <field name="model">account.analytic.default</field>
             <field name="inherit_id" ref="account_analytic_default.view_account_analytic_default_tree"/>
index 693283b..2627101 100644 (file)
@@ -40,7 +40,7 @@ class account_invoice_line(osv.osv):
 
         if inv.type in ('out_invoice','out_refund'):
             for i_line in inv.invoice_line:
-                if i_line.product_id:
+                if i_line.product_id and i_line.product_id.valuation == 'real_time':
                     if inv.type == 'out_invoice':
                         # debit account dacc will be the output account
                         # first check the product, if empty check the category
@@ -87,7 +87,7 @@ class account_invoice_line(osv.osv):
                             })
         elif inv.type in ('in_invoice','in_refund'):
             for i_line in inv.invoice_line:
-                if i_line.product_id:
+                if i_line.product_id and i_line.product_id.valuation == 'real_time':
                     if i_line.product_id.type != 'service':
                         # get the price difference account at the product
                         acc = i_line.product_id.property_account_creditor_price_difference and i_line.product_id.property_account_creditor_price_difference.id
index ac9024f..0dddf44 100644 (file)
@@ -386,7 +386,7 @@ class account_asset_depreciation_line(osv.osv):
             current_currency = line.asset_id.currency_id.id
             context.update({'date': depreciation_date})
             amount = currency_obj.compute(cr, uid, current_currency, company_currency, line.amount, context=context)
-            sign = line.asset_id.category_id.journal_id.type = 'purchase' and 1 or -1
+            sign = (line.asset_id.category_id.journal_id.type == 'purchase' and 1) or -1
             asset_name = line.asset_id.name
             reference = line.name
             move_vals = {
index cb0466a..d24124e 100644 (file)
         <menuitem parent="next_id_31"
             id="menu_act_crossovered_budget_view"
             action="act_crossovered_budget_view" sequence="1" />
-
+        
+       <record id="view_crossovered_budget_line_search" model="ir.ui.view">
+            <field name="name">account.budget.line.search</field>
+            <field name="model">crossovered.budget.lines</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Budget Lines">
+                    <field name="analytic_account_id"/>
+                </search>
+            </field>
+        </record>
+        
         <record model="ir.ui.view" id="view_crossovered_budget_line_tree">
             <field name="name">crossovered.budget.line.tree</field>
             <field name="model">crossovered.budget.lines</field>
index 8193264..c473e07 100644 (file)
@@ -8,14 +8,14 @@ msgstr ""
 "Project-Id-Version: openobject-addons\n"
 "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
 "POT-Creation-Date: 2012-02-08 00:35+0000\n"
-"PO-Revision-Date: 2012-02-09 19:28+0000\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"PO-Revision-Date: 2012-11-09 12:09+0000\n"
+"Last-Translator: Pedro Manuel Baeza <pedro.baeza@gmail.com>\n"
 "Language-Team: Spanish <es@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-30 05:36+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-10 04:59+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
 
 #. module: account_check_writing
 #: selection:res.company,check_layout:0
@@ -154,7 +154,7 @@ msgstr "Compañías"
 #. module: account_check_writing
 #: view:res.company:0
 msgid "Default Check Layout"
-msgstr ""
+msgstr "Comprobar formato por defecto"
 
 #. module: account_check_writing
 #: constraint:account.journal:0
index 1ad362b..b2d4041 100644 (file)
             <field eval="True" name="required"/>
             <field eval="2" name="sequence"/>
         </record>
+       <record id="sequence_journal" model="ir.sequence.type">
+           <field name="name">Account Journal</field>
+           <field name="code">account.journal</field>
+       </record>
+
+       <record id="sequence_journal_seq" model="ir.sequence">
+           <field name="name">Account journal sequence</field>
+           <field name="code">account.journal</field>
+           <field name="prefix">AJ</field>
+           <field eval="1" name="number_next"/>
+           <field eval="1" name="number_increment"/>
+       </record>
     </data>
 
 </openerp>
index 8bd9a48..b0c53b7 100644 (file)
@@ -267,6 +267,7 @@ class account_voucher(osv.osv):
     _order = "date desc, id desc"
 #    _rec_name = 'number'
     _columns = {
+        'active': fields.boolean('Active', help="By default, reconciliation vouchers made on draft bank statements are set as inactive, which allow to hide the customer/supplier payment while the bank statement isn't confirmed."),
         'type':fields.selection([
             ('sale','Sale'),
             ('purchase','Purchase'),
@@ -328,6 +329,7 @@ class account_voucher(osv.osv):
         'is_multi_currency': fields.boolean('Multi Currency Voucher', help='Fields with internal purpose only that depicts if the voucher is a multi currency one or not'),
     }
     _defaults = {
+        'active': True,
         'period_id': _get_period,
         'partner_id': _get_partner,
         'journal_id':_get_journal,
@@ -953,6 +955,9 @@ class account_voucher(osv.osv):
         if voucher_brw.number:
             name = voucher_brw.number
         elif voucher_brw.journal_id.sequence_id:
+            if not voucher_brw.journal_id.sequence_id.active:
+                raise osv.except_osv(_('Configuration Error !'),
+                    _('Please activate the sequence of selected journal !'))
             name = seq_obj.next_by_id(cr, uid, voucher_brw.journal_id.sequence_id.id, context=context)
         else:
             raise osv.except_osv(_('Error!'),
@@ -1504,6 +1509,15 @@ account_voucher_line()
 class account_bank_statement(osv.osv):
     _inherit = 'account.bank.statement'
 
+    def button_confirm_bank(self, cr, uid, ids, context=None):
+        voucher_obj = self.pool.get('account.voucher')
+        voucher_ids = []
+        for statement in self.browse(cr, uid, ids, context=context):
+            voucher_ids += [line.voucher_id.id for line in statement.line_ids if line.voucher_id]
+        if voucher_ids:
+            voucher_obj.write(cr, uid, voucher_ids, {'active': True}, context=context)
+        return super(account_bank_statement, self).button_confirm_bank(cr, uid, ids, context=context)
+
     def button_cancel(self, cr, uid, ids, context=None):
         voucher_obj = self.pool.get('account.voucher')
         for st in self.browse(cr, uid, ids, context=context):
@@ -1539,6 +1553,16 @@ account_bank_statement()
 class account_bank_statement_line(osv.osv):
     _inherit = 'account.bank.statement.line'
 
+    def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
+        res = super(account_bank_statement_line, self).onchange_partner_id(cr, uid, ids, partner_id, context=context)
+        if 'value' not in res:
+            res['value'] = {}
+        res['value'].update({'voucher_id' : False})
+        return res
+
+    def onchange_amount(self, cr, uid, ids, amount, context=None):
+        return {'value' :  {'voucher_id' : False}}
+
     def _amount_reconciled(self, cursor, user, ids, name, args, context=None):
         if not ids:
             return {}
@@ -1565,7 +1589,7 @@ class account_bank_statement_line(osv.osv):
     _columns = {
         'amount_reconciled': fields.function(_amount_reconciled,
             string='Amount reconciled', type='float'),
-        'voucher_id': fields.many2one('account.voucher', 'Payment'),
+        'voucher_id': fields.many2one('account.voucher', 'Reconciliation'),
     }
 
     def unlink(self, cr, uid, ids, context=None):
index babe60b..2cb393f 100644 (file)
             <field name="arch" type="xml">
                 <xpath expr="//div[@name='import_buttons']" position="inside">
                     <button name="%(action_view_account_statement_from_invoice_lines)d"
-                            string="Import Invoices" type="action" icon="gtk-execute"
+                            string="Import Invoices" type="action"
                             attrs="{'invisible':[('state','=','confirm')]}"/>
                 </xpath>
             </field>
         </record>
 
-        <record id="view_bank_statement_tree_voucher" model="ir.ui.view">
+        <record id="view_bank_statement_form_voucher" model="ir.ui.view">
             <field name="name">account.bank.statement.voucher.tree.inherit</field>
             <field name="model">account.bank.statement</field>
             <field name="inherit_id" ref="account.view_bank_statement_form"/>
             <field name="arch" type="xml">
                 <xpath expr="//page[@name='statement_line_ids']/field[@name='line_ids']/tree/field[@name='amount']" position="after">
-                    <field name="voucher_id" context="{'line_type': type, 'default_type': amount &lt; 0 and 'payment' or 'receipt', 'type': amount &lt; 0 and 'payment' or 'receipt', 'default_partner_id': partner_id, 'default_journal_id': parent.journal_id, 'default_amount': abs(amount), 'default_reference': ref, 'default_date': date, 'default_name': name}"/>
+                    <field name="voucher_id" widget="many2onebutton" options="{'label':{'create':'Reconcile','edit':'Edit Reconciliation'}}" context="{'line_type': type, 'default_type': amount &lt; 0 and 'payment' or 'receipt', 'type': amount &lt; 0 and 'payment' or 'receipt', 'default_partner_id': partner_id, 'default_journal_id': parent.journal_id, 'default_amount': abs(amount), 'default_reference': ref, 'default_date': date, 'default_name': name, 'default_active': False}"/>
                 </xpath>
-            </field>
-        </record>
-
-        <record id="view_bank_statement_form_voucher" model="ir.ui.view">
-            <field name="name">account.bank.statement.voucher.form.inherit</field>
-            <field name="model">account.bank.statement</field>
-            <field name="inherit_id" ref="account.view_bank_statement_form"/>
-            <field name="arch" type="xml">
                 <xpath expr="//page[@name='statement_line_ids']/field[@name='line_ids']/form/group/field[@name='sequence']" position="before">
-                    <field name="voucher_id" context="{'line_type': type, 'default_type': amount &lt; 0 and 'payment' or 'receipt', 'type': amount &lt; 0 and 'payment' or 'receipt', 'default_partner_id': partner_id, 'default_journal_id': parent.journal_id, 'default_amount': abs(amount), 'default_reference': ref, 'default_date': date, 'default_name': name}"/>
+                    <field name="voucher_id" widget="many2onebutton" options="{'label':{'create':'Reconcile','edit':'Edit Reconciliation'}}" context="{'line_type': type, 'default_type': amount &lt; 0 and 'payment' or 'receipt', 'type': amount &lt; 0 and 'payment' or 'receipt', 'default_partner_id': partner_id, 'default_journal_id': parent.journal_id, 'default_amount': abs(amount), 'default_reference': ref, 'default_date': date, 'default_name': name, 'default_active': False}"/>
                 </xpath>
+                <field name="amount" position="attributes">
+                    <attribute name="on_change">onchange_amount(amount)</attribute>
+                </field>
             </field>
         </record>
+
         <record id="view_cash_statement_tree_voucher" model="ir.ui.view">
             <field name="name">account.cash.statement.voucher.tree.inherit</field>
             <field name="model">account.bank.statement</field>
index ea82598..bec649e 100644 (file)
@@ -7,14 +7,15 @@ msgstr ""
 "Project-Id-Version: OpenERP Server 6.0dev\n"
 "Report-Msgid-Bugs-To: support@openerp.com\n"
 "POT-Creation-Date: 2012-02-08 01:37+0100\n"
-"PO-Revision-Date: 2012-05-10 17:31+0000\n"
-"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
+"PO-Revision-Date: 2012-11-07 13:27+0000\n"
+"Last-Translator: Frederic Clementi - Camptocamp.com "
+"<frederic.clementi@camptocamp.com>\n"
 "Language-Team: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-30 05:19+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-08 04:47+0000\n"
+"X-Generator: Launchpad (build 16232)\n"
 
 #. module: account_voucher
 #: view:sale.receipt.report:0
@@ -469,7 +470,7 @@ msgstr "Délai moyen de règlement"
 #. module: account_voucher
 #: field:res.company,income_currency_exchange_account_id:0
 msgid "Income Currency Rate"
-msgstr "Taux de change d'achat"
+msgstr "Compte de gain de change"
 
 #. module: account_voucher
 #: code:addons/account_voucher/account_voucher.py:1063
@@ -625,9 +626,9 @@ msgid ""
 "Unable to create accounting entry for currency rate difference. You have to "
 "configure the field 'Income Currency Rate' on the company! "
 msgstr ""
-"Impossible de créer une entrée de la comptabilité à cause de la différence "
-"de taux de change. Vous devez configurer le champ 'Taux de change de vente' "
-"sur  la société! "
+"Impossible de créer une écriture comptable à cause de la différence de taux "
+"de change. Vous devez configurer le champ 'Compte de gain de change' au "
+"niveau du formulaire de la société! "
 
 #. module: account_voucher
 #: view:account.voucher:0 view:sale.receipt.report:0
@@ -802,7 +803,7 @@ msgstr "Factures et transactions exceptionnelles"
 #. module: account_voucher
 #: field:res.company,expense_currency_exchange_account_id:0
 msgid "Expense Currency Rate"
-msgstr "Taux de change de la dépense"
+msgstr "Compte de perte de change"
 
 #. module: account_voucher
 #: sql_constraint:account.invoice:0
@@ -1089,9 +1090,9 @@ msgid ""
 "Unable to create accounting entry for currency rate difference. You have to "
 "configure the field 'Expense Currency Rate' on the company! "
 msgstr ""
-"Impossible de créer une entrée en comptabilité pour la différence de taux de "
-"change. Vous devez configurer le champ \"Taux de change d'achat\" de la "
-"société ! "
+"Impossible de créer une écriture comptable à cause de la différence de taux "
+"de change. Vous devez configurer le champ 'Compte de perte de change' au "
+"niveau du formulaire de la société! "
 
 #. module: account_voucher
 #: field:account.voucher,type:0
@@ -1156,7 +1157,7 @@ msgstr "Année"
 #. module: account_voucher
 #: field:account.voucher.line,amount_unreconciled:0
 msgid "Open Balance"
-msgstr "Solde initial"
+msgstr "Restant dû"
 
 #. module: account_voucher
 #: view:account.voucher:0 field:account.voucher,amount:0
index 881a5e3..b6f3676 100644 (file)
@@ -54,6 +54,8 @@
                 <form string="Bill Payment" version="7.0">
                     <group col="6">
                         <field name="partner_id" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" string="Supplier" context="{'default_customer': 0, 'search_default_supplier': 1, 'default_supplier': 1, 'invoice_currency': currency_id}"/>
+                        <field name="state" invisible="1"/>
+                        <field name="company_id" invisible="1"/>
                         <field name="amount" on_change="onchange_amount(amount, payment_rate, partner_id, journal_id, currency_id, type, date, payment_rate_currency_id, company_id, context)"/>
                         <field name="journal_id"
                             domain="[('type','in',['bank', 'cash'])]"
             <field name="model">account.voucher</field>
             <field name="arch" type="xml">
                 <form string="Receipt" version="7.0">
-                <header>
-                    <button name="proforma_voucher" string="Validate" states="draft" invisible="context.get('line_type', False)" class="oe_highlight"/>
-                    <button name="cancel_voucher" string="Cancel" states="draft,proforma"  invisible="context.get('line_type', False)"/>
-                    <button name="cancel_voucher" string="Unreconcile" type="object" states="posted" invisible="context.get('line_type', False)" confirm="Are you sure to unreconcile and cancel this record ?"/>
-                    <button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft" invisible="context.get('line_type', False)"/>
+                <header invisible="context.get('line_type', False)">
+                    <button name="proforma_voucher" string="Validate" states="draft" class="oe_highlight"/>
+                    <button name="cancel_voucher" string="Cancel" states="draft,proforma"/>
+                    <button name="cancel_voucher" string="Unreconcile" type="object" states="posted" confirm="Are you sure to unreconcile and cancel this record ?"/>
+                    <button name="action_cancel_draft" type="object" states="cancel" string="Set to Draft"/>
                     <field name="state" widget="statusbar" statusbar_visible="draft,posted" statusbar_colors='{"proforma":"blue"}'/>
                 </header>
                 <sheet>
                     <h1 attrs="{'invisible': [('number','=',False)]}"><field name="number"/></h1>
-                    <group>
+                    <group invisible="context.get('line_type', False)">
                         <group>
-                            <field name="partner_id" domain="[('customer','=',True)]" required="1" invisible="context.get('line_type', False)" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" string="Customer" context="{'search_default_customer': 1}"/>
+                            <field name="partner_id" domain="[('customer','=',True)]" required="1" on_change="onchange_partner_id(partner_id, journal_id, amount, currency_id, type, date, context)" string="Customer" context="{'search_default_customer': 1}"/>
                             <field name="currency_id" invisible="1"/>
                             <field name="amount" class="oe_inline"
                                 string="Paid Amount"
                                 widget="monetary" options="{'currency_field': 'currency_id'}"
-                                invisible="context.get('line_type', False)"
                                 on_change="onchange_amount(amount, payment_rate, partner_id, journal_id, currency_id, type, date, payment_rate_currency_id, company_id, context)"/>
                             <field name="journal_id"
                                 domain="[('type','in',['bank', 'cash'])]"
-                                invisible="context.get('line_type', False)"
                                 widget="selection"
                                 on_change="onchange_journal(journal_id, line_cr_ids, False, partner_id, date, amount, type, company_id, context)"
                                 string="Payment Method"/>
                         </group>
                         <group>
-                            <field name="date" invisible="context.get('line_type', False)" on_change="onchange_date(date, currency_id, payment_rate_currency_id, amount, company_id, context)"/>
-                            <field name="reference" invisible="context.get('line_type', False)" string="Payment Ref" placeholder="e.g. 003/10"/>
-                            <field name="name" colspan="2" invisible="context.get('line_type', False)" placeholder="e.g. Invoice SAJ/0042"/>
+                            <field name="date" on_change="onchange_date(date, currency_id, payment_rate_currency_id, amount, company_id, context)"/>
+                            <field name="reference" string="Payment Ref" placeholder="e.g. 003/10"/>
+                            <field name="name" colspan="2" placeholder="e.g. Invoice SAJ/0042"/>
                             <field name="company_id" widget="selection" groups="base.group_multi_company"/>
 
                             <field name="account_id"
                     </group>
                     <notebook>
                         <page string="Payment Information" groups="base.group_user">
-                            <label for="line_cr_ids"/>
+                            <label for="line_cr_ids" invisible="context.get('line_type', False)"/>
                             <field name="line_cr_ids" context="{'journal_id':journal_id, 'type':type, 'partner_id':partner_id}" on_change="onchange_line_ids(line_dr_ids, line_cr_ids, amount, currency_id, type, context)">
                                 <tree string="Invoices and outstanding transactions" editable="bottom" colors="gray:amount==0">
                                     <field name="move_line_id" context="{'journal_id':parent.journal_id, 'partner_id':parent.partner_id}"
                         </page>
                     </notebook>
                     </sheet>
-                    <div class="oe_chatter">
+                    <div class="oe_chatter" invisible="context.get('line_type', False)">
                         <field name="message_follower_ids" widget="mail_followers"/>
                         <field name="message_ids" widget="mail_thread"/>
                     </div>
index dd69173..0a0ad94 100644 (file)
@@ -50,7 +50,7 @@
             <field name="res_model">account.voucher</field>
             <field name="view_type">form</field>
             <field name="domain">[('journal_id.type', 'in', ['bank', 'cash']), ('type','=','receipt'), ('partner_id','=',partner_id)]</field>
-            <field name="context">{'type':'receipt', 'partner_id': partner_id, 'default_reference':reference}</field>
+            <field name="context">{'default_type':'receipt', 'type':'receipt', 'partner_id': partner_id, 'default_reference':reference}</field>
             <field name="view_id" ref="view_vendor_receipt_form"/>
             <field name="target">current</field>
         </record>
index 8dd73f6..1c9a095 100644 (file)
@@ -98,12 +98,11 @@ class account_analytic_account(osv.osv):
 
     def name_get(self, cr, uid, ids, context=None):
         res = []
-        full_ids = []
-        if isinstance(ids,list):
-            full_ids.extend(ids)
-        else:
-            full_ids.append(ids)
-        for id in full_ids:
+        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
index 17371f8..1277d25 100644 (file)
@@ -40,7 +40,7 @@
                                         Once the end date of the contract is
                                         passed or the maximum number of service
                                         units (e.g. support contract) is
-                                        reached, the account manager is warned
+                                        reached, the account manager is notified 
                                         by email to renew the contract with the
                                         customer.
                                     </p>
index 9232a69..45ab0c4 100644 (file)
@@ -388,6 +388,8 @@ class audittrail_objects_proxy(object_proxy):
         }
         # loop on all the fields
         for field_name, field_definition in pool.get(model.model)._all_columns.items():
+            if field_name in ('__last_update', 'id'):
+                continue
             #if the field_list param is given, skip all the fields not in that list
             if field_list and field_name not in field_list:
                 continue
index c26e1b5..5d5bdcc 100644 (file)
@@ -99,7 +99,7 @@
                         <field name="res_id" readonly="1"/>
                         <field name="object_id" readonly="1"/>
                     </group>
-                    <field name="line_ids" mode="tree,form"
+                    <field name="line_ids" mode="tree"
                         widget="one2many_list" readonly="1">
                         <form string="Log Lines" version="7.0">
                             <group col="4">
index 55425e2..8a78aca 100644 (file)
@@ -3,7 +3,7 @@ openerp.auth_anonymous = function(instance) {
     instance.web.Login.include({
         start: function() {
             var self = this;
-            return $.when(this._super()).pipe(function() {
+            return $.when(this._super()).then(function() {
                 var dblist = self._db_list || [];
                 if (!self.session.session_is_valid() && dblist.length === 1) {
                     self.remember_credentials = false;
index 5c80d29..15683cf 100644 (file)
@@ -12,7 +12,7 @@ openerp.auth_oauth = function(instance) {
             } else if(this.params.oauth_error === 2) {
                 this.do_warn("Authentication error","");
             }
-            return d.then(this.do_oauth_load).fail(function() {
+            return d.done(this.do_oauth_load).fail(function() {
                 self.do_oauth_load([]);
             });
         },
@@ -23,7 +23,7 @@ openerp.auth_oauth = function(instance) {
         do_oauth_load: function() {
             var db = this.$("form [name=db]").val();
             if (db) {
-                this.rpc("/auth_oauth/list_providers", { dbname: db }).then(this.on_oauth_loaded);
+                this.rpc("/auth_oauth/list_providers", { dbname: db }).done(this.on_oauth_loaded);
             }
         },
         on_oauth_loaded: function(result) {
index 56ec5b5..a8901ee 100644 (file)
@@ -69,7 +69,7 @@ instance.web.Login = instance.web.Login.extend({
     _check_error: function() {
         var self = this;
         if (this.params.loginerror !== undefined) {
-            this.rpc('/auth_openid/login/status', {}).then(function(result) {
+            this.rpc('/auth_openid/login/status', {}).done(function(result) {
                 if (_.contains(['success', 'failure'], result.status) && result.message) {
                     self.do_warn('Invalid OpenID Login', result.message);
                 }
@@ -106,7 +106,7 @@ instance.web.Login = instance.web.Login.extend({
 
     do_openid_login: function(db, openid_url) {
         var self = this;
-        this.rpc('/auth_openid/login/verify', {'db': db, 'url': openid_url}).then(function(result) {
+        this.rpc('/auth_openid/login/verify', {'db': db, 'url': openid_url}).done(function(result) {
             if (result.error) {
                 self.do_warn(result.title, result.error);
                 return;
index fe8ff9b..0e85533 100644 (file)
                         <group col="4">
                             <field name="name"/>
                             <field name="model_id"/>
-                            <field name="model" invisible="1"/>
                             <field name="filter_id" domain="[('model_id','=',model)]" context="{'default_model_id': model}"/>
                             <field name="sequence"/>
                             <field name="active"/>
+                            <field name="model" invisible="1"/>
                         </group>
                         <notebook>
                             <page string="Conditions">
index ae917d2..5efae77 100644 (file)
@@ -8,15 +8,14 @@ msgstr ""
 "Project-Id-Version: openobject-addons\n"
 "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
 "POT-Creation-Date: 2012-02-08 00:36+0000\n"
-"PO-Revision-Date: 2012-05-10 17:24+0000\n"
-"Last-Translator: Jordi Esteve (www.zikzakmedia.com) "
-"<jesteve@zikzakmedia.com>\n"
+"PO-Revision-Date: 2012-11-09 12:09+0000\n"
+"Last-Translator: Pedro Manuel Baeza <pedro.baeza@gmail.com>\n"
 "Language-Team: Spanish <es@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-30 05:32+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-10 04:59+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
 
 #. module: base_action_rule
 #: help:base.action.rule,act_mail_to_user:0
@@ -340,7 +339,7 @@ msgstr "Activo"
 #: code:addons/base_action_rule/base_action_rule.py:329
 #, python-format
 msgid "No Email ID Found for your Company address!"
-msgstr ""
+msgstr "¡No se ha encontrado Id del e-mail para la dirección de su compañía!"
 
 #. module: base_action_rule
 #: field:base.action.rule,act_remind_user:0
index 14e029e..c5fdbd7 100644 (file)
@@ -200,7 +200,7 @@ class users(osv.osv):
         else:
             # Return early if no one has a login name like that.
             return False
-
+        print "<--------------------------- HELLO ------------------------------>"
         stored_pw = self.maybe_encrypt(cr, stored_pw, id)
 
         if not stored_pw:
index c29647a..ff492a8 100644 (file)
@@ -99,6 +99,8 @@ class res_partner_bank(osv.osv):
             @param iban: IBAN as string
             @return: True if IBAN is valid, False otherwise
         """
+        if not iban:
+            return False
         iban = _format_iban(iban).lower()
         if iban[:2] in _ref_iban and len(iban) != len(_format_iban(_ref_iban[iban[:2]])):
             return False
@@ -128,9 +130,9 @@ class res_partner_bank(osv.osv):
     def _construct_constraint_msg(self, cr, uid, ids, context=None):
 
         def default_iban_check(iban_cn):
-            return iban_cn[0] in string.ascii_lowercase and iban_cn[1] in string.ascii_lowercase
+             return iban_cn and iban_cn[0] in string.ascii_lowercase and iban_cn[1] in string.ascii_lowercase
 
-        iban_country = self.browse(cr, uid, ids)[0].acc_number[:2].lower()
+        iban_country = self.browse(cr, uid, ids)[0].acc_number and self.browse(cr, uid, ids)[0].acc_number[:2].lower()
         if default_iban_check(iban_country):
             if iban_country in _ref_iban:
                 return _('The IBAN does not seem to be correct. You should have entered something like this %s'), \
index 1f11b3c..d29452c 100644 (file)
@@ -139,7 +139,7 @@ openerp.base_import = function (instance) {
                 this._super(),
                 this.Import.call('create', [{
                     'res_model': this.res_model
-                }]).then(function (id) {
+                }]).done(function (id) {
                     self.id = id;
                     self.$('input[name=import_id]').val(id);
                 })
@@ -200,7 +200,7 @@ openerp.base_import = function (instance) {
                 !this.$('input.oe_import_has_header').prop('checked'));
             this.Import.call(
                 'parse_preview', [this.id, this.import_options()])
-                .then(function (result) {
+                .done(function (result) {
                     var signal = result.error ? 'preview_failed' : 'preview_succeeded';
                     self[signal](result);
                 });
@@ -341,11 +341,11 @@ openerp.base_import = function (instance) {
         },
         onvalidate: function () {
             return this.call_import({ dryrun: true })
-                .then(this.proxy('validated'));
+                .done(this.proxy('validated'));
         },
         onimport: function () {
             var self = this;
-            return this.call_import({ dryrun: false }).then(function (message) {
+            return this.call_import({ dryrun: false }).done(function (message) {
                 if (!_.any(message, function (message) {
                         return message.type === 'error' })) {
                     self['import_succeeded']();
index 0dc4a0f..e789839 100644 (file)
@@ -60,14 +60,14 @@ class sale_config_settings(osv.osv_memory):
         'module_web_linkedin': fields.boolean('Get contacts automatically from linkedIn',
             help="""When you create a new contact (person or company), you will be able to load all the data from LinkedIn (photos, address, etc)."""),
         'module_crm': fields.boolean('CRM'),
-        'module_plugin_thunderbird': fields.boolean('Enable Thunderbird plugin',
+        'module_plugin_thunderbird': fields.boolean('Enable Thunderbird plug-in',
             help="""The plugin allows you archive email and its attachments to the selected
                 OpenERP objects. You can select a partner, or a lead and
                 attach the selected mail as a .eml file in
                 the attachment of a selected record. You can create documents for CRM Lead,
                 Partner from the selected emails.
                 This installs the module plugin_thunderbird."""),
-        'module_plugin_outlook': fields.boolean('Enable Outlook plugin',
+        'module_plugin_outlook': fields.boolean('Enable Outlook plug-in',
             help="""The Outlook plugin allows you to select an object that you would like to add
                 to your email and its attachments from MS Outlook. You can select a partner,
                 or a lead object and archive a selected
index 30802fc..eb835c4 100644 (file)
@@ -46,7 +46,7 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
                 delete(action.attrs.colspan);
                 var action_id = _.str.toNumber(action.attrs.name);
                 if (!_.isNaN(action_id)) {
-                    self.rpc('/web/action/load', {action_id: action_id}).then(function(result) {
+                    self.rpc('/web/action/load', {action_id: action_id}).done(function(result) {
                         self.on_load_action(result, column_index + '_' + action_index, action.attrs);
                     });
                 }
@@ -81,7 +81,7 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
         this.rpc('/web/view/undo_custom', {
             view_id: this.view.fields_view.view_id,
             reset: true
-        }).then(this.do_reload);
+        }).done(this.do_reload);
     },
     on_change_layout: function() {
         var self = this;
@@ -242,7 +242,7 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
             };
             var list = am.inner_widget.views.list;
             if (list) {
-                list.deferred.then(function() {
+                list.deferred.done(function() {
                     $(list.controller.groups).off('row_link').on('row_link', function(e, id) {
                         new_form_action(id);
                     });
@@ -250,7 +250,7 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
             }
             var kanban = am.inner_widget.views.kanban;
             if (kanban) {
-                kanban.deferred.then(function() {
+                kanban.deferred.done(function() {
                     kanban.controller.open_record = function(id, editable) {
                         new_form_action(id, editable);
                     };
@@ -335,7 +335,7 @@ instance.board.AddToDashboard = instance.web.search.Input.extend({
                 e.preventDefault();
                 self.add_dashboard();
             });
-        return this.load_data().then(this.proxy("render_data"));
+        return this.load_data().done(this.proxy("render_data"));
     },
     load_data:function(){
         var board = new instance.web.Model('board.board');
@@ -347,7 +347,7 @@ instance.board.AddToDashboard = instance.web.search.Input.extend({
         return new instance.web.Model('ir.model.data')
                 .query(['res_id'])
                 .filter([['name','=','menu_reporting_dashboard']])
-                .first().pipe(function (result) {
+                .first().then(function (result) {
             var menu = _(dashboard_menu).chain()
                 .pluck('children')
                 .flatten(true)
@@ -382,7 +382,7 @@ instance.board.AddToDashboard = instance.web.search.Input.extend({
             domain: domain,
             view_mode: view_parent.active_view,
             name: this.$el.find("input").val()
-        }).then(function(r) {
+        }).done(function(r) {
             if (r === false) {
                 self.do_warn("Could not add filter to dashboard");
             } else {
index eb602b9..3686562 100644 (file)
@@ -115,7 +115,6 @@ class crm_case_section(osv.osv):
         'code': fields.char('Code', size=8),
         'active': fields.boolean('Active', help="If the active field is set to "\
                         "true, it will allow you to hide the sales team without removing it."),
-        'allow_unlink': fields.boolean('Allow Delete', help="Allows to delete non draft cases"),
         'change_responsible': fields.boolean('Reassign Escalated', help="When escalating to this team override the salesman with the team leader."),
         'user_id': fields.many2one('res.users', 'Team Leader'),
         'member_ids':fields.many2many('res.users', 'sale_member_rel', 'section_id', 'member_id', 'Team Members'),
@@ -137,7 +136,6 @@ class crm_case_section(osv.osv):
 
     _defaults = {
         'active': 1,
-        'allow_unlink': 1,
         'stage_ids': _get_stage_common,
         'alias_domain': False, # always hide alias during creation
     }
index 910a1fc..4a8ee98 100644 (file)
@@ -79,7 +79,7 @@ class base_action_rule(osv.osv):
             write['section_id'] = action.act_section_id.id
 
         if hasattr(action, 'act_categ_id') and action.act_categ_id:
-            write['categ_ids'] = [4, action.act_categ_id.id]                        
+            write['categ_ids'] = [(4, action.act_categ_id.id)]
 
         model_obj.write(cr, uid, [obj.id], write, context)
         return res
index 5ab6886..547b25a 100644 (file)
@@ -610,9 +610,9 @@ class crm_lead(base_stage, format_address, osv.osv):
         }
 
     def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, section_id=False, context=None):
-        partner = self.pool.get('res.partner')
         customer = False
         if partner_id:
+            partner = self.pool.get('res.partner')
             customer = partner.browse(cr, uid, partner_id, context=context)
         for lead in self.browse(cr, uid, ids, context=context):
             if lead.state in ('done', 'cancel'):
@@ -676,19 +676,17 @@ class crm_lead(base_stage, format_address, osv.osv):
 
     def convert_partner(self, cr, uid, ids, action='create', partner_id=False, context=None):
         """
-        This function convert partner based on action.
+        Convert partner based on action.
         if action is 'create', create new partner with contact and assign lead to new partner_id.
         otherwise assign lead to specified partner_id
         """
         if context is None:
             context = {}
         partner_ids = {}
-        force_partner_id = partner_id
         for lead in self.browse(cr, uid, ids, context=context):
             if action == 'create':
                 if not partner_id:
                     partner_id = self._create_lead_partner(cr, uid, lead, context)
-                partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context=context)
             self._lead_set_partner(cr, uid, lead, partner_id, context=context)
             partner_ids[lead.id] = partner_id
         return partner_ids
@@ -781,14 +779,6 @@ class crm_lead(base_stage, format_address, osv.osv):
         }
         return res
 
-    def unlink(self, cr, uid, ids, context=None):
-        for lead in self.browse(cr, uid, ids, context):
-            if (not lead.section_id.allow_unlink) and (lead.state != 'draft'):
-                raise osv.except_osv(_('Error!'),
-                    _("You cannot delete lead '%s' because it is not in 'Draft' state. " \
-                      "You can still cancel it, instead of deleting it.") % lead.name)
-        return super(crm_lead, self).unlink(cr, uid, ids, context)
-
     def write(self, cr, uid, ids, vals, context=None):
         if vals.get('stage_id') and not vals.get('probability'):
             # change probability of lead(s) if required by stage
index 9cafe95..b87d6a3 100644 (file)
@@ -56,6 +56,7 @@ added to partners that match the segmentation criterions after computation.'),
             @param uid: the current user’s ID for security checks,
             @param ids: List of Process continue’s IDs"""
 
+        partner_obj = self.pool.get('res.partner')
         categs = self.read(cr, uid, ids, ['categ_id', 'exclusif', 'partner_id',\
                                  'sales_purchase_active', 'profiling_active'])
         for categ in categs:
@@ -80,9 +81,11 @@ added to partners that match the segmentation criterions after computation.'),
                 for pid in to_remove_list:
                     partners.remove(pid)
 
-            for partner_id in partners:
-                cr.execute('insert into res_partner_res_partner_category_rel (category_id,partner_id) \
-                        values (%s,%s)', (categ['categ_id'][0], partner_id))
+            for partner in partner_obj.browse(cr, uid, partners):
+                category_ids = [categ_id.id for categ_id in partner.category_id]
+                if categ['categ_id'][0] not in category_ids:
+                    cr.execute('insert into res_partner_res_partner_category_rel (category_id,partner_id) \
+                            values (%s,%s)', (categ['categ_id'][0], partner.id))
 
             self.write(cr, uid, [id], {'state':'not running', 'partner_id':0})
         return True
index 5e2034d..99d3752 100644 (file)
@@ -24,9 +24,9 @@
             parent="base.menu_base_config" sequence="45" groups="base.group_sale_salesman"/>
 
         <menuitem id="base.next_id_64" name="Sales"
-            parent="base.menu_reporting" sequence="1" />
+            parent="base.menu_reporting" sequence="1"/>
 
-        <menuitem id="base.menu_sales_configuration_misc" name="Miscellaneous" parent="base.menu_base_config" sequence="75"/>
+        <menuitem id="base.menu_sales_configuration_misc" name="Miscellaneous" parent="base.menu_base_config" sequence="81"/>
 
 
         <!-- crm.case.channel -->
@@ -86,7 +86,7 @@
                             <field name="parent_id"/>
                             <field name="code"/>
                         </group>
-                        
+
                         <group>
                             <field name="user_id"/>
                             <field name="resource_calendar_id"/>
                                     <field name="alias_name" class="oe_inline" attrs="{'required': [('alias_id', '!=', False)]}"/>@<field name="alias_domain" class="oe_inline"/>
                                  </div>
                                 <field name="change_responsible"/>
-                                <field name="allow_unlink"/>
                             </group>
                             <separator string="Team Members"/>
                             <field name="member_ids" widget="many2many_kanban">
             <field name="view_id" ref="crm_case_section_view_tree"/>
             <field name="help" type="html">
               <p class="oe_view_nocontent_create">
-                Click to define a new sales team. 
+                Click to define a new sales team.
               </p><p>
                 Use sales team to organize your different salespersons or
                 departments into separate teams. Each team will work in
                 <form string="Stage" version="7.0">
                     <group col="4">
                         <field name="name"/>
-                        <field name="state" />
+                        <field name="state"/>
                         <field name="probability"/>
                         <field name="type"/>
                         <field name="on_change"/>
         </record>
 
 
-     <!-- Case Categories Form View -->
+        <!-- Case Categories Form View -->
 
         <record id="crm_case_categ-view" model="ir.ui.view">
             <field name="name">crm.case.categ.form</field>
                     <group>
                         <field name="name"/>
                         <field name="section_id"/>
-                        <field name="object_id"  invisible="1" />
+                        <field name="object_id" invisible="1"/>
                     </group>
                 </form>
             </field>
          <menuitem action="crm_case_resource_type_act"
             id="menu_crm_case_resource_type_act" sequence="4"
             groups="base.group_no_one"
-            parent="base.menu_crm_config_lead" />
+            parent="base.menu_crm_config_lead"/>
 
         <record id="crm_case_section_act_tree" model="ir.actions.act_window">
             <field name="name">Cases by Sales Team</field>
                         <button name="process_start"
                             states="not running"
                             string="Compute Segmentation" type="object"
-                            icon="gtk-execute" />
+                            icon="gtk-execute"/>
                         <button name="process_stop" states="running"
                             string="Stop Process" type="object"
-                            icon="gtk-cancel" />
+                            icon="gtk-cancel"/>
                         <button name="process_continue" states="running"
                             string="Continue Process" type="object"
-                            icon="gtk-go-forward" />
+                            icon="gtk-go-forward"/>
                         <field name="state" widget="statusbar"/>
                     </header>
                     <group col="4">
             <field name="view_mode">tree,form</field>
             <field name="help" type="html">
               <p class="oe_view_nocontent_create">
-                Click to define a new customer segmentation. 
+                Click to define a new customer segmentation.
               </p><p>
                 Create specific categories which you can assign to your
                 contacts to better manage your interactions with them. The
        <menuitem action="crm_segmentation_tree-act"
             id="menu_crm_segmentation-act"
             groups="base.group_no_one" sequence="15"
-            parent="base.menu_base_config" />
+            parent="base.menu_base_config"/>
 
         <!-- menu for the working time -->
         <menuitem action="resource.action_resource_calendar_form" id="menu_action_resource_calendar_form" parent="resource.menu_resource_config" sequence="1"/>
             <field name="view_type">form</field>
             <field name="view_mode">tree,form</field>
         </record>
-            
+
         <menuitem action="action_crm_payment_mode"
             id="menu_crm_payment_mode_act"
             groups="base.group_no_one"
             name="Payment Modes"
-            parent="base.menu_crm_config_lead" />
+            parent="base.menu_crm_config_lead"/>
     </data>
 </openerp>
index fe2f139..a72a6ec 100644 (file)
@@ -7,14 +7,14 @@ msgstr ""
 "Project-Id-Version: OpenERP Server 6.0dev\n"
 "Report-Msgid-Bugs-To: support@openerp.com\n"
 "POT-Creation-Date: 2012-02-08 01:37+0100\n"
-"PO-Revision-Date: 2012-02-10 17:48+0000\n"
-"Last-Translator: Carlos Ch. <Unknown>\n"
+"PO-Revision-Date: 2012-11-09 12:10+0000\n"
+"Last-Translator: Pedro Manuel Baeza <pedro.baeza@gmail.com>\n"
 "Language-Team: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-30 04:57+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-10 04:59+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
 
 #. module: crm
 #: view:crm.lead.report:0
@@ -172,7 +172,7 @@ msgstr "Mes esperado de cierre"
 #. module: crm
 #: view:crm.lead2opportunity.partner.mass:0
 msgid "Assigned Opportunities to"
-msgstr ""
+msgstr "Oportunidades asignadas a"
 
 #. module: crm
 #: view:crm.lead:0 field:crm.lead,partner_id:0 view:crm.lead.report:0
@@ -598,7 +598,7 @@ msgstr "Fecha de fin"
 #. module: crm
 #: view:crm.opportunity2phonecall:0 view:crm.phonecall2phonecall:0
 msgid "Schedule/Log a Call"
-msgstr ""
+msgstr "Planificar/Registrar una llamada"
 
 #. module: crm
 #: constraint:base.action.rule:0
@@ -792,7 +792,7 @@ msgstr "Siguiente"
 #. module: crm
 #: field:crm.segmentation,som_interval:0
 msgid "Days per Period"
-msgstr ""
+msgstr "Días por periodo"
 
 #. module: crm
 #: field:crm.meeting,byday:0
@@ -959,7 +959,7 @@ msgstr "Días para abrir"
 #. module: crm
 #: view:crm.meeting:0
 msgid "Show Time as"
-msgstr ""
+msgstr "Mostrar hora como"
 
 #. module: crm
 #: view:crm.phonecall2partner:0
@@ -1342,7 +1342,7 @@ msgstr "Fecha escritura"
 #. module: crm
 #: view:crm.meeting:0
 msgid "End of Recurrency"
-msgstr ""
+msgstr "Fin de recurrencia"
 
 #. module: crm
 #: view:crm.meeting:0
@@ -1392,6 +1392,12 @@ msgid ""
 "       \n"
 "If the call needs to be done then the state is set to 'Not Held'."
 msgstr ""
+"El estado se establece a 'Para hacer' cuando se crea el caso.\n"
+"Si es caso está en marcha, el estado se establece a 'Abierto.\n"
+"Cuando la llamada se termina, el estado se establece en 'Realizada'.         "
+"                         \n"
+"Si la llamada está pendiente de ser realizada, entonces el estado se "
+"establece en 'Pendiente'."
 
 #. module: crm
 #: selection:crm.meeting,week_list:0
@@ -1449,7 +1455,7 @@ msgstr "Oportunidades por categorías"
 #. module: crm
 #: model:crm.case.section,name:crm.section_sales_marketing_department
 msgid "Sales Marketing Department"
-msgstr ""
+msgstr "Departamento de ventas y marketing"
 
 #. module: crm
 #: view:crm.phonecall.report:0
@@ -1900,7 +1906,7 @@ msgstr "Responder a"
 #. module: crm
 #: view:crm.case.section:0
 msgid "Select Stages for this Sales Team"
-msgstr ""
+msgstr "Seleccione etapas para este equipo de ventas"
 
 #. module: crm
 #: view:board.board:0
@@ -2015,7 +2021,7 @@ msgstr "Ubicación"
 #. module: crm
 #: model:ir.model,name:crm.model_crm_lead2opportunity_partner_mass
 msgid "Mass Lead To Opportunity Partner"
-msgstr ""
+msgstr "Transformación masiva de iniciativa a oportunidad"
 
 #. module: crm
 #: view:crm.lead:0
@@ -2777,7 +2783,7 @@ msgstr "e-mail del contacto"
 #. module: crm
 #: field:crm.lead,referred:0
 msgid "Referred by"
-msgstr ""
+msgstr "Referido por"
 
 #. module: crm
 #: view:crm.lead:0 model:ir.model,name:crm.model_crm_add_note
@@ -3541,7 +3547,7 @@ msgstr "Nuevas oportunidades"
 #: code:addons/crm/crm_action_rule.py:61
 #, python-format
 msgid "No E-Mail Found for your Company address!"
-msgstr ""
+msgstr "No se encontró dirección de e-mail para el contacto de su compañía"
 
 #. module: crm
 #: field:crm.lead.report,email:0
@@ -3561,7 +3567,7 @@ msgstr "Oportunidades por usuario y equipo"
 #. module: crm
 #: view:crm.phonecall:0
 msgid "Reset to Todo"
-msgstr ""
+msgstr "Cambiar a 'Para hacer'"
 
 #. module: crm
 #: field:crm.case.section,working_hours:0
@@ -3643,6 +3649,10 @@ msgid ""
 "partner. From the phone call form, you can trigger a request for another "
 "call, a meeting or an opportunity."
 msgstr ""
+"Esta herramienta permite registrar sus llamadas entrantes sobre la marcha. "
+"Cada llamada que recibe aparecerá en el formulario de la empresa para trazar "
+"cada contacto que tiene con una empresa. Desde el formulario de llamadas, "
+"puede lanzar una petición para otra llamada, una reunión u oportunidad."
 
 #. module: crm
 #: selection:crm.lead.report,creation_month:0
@@ -3673,6 +3683,10 @@ msgid ""
 "channels that will be maintained at the creation of a document in the "
 "system. Some examples of channels can be: Website, Phone Call, Reseller, etc."
 msgstr ""
+"Controle el origen de sus iniciativas y oportunidades de venta mediante la "
+"creación de canales específicos que se usarán en la creación de documentos "
+"en el sistema. Algunos ejemplos de canales son: Sitio web, llamada "
+"telefónica, distribuidores, ..."
 
 #. module: crm
 #: selection:crm.lead2opportunity.partner,name:0
@@ -3740,7 +3754,7 @@ msgstr "Año"
 #. module: crm
 #: constraint:res.partner:0
 msgid "Error ! You cannot create recursive associated members."
-msgstr ""
+msgstr "¡Error! No puede crear miembros asociados recursivamente."
 
 #. module: crm
 #: model:crm.case.resource.type,name:crm.type_lead8
index a2120ae..1f1a4f9 100644 (file)
@@ -91,7 +91,7 @@
             <field name="res_model">crm.phonecall.report</field>
             <field name="view_type">form</field>
             <field name="view_mode">tree,graph</field>
-            <field name="context">{"search_default_year":1,"search_default_User":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
+            <field name="context">{"search_default_year":1,"search_default_Salesperson":1,"search_default_This Month":1,'group_by_no_leaf':1,'group_by':[]}</field>
             <field name="view_id" ref="view_report_crm_phonecall_tree"/>
             <field name="search_view_id" ref="view_report_crm_phonecall_filter"/>
             <field name="help">From this report, you can analyse the performance of your sales team, based on their phone calls. You can group or filter the information according to several criteria and drill down the information, by adding more groups in the report.</field>
index 2473a83..ea96e1d 100644 (file)
@@ -175,12 +175,19 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory):
         return res
 
     def _convert_opportunity(self, cr, uid, ids, vals, context=None):
+        """
+        When "massively" (more than one at a time) converting leads to
+        opportunities, check the salesteam_id and salesmen_ids and update
+        the values before calling super.
+        """
+        if context is None:
+            context = {}
         data = self.browse(cr, uid, ids, context=context)[0]
         salesteam_id = data.section_id and data.section_id.id or False
-        salesman = []
+        salesmen_ids = []
         if data.user_ids:
-            salesman = [x.id for x in data.user_ids]
-        vals.update({'user_ids': salesman, 'section_id': salesteam_id})
+            salesmen_ids = [x.id for x in data.user_ids]
+        vals.update({'user_ids': salesmen_ids, 'section_id': salesteam_id})
         return super(crm_lead2opportunity_mass_convert, self)._convert_opportunity(cr, uid, ids, vals, context=context)
 
     def mass_convert(self, cr, uid, ids, context=None):
index c201f48..c48990f 100644 (file)
@@ -35,7 +35,7 @@ class crm_lead2partner(osv.osv_memory):
     }
     def view_init(self, cr, uid, fields, context=None):
         """
-        This function checks for precondition before wizard executes
+        Check for precondition before wizard executes.
         """
         if context is None:
             context = {}
@@ -69,22 +69,19 @@ class crm_lead2partner(osv.osv_memory):
         return partner_id
 
     def default_get(self, cr, uid, fields, context=None):
-        """
-        This function gets default values
-        """
-        res = super(crm_lead2partner, self).default_get(cr, uid, fields, context=context)        
+        res = super(crm_lead2partner, self).default_get(cr, uid, fields, context=context)
         partner_id = self._select_partner(cr, uid, context=context)
 
         if 'partner_id' in fields:
             res.update({'partner_id': partner_id})
         if 'action' in fields:
             res.update({'action': partner_id and 'exist' or 'create'})
-            
+
         return res
 
     def open_create_partner(self, cr, uid, ids, context=None):
         """
-        This function Opens form of create partner.
+        Open form of create partner.
         """
         view_obj = self.pool.get('ir.ui.view')
         view_id = view_obj.search(cr, uid, [('model', '=', self._name), \
@@ -101,21 +98,21 @@ class crm_lead2partner(osv.osv_memory):
 
     def _create_partner(self, cr, uid, ids, context=None):
         """
-        This function Creates partner based on action.
+        Create partner based on action.
         """
         if context is None:
             context = {}
         lead = self.pool.get('crm.lead')
-        lead_ids = context and context.get('active_ids') or []
+        lead_ids = context.get('active_ids', [])
         data = self.browse(cr, uid, ids, context=context)[0]
         partner_id = data.partner_id and data.partner_id.id or False
         return lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context)
 
     def make_partner(self, cr, uid, ids, context=None):
         """
-        This function Makes partner based on action.
+        Make a partner based on action.
+        Only called from form view, so only meant to convert one lead at a time.
         """
-        # Only called from Form view, so only meant to convert  one Lead. 
         lead_id = context and context.get('active_id') or False
         partner_ids_map = self._create_partner(cr, uid, ids, context=context)
         return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_ids_map.get(lead_id, False), context=context)
index e4d5f59..8308e54 100644 (file)
@@ -105,7 +105,7 @@ class crm_claim(base_stage, osv.osv):
         'email_from': fields.char('Email', size=128, help="Destination email for email gateway."),
         'partner_phone': fields.char('Phone', size=32),
         'stage_id': fields.many2one ('crm.claim.stage', 'Stage',
-                        domain="['|', ('section_ids', '=', section_id), ('case_default', '=', True)]"),
+                        domain="['&',('fold', '=', False),'|', ('section_ids', '=', section_id), ('case_default', '=', True)]"),
         'cause': fields.text('Root Cause'),
         'state': fields.related('stage_id', 'state', type="selection", store=True,
                 selection=crm.AVAILABLE_STATES, string="Status", readonly=True,
index 33bd280..ed0558f 100644 (file)
         -->
         
         <record model="crm.claim.stage" id="stage_claim1">
-            <field name="name">Draft claim</field>
+            <field name="name">New</field>
             <field name="state">draft</field>
             <field name="sequence">26</field>
             <field name="case_default" eval="True"/>
         </record>
         <record model="crm.claim.stage" id="stage_claim5">
-            <field name="name">Actions Defined</field>
+            <field name="name">In Progress</field>
             <field name="state">open</field>
             <field name="sequence">27</field>
             <field name="case_default" eval="True"/>
         </record>
         <record model="crm.claim.stage" id="stage_claim2">
-            <field name="name">Actions Done</field>
+            <field name="name">Settled</field>
             <field name="state">done</field>
             <field name="sequence">28</field>
             <field name="case_default" eval="True"/>
         </record>
         <record model="crm.claim.stage" id="stage_claim3">
-            <field name="name">Refused</field>
-            <field name="state">done</field>
+            <field name="name">Rejected</field>
+            <field name="state">cancel</field>
             <field name="sequence">29</field>
             <field name="case_default" eval="True"/>
             <field name="case_refused" eval="True"/>
             <field name="fold" eval="True"/>
         </record>
-        <record model="crm.claim.stage" id="stage_claim3">
-            <field name="name">Cancelled</field>
-            <field name="state">cancel</field>
-            <field name="sequence">30</field>
-            <field name="case_default" eval="True"/>
-            <field name="fold" eval="True"/>
-        </record>
+        
 
     </data>
 </openerp>
index 3782a3a..4c0eca3 100644 (file)
@@ -52,7 +52,7 @@
         <!-- Claim Stages -->
         <menuitem id="base.menu_definitions" name="Configuration" parent="base.menu_main_pm" sequence="60"/>
         <menuitem id="base.menu_project_config_project" name="Stages" parent="base.menu_definitions" sequence="1"/>
-        <menuitem id="menu_claim_stage_view" name="Claim Stages" action="crm_claim_stage_act" parent="base.menu_project_config_project" sequence="20"/>
+        <menuitem id="menu_claim_stage_view" name="Stages" action="crm_claim_stage_act" parent="menu_config_claim" sequence="20"/>
 
     </data>
 </openerp>
index 2b5ca4b..aa4243a 100644 (file)
             <field name="arch" type="xml">
                 <form string="Claim" version="7.0">
                 <header>
-                    <button name="case_open" string="Open" type="object" class="oe_highlight"
-                            states="draft,pending" groups="base.group_user"/>
-                    <button name="case_close" string="Done" type="object" class="oe_highlight"
-                            states="open,pending" groups="base.group_user"/>
-                    <button name="case_reset" string="Reset to Draft" type="object" groups="base.group_user"
-                            states="cancel,done"/>
-                    <button name="case_cancel" string="Cancel" type="object"  groups="base.group_user"
+                    <button name="case_close" string="Settle" type="object" class="oe_highlight"
+                            states="draft,open,pending" groups="base.group_user"/>
+                    <button name="case_cancel" string="Reject" type="object"  groups="base.group_user"
                             states="draft,open,pending"/>
-                    <field name="stage_id" widget="statusbar"
-                            on_change="onchange_stage_id(stage_id)"/>
+                    <field name="stage_id" widget="statusbar" clickable="True"/>
                 </header>
                 <sheet string="Claims">
                     <group>
index 1f1fb3d..4f6521a 100644 (file)
@@ -8,14 +8,14 @@ msgstr ""
 "Project-Id-Version: openobject-addons\n"
 "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
 "POT-Creation-Date: 2012-02-08 00:36+0000\n"
-"PO-Revision-Date: 2011-01-16 17:13+0000\n"
-"Last-Translator: mgaja (GrupoIsep.com) <Unknown>\n"
+"PO-Revision-Date: 2012-11-09 12:10+0000\n"
+"Last-Translator: Pedro Manuel Baeza <pedro.baeza@gmail.com>\n"
 "Language-Team: Spanish <es@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-30 05:33+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-10 04:59+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
 
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,send_to:0
@@ -25,12 +25,12 @@ msgstr "Enviar a"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,subtype:0
 msgid "Message type"
-msgstr ""
+msgstr "Tipo de mensaje"
 
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,auto_delete:0
 msgid "Permanently delete emails after sending"
-msgstr ""
+msgstr "Eliminar permanentemente los emails depués de su envío"
 
 #. module: crm_partner_assign
 #: field:crm.lead.report.assign,delay_close:0
@@ -40,7 +40,7 @@ msgstr "Retraso para cerrar"
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,email_to:0
 msgid "Message recipients"
-msgstr ""
+msgstr "Destinatarios del mensaje"
 
 #. module: crm_partner_assign
 #: field:crm.lead.report.assign,planned_revenue:0
@@ -61,7 +61,7 @@ msgstr "Agrupar por..."
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,template_id:0
 msgid "Template"
-msgstr ""
+msgstr "Plantilla"
 
 #. module: crm_partner_assign
 #: view:crm.lead:0
@@ -76,12 +76,12 @@ msgstr "Geo localizar"
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,body:0
 msgid "Plain-text version of the message"
-msgstr ""
+msgstr "Versión en texto plano del mensaje"
 
 #. module: crm_partner_assign
 #: view:crm.lead.forward.to.partner:0
 msgid "Body"
-msgstr ""
+msgstr "Cuerpo"
 
 #. module: crm_partner_assign
 #: selection:crm.lead.report.assign,month:0
@@ -101,7 +101,7 @@ msgstr "Demora cierre"
 #. module: crm_partner_assign
 #: view:crm.partner.report.assign:0
 msgid "#Partner"
-msgstr ""
+msgstr "Nº empresa"
 
 #. module: crm_partner_assign
 #: selection:crm.lead.forward.to.partner,history:0
@@ -137,7 +137,7 @@ msgstr "Más alta"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,body:0
 msgid "Text contents"
-msgstr ""
+msgstr "Contenido del texto"
 
 #. module: crm_partner_assign
 #: view:crm.lead.report.assign:0
@@ -148,7 +148,7 @@ msgstr "Día"
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,message_id:0
 msgid "Message unique identifier"
-msgstr ""
+msgstr "Identificador único del mensaje"
 
 #. module: crm_partner_assign
 #: selection:crm.lead.forward.to.partner,history:0
@@ -167,6 +167,8 @@ msgid ""
 "Add here all attachments of the current document you want to include in the "
 "Email."
 msgstr ""
+"Añada aquí todos los datos adjuntos del documento que quiere incluir en el "
+"correo."
 
 #. module: crm_partner_assign
 #: selection:crm.lead.report.assign,state:0
@@ -195,17 +197,17 @@ msgstr ""
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,body_html:0
 msgid "Rich-text/HTML version of the message"
-msgstr ""
+msgstr "Versión en texto enriquecido / HTML del mensaje"
 
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,auto_delete:0
 msgid "Auto Delete"
-msgstr ""
+msgstr "Auto eliminar"
 
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,email_bcc:0
 msgid "Blind carbon copy message recipients"
-msgstr ""
+msgstr "Destinatarios de la copia oculta"
 
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,partner_id:0
@@ -254,7 +256,7 @@ msgstr "Sección"
 #. module: crm_partner_assign
 #: view:crm.lead.forward.to.partner:0
 msgid "Send"
-msgstr ""
+msgstr "Enviar"
 
 #. module: crm_partner_assign
 #: view:res.partner:0
@@ -286,7 +288,7 @@ msgstr "Tipo"
 #. module: crm_partner_assign
 #: view:crm.partner.report.assign:0
 msgid "Name"
-msgstr ""
+msgstr "Nombre"
 
 #. module: crm_partner_assign
 #: selection:crm.lead.report.assign,priority:0
@@ -299,11 +301,13 @@ msgid ""
 "Type of message, usually 'html' or 'plain', used to select plaintext or rich "
 "text contents accordingly"
 msgstr ""
+"Tipo de mensaje, normalmente 'html' o 'plano', utilizado para seleccionar "
+"contenidos en texto plano o en texto enriquecido"
 
 #. module: crm_partner_assign
 #: view:crm.lead.report.assign:0
 msgid "Assign Date"
-msgstr ""
+msgstr "Fecha de asignación"
 
 #. module: crm_partner_assign
 #: view:crm.lead.report.assign:0
@@ -318,7 +322,7 @@ msgstr "Fecha creación"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,res_id:0
 msgid "Related Document ID"
-msgstr ""
+msgstr "ID del docuemtno relacionado"
 
 #. module: crm_partner_assign
 #: view:crm.lead.report.assign:0
@@ -349,7 +353,7 @@ msgstr "Etapa"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,model:0
 msgid "Related Document model"
-msgstr ""
+msgstr "Modelo del documento relacionado"
 
 #. module: crm_partner_assign
 #: code:addons/crm_partner_assign/wizard/crm_forward_to_partner.py:192
@@ -392,7 +396,7 @@ msgstr "Cerrar"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,use_template:0
 msgid "Use Template"
-msgstr ""
+msgstr "Usar plantilla"
 
 #. module: crm_partner_assign
 #: model:ir.actions.act_window,name:crm_partner_assign.action_report_crm_opportunity_assign
@@ -464,12 +468,12 @@ msgstr "nº oportunidades"
 #. module: crm_partner_assign
 #: view:crm.lead:0
 msgid "Team"
-msgstr ""
+msgstr "Equipo"
 
 #. module: crm_partner_assign
 #: view:crm.lead:0
 msgid "Referred Partner"
-msgstr ""
+msgstr "Empresa referida"
 
 #. module: crm_partner_assign
 #: selection:crm.lead.report.assign,state:0
@@ -490,7 +494,7 @@ msgstr "Cerrado"
 #. module: crm_partner_assign
 #: model:ir.actions.act_window,name:crm_partner_assign.action_crm_send_mass_forward
 msgid "Mass forward to partner"
-msgstr ""
+msgstr "Envío masivo a empresa"
 
 #. module: crm_partner_assign
 #: view:res.partner:0
@@ -570,7 +574,7 @@ msgstr "Longitud Geo"
 #. module: crm_partner_assign
 #: field:crm.partner.report.assign,opp:0
 msgid "# of Opportunity"
-msgstr ""
+msgstr "Nº oportunidad"
 
 #. module: crm_partner_assign
 #: view:crm.lead.report.assign:0
@@ -600,12 +604,12 @@ msgstr "Empresa a la que este caso ha sido reenviado/asignado."
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,date:0
 msgid "Date"
-msgstr ""
+msgstr "Fecha"
 
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,body_html:0
 msgid "Rich-text contents"
-msgstr ""
+msgstr "Contenido en texto enriquecido"
 
 #. module: crm_partner_assign
 #: view:crm.lead.report.assign:0
@@ -620,18 +624,18 @@ msgstr "res.empresa.nivel"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,message_id:0
 msgid "Message-Id"
-msgstr ""
+msgstr "Id del mensaje"
 
 #. module: crm_partner_assign
 #: view:crm.lead.forward.to.partner:0
 #: field:crm.lead.forward.to.partner,attachment_ids:0
 msgid "Attachments"
-msgstr ""
+msgstr "Adjuntos"
 
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,email_cc:0
 msgid "Cc"
-msgstr ""
+msgstr "Cc"
 
 #. module: crm_partner_assign
 #: selection:crm.lead.report.assign,month:0
@@ -641,7 +645,7 @@ msgstr "Septiembre"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,references:0
 msgid "References"
-msgstr ""
+msgstr "Referencias"
 
 #. module: crm_partner_assign
 #: view:crm.lead.report.assign:0
@@ -668,7 +672,7 @@ msgstr "Abierto"
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,email_cc:0
 msgid "Carbon copy message recipients"
-msgstr ""
+msgstr "Destinatarios de la copia"
 
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,headers:0
@@ -676,6 +680,8 @@ msgid ""
 "Full message headers, e.g. SMTP session headers (usually available on "
 "inbound messages only)"
 msgstr ""
+"Cabeceras completas del mensaje, por ejemplo las cabeceras de sesión SMTP "
+"(normalmente disponibles sólo en mensajes entrantes)"
 
 #. module: crm_partner_assign
 #: field:res.partner,date_localization:0
@@ -698,11 +704,13 @@ msgid ""
 "Message sender, taken from user preferences. If empty, this is not a mail "
 "but a message."
 msgstr ""
+"Remitente del mensaje, proveniente de las preferencias del usuario. Si está "
+"vacío, esto no es correo electrónico, sino un mensaje."
 
 #. module: crm_partner_assign
 #: field:crm.partner.report.assign,nbr:0
 msgid "# of Partner"
-msgstr ""
+msgstr "Nº empresa"
 
 #. module: crm_partner_assign
 #: view:crm.lead.forward.to.partner:0
@@ -713,7 +721,7 @@ msgstr "Reenviar a empresa"
 #. module: crm_partner_assign
 #: field:crm.partner.report.assign,name:0
 msgid "Partner name"
-msgstr ""
+msgstr "Nombre de la empresa"
 
 #. module: crm_partner_assign
 #: selection:crm.lead.report.assign,month:0
@@ -728,7 +736,7 @@ msgstr "Ingreso estimado"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,reply_to:0
 msgid "Reply-To"
-msgstr ""
+msgstr "Responder a"
 
 #. module: crm_partner_assign
 #: field:crm.lead,partner_assigned_id:0
@@ -748,7 +756,7 @@ msgstr "Oportunidad"
 #. module: crm_partner_assign
 #: view:crm.lead.forward.to.partner:0
 msgid "Send Mail"
-msgstr ""
+msgstr "Enviar correo"
 
 #. module: crm_partner_assign
 #: field:crm.lead.report.assign,partner_id:0
@@ -776,7 +784,7 @@ msgstr "País"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,headers:0
 msgid "Message headers"
-msgstr ""
+msgstr "Cabeceras del mensaje"
 
 #. module: crm_partner_assign
 #: view:res.partner:0
@@ -786,7 +794,7 @@ msgstr "Convertir en oportunidad"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,email_bcc:0
 msgid "Bcc"
-msgstr ""
+msgstr "Cco"
 
 #. module: crm_partner_assign
 #: view:crm.lead:0
@@ -802,7 +810,7 @@ msgstr "Abril"
 #: model:ir.actions.act_window,name:crm_partner_assign.action_report_crm_partner_assign
 #: model:ir.ui.menu,name:crm_partner_assign.menu_report_crm_partner_assign_tree
 msgid "Partnership Analysis"
-msgstr ""
+msgstr "Análisis de la relación"
 
 #. module: crm_partner_assign
 #: model:ir.model,name:crm_partner_assign.model_crm_lead
@@ -817,7 +825,7 @@ msgstr "Pendiente"
 #. module: crm_partner_assign
 #: view:crm.partner.report.assign:0
 msgid "Partner assigned Analysis"
-msgstr ""
+msgstr "Análisis de la empresa asignada"
 
 #. module: crm_partner_assign
 #: model:ir.model,name:crm_partner_assign.model_crm_lead_report_assign
@@ -828,11 +836,12 @@ msgstr "Informe de iniciativas CRM"
 #: help:crm.lead.forward.to.partner,references:0
 msgid "Message references, such as identifiers of previous messages"
 msgstr ""
+"Referencias del mensaje, tales como identificadores de mensajes anteriores"
 
 #. module: crm_partner_assign
 #: constraint:res.partner:0
 msgid "Error ! You cannot create recursive associated members."
-msgstr ""
+msgstr "¡Error! No puede crear miembros asociados recursivamente."
 
 #. module: crm_partner_assign
 #: selection:crm.lead.forward.to.partner,history:0
@@ -847,12 +856,12 @@ msgstr "Secuencia"
 #. module: crm_partner_assign
 #: model:ir.model,name:crm_partner_assign.model_crm_partner_report_assign
 msgid "CRM Partner Report"
-msgstr ""
+msgstr "Informe de la empresa CRM"
 
 #. module: crm_partner_assign
 #: model:ir.model,name:crm_partner_assign.model_crm_lead_forward_to_partner
 msgid "Email composition wizard"
-msgstr ""
+msgstr "Asistente de composición de e-mail"
 
 #. module: crm_partner_assign
 #: selection:crm.lead.report.assign,priority:0
@@ -873,7 +882,7 @@ msgstr "Fecha de creación"
 #. module: crm_partner_assign
 #: field:crm.lead.forward.to.partner,filter_id:0
 msgid "Filters"
-msgstr ""
+msgstr "Filtros"
 
 #. module: crm_partner_assign
 #: view:crm.lead.report.assign:0
@@ -884,7 +893,7 @@ msgstr "Año"
 #. module: crm_partner_assign
 #: help:crm.lead.forward.to.partner,reply_to:0
 msgid "Preferred response address for the message"
-msgstr ""
+msgstr "Dirección de correo de respuesta preferida para este mensaje"
 
 #~ msgid "Reply-to of the Sales team defined on this case"
 #~ msgstr "\"Responder a\" del equipo de ventas definido en este caso"
@@ -941,3 +950,6 @@ msgstr ""
 #~ "partners o asesores,\n"
 #~ "basándose en geo-localización.\n"
 #~ "    "
+
+#~ msgid "E-mail composition wizard"
+#~ msgstr "Asistente de composición de e-mail"
index 8f07dfc..7dc72f8 100644 (file)
@@ -244,6 +244,7 @@ class crm_segmentation(osv.osv):
             @param uid: the current user’s ID for security checks,
             @param ids: List of crm segmentation’s IDs """
 
+        partner_obj = self.pool.get('res.partner')
         categs = self.read(cr,uid,ids,['categ_id','exclusif','partner_id', \
                             'sales_purchase_active', 'profiling_active'])
         for categ in categs:
@@ -280,8 +281,10 @@ class crm_segmentation(osv.osv):
                 for pid in to_remove_list:
                     partners.remove(pid)
 
-            for partner_id in partners:
-                cr.execute('insert into res_partner_res_partner_category_rel (category_id,partner_id) values (%s,%s)', (categ['categ_id'][0],partner_id))
+            for partner in partner_obj.browse(cr, uid, partners):
+                category_ids = [categ_id.id for categ_id in partner.category_id]
+                if categ['categ_id'][0] not in category_ids:
+                    cr.execute('insert into res_partner_res_partner_category_rel (category_id,partner_id) values (%s,%s)', (categ['categ_id'][0],partner.id))
 
             self.write(cr, uid, [id], {'state':'not running', 'partner_id':0})
         return True
index 6bcb223..aeb2e64 100644 (file)
@@ -823,6 +823,7 @@ class node_res_dir(node_class):
         uid = self.context.uid
         ctx = self.context.context.copy()
         ctx.update(self.dctx)
+        ctx.update(self.context.extra_ctx)
         where = []
         if self.domain:
             app = safe_eval(self.domain, ctx)
index dbda715..326c62f 100644 (file)
@@ -272,7 +272,7 @@ class abstracted_fs(object):
             if path.startswith('/'):
                 path = path[1:]
 
-        p_parts = path.split('/') # hard-code the unix sep here, by spec.
+        p_parts = path.split(os.sep)
 
         assert '..' not in p_parts
 
index ab55727..13e8e79 100644 (file)
@@ -43,7 +43,7 @@ class showdiff(osv.osv_memory):
 
         elif len(ids) == 1:
             old = history.browse(cr, uid, ids[0])
-            nids = history.search(cr, uid, [('document_id', '=', old.document_id.id)])
+            nids = history.search(cr, uid, [('page_id', '=', old.page_id.id)])
             nids.sort()
             diff = history.getDiff(cr, uid, ids[0], nids[-1])
         else:
index 3b604d6..cb13151 100644 (file)
@@ -51,9 +51,10 @@ class document_davdir(osv.osv):
         # that might be not worth preparing.
         nctx.extra_ctx['webdav_path'] = '/'+config.get_misc('webdav','vdir','webdav')
         usr_obj = self.pool.get('res.users')
-        res = usr_obj.read(cr, uid, uid, ['login'])
+        res = usr_obj.read(cr, uid, uid, ['login','lang'])
         if res:
             nctx.extra_ctx['username'] = res['login']
+            nctx.extra_ctx['lang'] = res['lang']
         # TODO group
         return
 
index e82871c..a9c6981 100644 (file)
@@ -15,7 +15,7 @@ openerp.edi.EdiView = openerp.web.Widget.extend({
         this._super();
         var self = this;
         var param = {"db": self.db, "token": self.token};
-        return self.rpc('/edi/get_edi_document', param).then(this.on_document_loaded, this.on_document_failed);
+        return self.rpc('/edi/get_edi_document', param).done(this.on_document_loaded).fail(this.on_document_failed);
     },
     on_document_loaded: function(docs){
         this.doc = docs[0];
@@ -108,7 +108,7 @@ openerp.edi.EdiView = openerp.web.Widget.extend({
 });
 
 openerp.edi.edi_view = function (db, token) {
-    openerp.session.session_bind().then(function () {
+    openerp.session.session_bind().done(function () {
         new openerp.edi.EdiView(null,db,token).appendTo($("body").addClass('openerp'));
     });
 }
@@ -149,11 +149,11 @@ openerp.edi.EdiImport = openerp.web.Widget.extend({
     },
 
     do_import: function() {
-        this.rpc('/edi/import_edi_url', {url: this.url}).then(this.on_imported, this.on_imported_error);
+        this.rpc('/edi/import_edi_url', {url: this.url}).done(this.on_imported).fail(this.on_imported_error);
     },
     on_imported: function(response) {
         if ('action' in response) {
-            this.rpc("/web/session/save_session_action", {the_action: response.action}).then(function(key) {
+            this.rpc("/web/session/save_session_action", {the_action: response.action}).done(function(key) {
                 window.location = "/#sa="+encodeURIComponent(key);
             });
         }
@@ -188,7 +188,7 @@ openerp.edi.EdiImport = openerp.web.Widget.extend({
 });
 
 openerp.edi.edi_import = function (url) {
-    openerp.session.session_bind().then(function () {
+    openerp.session.session_bind().done(function () {
         new openerp.edi.EdiImport(null,url).appendTo($("body").addClass('openerp'));
     });
 }
index 73337d9..fbef2b0 100644 (file)
@@ -118,7 +118,8 @@ class email_template(osv.osv):
                                               "of the message"),
         'subject': fields.char('Subject', translate=True, help="Subject (placeholders may be used here)",),
         'email_from': fields.char('From', help="Sender address (placeholders may be used here)"),
-        'email_to': fields.char('To', help="Comma-separated recipient addresses (placeholders may be used here)"),
+        'email_to': fields.char('To (Emails)', help="Comma-separated recipient addresses (placeholders may be used here)"),
+        'email_recipients': fields.char('To (Partners)', help="Comma-separated ids of recipient partners (placeholders may be used here)"),
         'email_cc': fields.char('Cc', help="Carbon copy recipients (placeholders may be used here)"),
         'reply_to': fields.char('Reply-To', help="Preferred response address (placeholders may be used here)"),
         'mail_server_id': fields.many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False,
@@ -286,7 +287,7 @@ class email_template(osv.osv):
         template = self.get_email_template(cr, uid, template_id, res_id, context)
         values = {}
         for field in ['subject', 'body_html', 'email_from',
-                      'email_to', 'email_cc', 'reply_to']:
+                      'email_to', 'email_recipients', 'email_cc', 'reply_to']:
             values[field] = self.render_template(cr, uid, getattr(template, field),
                                                  template.model, res_id, context=context) \
                                                  or False
index 448655a..4cddb31 100644 (file)
@@ -29,7 +29,8 @@
                                 <group>
                                     <group string="Addressing">
                                       <field name="email_from" required="1"/>
-                                      <field name="email_to" required="1"/>
+                                      <field name="email_to"/>
+                                      <field name="email_recipients"/>
                                       <field name="email_cc"/>
                                       <field name="reply_to"/>
                                       <field name="user_signature"/>
@@ -77,6 +78,7 @@
                     <field name="subject"/>
                     <field name="email_from"/>
                     <field name="email_to"/>
+                    <field name="email_recipients"/>
                     <field name="report_name"/>
                 </tree>
             </field>
index 7af05f3..9a7b71a 100644 (file)
@@ -63,11 +63,16 @@ class test_message_compose(test_mail.TestMailMockups):
         # Create template on mail.group, with attachments
         group_model_id = self.registry('ir.model').search(cr, uid, [('model', '=', 'mail.group')])[0]
         email_template = self.registry('email.template')
-        email_template_id = email_template.create(cr, uid, {'model_id': group_model_id,
-            'name': 'Pigs Template', 'subject': '${object.name}',
-            'body_html': '${object.description}', 'user_signature': True,
+        email_template_id = email_template.create(cr, uid, {
+            'model_id': group_model_id,
+            'name': 'Pigs Template',
+            'subject': '${object.name}',
+            'body_html': '${object.description}',
+            'user_signature': True,
             'attachment_ids': [(0, 0, _attachments[0]), (0, 0, _attachments[1])],
-            'email_to': 'b@b.b c@c.c', 'email_cc': 'd@d.d'})
+            'email_to': 'b@b.b c@c.c',
+            'email_cc': 'd@d.d'
+            })
 
         # ----------------------------------------
         # CASE1: comment and save as template
@@ -76,9 +81,9 @@ class test_message_compose(test_mail.TestMailMockups):
         # 1. Comment on pigs
         compose_id = mail_compose.create(cr, uid,
             {'subject': 'Forget me subject', 'body': '<p>Dummy body</p>'},
-            {'default_composition_mode': 'comment', 'default_model': 'mail.group',
+            {'default_composition_mode': 'comment',
+                'default_model': 'mail.group',
                 'default_res_id': self.group_pigs_id,
-                'default_template_id': email_template_id,
                 'active_ids': [self.group_pigs_id, self.group_bird_id]})
         compose = mail_compose.browse(cr, uid, compose_id)
 
@@ -97,8 +102,10 @@ class test_message_compose(test_mail.TestMailMockups):
         # 1. Comment on pigs
         compose_id = mail_compose.create(cr, uid,
             {'subject': 'Forget me subject', 'body': 'Dummy body'},
-            {'default_composition_mode': 'comment', 'default_model': 'mail.group',
+            {'default_composition_mode': 'comment',
+                'default_model': 'mail.group',
                 'default_res_id': self.group_pigs_id,
+                'default_use_template': False,
                 'default_template_id': email_template_id,
                 'active_ids': [self.group_pigs_id, self.group_bird_id]})
         compose = mail_compose.browse(cr, uid, compose_id)
@@ -135,8 +142,10 @@ class test_message_compose(test_mail.TestMailMockups):
         # 1. Mass_mail on pigs and bird, with a default_partner_ids set to check he is correctly added
         compose_id = mail_compose.create(cr, uid,
             {'subject': 'Forget me subject', 'body': 'Dummy body'},
-            {'default_composition_mode': 'mass_mail', 'default_model': 'mail.group',
+            {'default_composition_mode': 'mass_mail',
+                'default_model': 'mail.group',
                 'default_res_id': self.group_pigs_id,
+                'default_use_template': False,
                 'default_template_id': email_template_id,
                 'default_partner_ids': [p_a_id],
                 'active_ids': [self.group_pigs_id, self.group_bird_id]})
@@ -170,3 +179,26 @@ class test_message_compose(test_mail.TestMailMockups):
         partner_ids = self.res_partner.search(cr, uid, [('email', 'in', ['b@b.b', 'c@c.c', 'd@d.d'])])
         self.assertEqual(set(message_pigs_pids), set(partner_ids), 'mail.message on pigs incorrect number of notified_partner_ids')
         self.assertEqual(set(message_bird_pids), set(partner_ids), 'mail.message on bird notified_partner_ids incorrect')
+
+        # ----------------------------------------
+        # CASE4: test newly introduced email_recipients field
+        # ----------------------------------------
+
+        # get already-created partners back
+        p_b_id = self.res_partner.search(cr, uid, [('email', '=', 'b@b.b')])[0]
+        p_c_id = self.res_partner.search(cr, uid, [('email', '=', 'c@c.c')])[0]
+        p_d_id = self.res_partner.search(cr, uid, [('email', '=', 'd@d.d')])[0]
+        # modify template: use email_recipients, use template and email address in email_to to test all features together
+        user_model_id = self.registry('ir.model').search(cr, uid, [('model', '=', 'res.users')])[0]
+        email_template.write(cr, uid, [email_template_id], {
+            'model_id': user_model_id,
+            'body_html': '${object.login}',
+            'email_to': '${object.email} c@c',
+            'email_recipients': '%i,%i' % (p_b_id, p_c_id),
+            'email_cc': 'd@d',
+            })
+        # patner by email + partner by id (no double)
+        send_to = [p_a_id, p_b_id, p_c_id, p_d_id]
+        # Generate messsage with default email and partner on template
+        mail_value = mail_compose.generate_email_for_composer(cr, uid, email_template_id, uid)
+        self.assertEqual(set(mail_value['partner_ids']), set(send_to), 'mail.message partner_ids list created by template is incorrect')
index f91535a..8dd4bc9 100644 (file)
@@ -17,6 +17,7 @@
                     <group>
                       <field name="email_from" readonly="1"/>
                       <field name="email_to" readonly="1"/>
+                      <field name="email_recipients" readonly="1"/>
                       <field name="email_cc" readonly="1" attrs="{'invisible':[('email_cc','=',False)]}"/>
                       <field name="reply_to" readonly="1" attrs="{'invisible':[('reply_to','=',False)]}"/>
                       <field name="subject" readonly="1"/>
index fe6f85d..129c327 100644 (file)
@@ -52,8 +52,9 @@ class mail_compose_message(osv.TransientModel):
             context = {}
         result = super(mail_compose_message, self).default_get(cr, uid, fields, context=context)
         result['template_id'] = context.get('default_template_id', context.get('mail.compose.template_id', False))
+
         # pre-render the template if any
-        if result.get('use_template'):
+        if result.get('use_template') and result.get('template_id'):
             onchange_res = self.onchange_use_template(cr, uid, [], result.get('use_template'), result.get('template_id'),
                 result.get('composition_mode'), result.get('model'), result.get('res_id'), context=context)
             result.update(onchange_res['value'])
@@ -65,6 +66,10 @@ class mail_compose_message(osv.TransientModel):
         'template_id': fields.selection(_get_templates, 'Template', size=-1),
     }
 
+    _defaults = {
+        'use_template': True,
+    }
+
     def onchange_template_id(self, cr, uid, ids, use_template, template_id, composition_mode, model, res_id, context=None):
         """ - use_template not set: return default_get
             - use_template set in mass_mailing: we cannot render, so return the template values
@@ -148,15 +153,23 @@ class mail_compose_message(osv.TransientModel):
             mail.compose.message, transform email_cc and email_to into partner_ids """
         template_values = self.pool.get('email.template').generate_email(cr, uid, template_id, res_id, context=context)
         # filter template values
-        fields = ['body', 'body_html', 'subject', 'email_to', 'email_cc', 'attachments']
+        fields = ['body', 'body_html', 'subject', 'email_to', 'email_recipients', 'email_cc', 'attachments']
         values = dict((field, template_values[field]) for field in fields if template_values.get(field))
         values['body'] = values.pop('body_html', '')
         # transform email_to, email_cc into partner_ids
         values['partner_ids'] = []
+
         mails = tools.email_split(values.pop('email_to', '') + ' ' + values.pop('email_cc', ''))
         for mail in mails:
             partner_id = self.pool.get('res.partner').find_or_create(cr, uid, mail, context=context)
             values['partner_ids'].append(partner_id)
+        email_recipients = values.pop('email_recipients', '')
+        if email_recipients:
+            for partner_id in email_recipients.split(','):
+                values['partner_ids'].append(int(partner_id))
+
+        values['partner_ids'] = list(set(values['partner_ids']))
+
         return values
 
     def render_message(self, cr, uid, wizard, res_id, context=None):
index 05d0e8d..0a53029 100644 (file)
@@ -8,23 +8,23 @@
             <field name="inherit_id" ref="mail.email_compose_message_wizard_form"/>
             <field name="arch" type="xml">
                 <data>
-                    <xpath expr="//form/group" position="after">
-                        <group attrs="{'invisible':[('use_template','=',False)]}">
-                            <field name="use_template" invisible="1"
-                                on_change="onchange_use_template(use_template, template_id, composition_mode, model, res_id, context)"/>
-                            <field name="template_id"
-                                on_change="onchange_template_id(use_template, template_id, composition_mode, model, res_id, context)"/>
-                        </group>
+                    <xpath expr="//field[@name='content_subtype']" position="after">
+                        <field name="use_template" invisible="1"
+                            on_change="onchange_use_template(use_template, template_id, composition_mode, model, res_id, context)"/>
                     </xpath>
-                    <xpath expr="//form/footer/button" position="after">
-                        <button icon="/email_template/static/src/img/email_template.png"
-                            type="object" name="toggle_template" string=""
-                            help="Use a message template"
-                            attrs="{'invisible':[('content_subtype','!=','html')]}"/>
-                        <button icon="/email_template/static/src/img/email_template_save.png"
-                            type="object" name="save_as_template" string=""
-                            help="Save as a new template"
-                            attrs="{'invisible':[('content_subtype','!=','html')]}"/>
+                    <xpath expr="//footer" position="inside">
+                        <group class="oe_right" col="1">
+                            <div>Use template
+                                <field name="template_id" attrs="{'invisible':[('use_template','=',False)]}"
+                                    nolabel="1"
+                                    on_change="onchange_template_id(use_template, template_id, composition_mode, model, res_id, context)"/>
+                            </div>
+                            or
+                            <button icon="/email_template/static/src/img/email_template_save.png"
+                                type="object" name="save_as_template" string="Save as new template" class="oe_link"
+                                help="Save as a new template"
+                                attrs="{'invisible':[('content_subtype','!=','html')]}"/>
+                        </group>
                     </xpath>
                 </data>
             </field>
index 2895855..85e3020 100644 (file)
             <field name="model">event.registration</field>
             <field name="arch" type="xml">
                 <search string="Event Registration">
-                    <field name="name" string="Participant" filter_domain="['|','|','|',('name','ilike',self),('partner_id','ilike',self),('email','ilike',self),('origin','ilike',self)]"/>
+                    <field name="name" string="Participant" filter_domain="['|','|',('name','ilike',self),('email','ilike',self),('origin','ilike',self)]"/>
                     <filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
                     <separator/>
                     <filter icon="terp-check" string="New" name="draft" domain="[('state','=','draft')]" help="Registrations in unconfirmed state"/>
                     <filter icon="terp-personal" string="My Registrations" help="My Registrations" domain="[('user_id','=',uid)]"/>
                     <field name="event_id"/>
                     <field name="user_id"/>
+                    <field name="partner_id"/>
                     <group expand="0" string="Group By...">
                         <filter string="Responsible"  icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
                         <filter string="Partner" icon="terp-partner" domain="[]" context="{'group_by':'partner_id'}"/>
index 2dd40a7..f4d7fca 100644 (file)
@@ -39,7 +39,7 @@ class sale_order_line(osv.osv):
     _columns = {
         'event_id': fields.many2one('event.event', 'Event', help="Choose an event and it will automatically create a registration for this event."),
         #those 2 fields are used for dynamic domains and filled by onchange
-        'event_type_id': fields.related('event_type_id', type='many2one', relation="event.type", string="Event Type"),
+        'event_type_id': fields.related('product_id','event_type_id', type='many2one', relation="event.type", string="Event Type"),
         'event_ok': fields.related('product_id', 'event_ok', string='event_ok', type='boolean'),
     }
 
index 0cc93b2..45e1d09 100644 (file)
@@ -16,9 +16,9 @@ var _t = instance.web._t,
             var view = self.getParent();
             var ids = ( view.fields_view.type != "form" )? view.groups.get_selection().ids : [ view.datarecord.id ];
             if( !_.isEmpty(ids) ){
-                view.sidebar_context().then(function (context) {
+                view.sidebar_context().done(function (context) {
                     var ds = new instance.web.DataSet(this, 'ir.attachment', context);
-                    ds.call('google_doc_get', [view.dataset.model, ids, context]).then(function(r) {
+                    ds.call('google_doc_get', [view.dataset.model, ids, context]).done(function(r) {
                         if (r == 'False') {
                             var params = {
                                 error: response,
index c4a622d..8918c50 100644 (file)
         </record>
 
         <record id="open_view_categ_form" model="ir.actions.act_window">
-            <field name="name">Categories of Employee</field>
+            <field name="name">Categories of Employees</field>
             <field name="res_model">hr.employee.category</field>
             <field name="view_type">form</field>
             <field name="view_mode">tree,form</field>
                         <group>
                             <group>
                                 <field name="no_of_employee" groups="base.group_user"/>
-                                <field name="expected_employees" groups="base.group_user"/>
                                 <field name="no_of_recruitment" on_change="on_change_expected_employee(no_of_recruitment,no_of_employee)"/>
+                                <field name="expected_employees" groups="base.group_user"/>
                             </group>
                             <group>
                                 <field name="company_id" widget="selection" groups="base.group_multi_company"/>
index 32e81b6..bfbbb7f 100644 (file)
@@ -8,15 +8,14 @@ msgstr ""
 "Project-Id-Version: openobject-addons\n"
 "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
 "POT-Creation-Date: 2012-02-08 01:37+0100\n"
-"PO-Revision-Date: 2012-01-03 02:55+0000\n"
-"Last-Translator: Christopher Ormaza - (Ecuadorenlinea.net) "
-"<chris.ormaza@gmail.com>\n"
+"PO-Revision-Date: 2012-11-10 17:20+0000\n"
+"Last-Translator: Cristian Salamea (Gnuthink) <ovnicraft@gmail.com>\n"
 "Language-Team: Spanish (Ecuador) <es_EC@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-30 05:17+0000\n"
-"X-Generator: Launchpad (build 16206)\n"
+"X-Launchpad-Export-Date: 2012-11-11 04:57+0000\n"
+"X-Generator: Launchpad (build 16251)\n"
 
 #. module: hr
 #: model:process.node,name:hr.process_node_openerpuser0
@@ -199,6 +198,8 @@ msgstr "Mujer"
 msgid ""
 "Expected number of employees for this job position after new recruitment."
 msgstr ""
+"Número de Empleados para este puesto de trabajo después de las nuevas "
+"contrataciones"
 
 #. module: hr
 #: model:ir.ui.menu,name:hr.menu_open_view_attendance_reason_new_config
@@ -291,7 +292,7 @@ msgstr "Categorías"
 #. module: hr
 #: field:hr.job,expected_employees:0
 msgid "Total Employees"
-msgstr ""
+msgstr "Total de Empleados"
 
 #. module: hr
 #: selection:hr.employee,marital:0
@@ -435,7 +436,7 @@ msgstr "Estado"
 #: model:ir.actions.act_window,name:hr.open_view_categ_tree
 #: model:ir.ui.menu,name:hr.menu_view_employee_category_tree
 msgid "Categories Structure"
-msgstr ""
+msgstr "Estructura de categorías"
 
 #. module: hr
 #: field:hr.employee,partner_id:0
@@ -465,7 +466,7 @@ msgstr "¡Error! No se puede crear una jerarquía recursiva de empleados."
 #. module: hr
 #: model:ir.actions.act_window,name:hr.action2
 msgid "Subordinate Hierarchy"
-msgstr ""
+msgstr "Jerarquía subirdinada"
 
 #. module: hr
 #: model:ir.actions.act_window,help:hr.view_department_form_installer
@@ -715,12 +716,12 @@ msgstr "Subordinados"
 #. module: hr
 #: field:hr.job,no_of_employee:0
 msgid "Number of employees currently occupying this job position."
-msgstr ""
+msgstr "Número de empleados ocupando actualmente este puesto de trabajo"
 
 #. module: hr
 #: field:hr.job,no_of_recruitment:0
 msgid "Number of new employees you expect to recruit."
-msgstr ""
+msgstr "Número de empleados que espera contratar"
 
 #~ msgid "Working Time Categories"
 #~ msgstr "Categorías de Horarios de Trabajo"
index f104b9c..6d41869 100644 (file)
@@ -31,6 +31,7 @@ from report.interface import toxml
 from report import report_sxw
 from tools import ustr
 from tools.translate import _
+from tools import to_xml
 
 one_day = relativedelta(days=1)
 month2name = [0, 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
@@ -111,7 +112,7 @@ class report_custom(report_rml):
         <date>%s</date>
         <company>%s</company>
         </header>
-        ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,uid).company_id.name)
+        ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),to_xml(pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,uid).company_id.name))
 
         first_date = str(month)
         som = datetime.strptime(first_date, '%Y-%m-%d %H:%M:%S')
index d92e753..8451635 100644 (file)
@@ -57,7 +57,7 @@ openerp.hr_attendance = function (instance) {
             var employee = new instance.web.DataSetSearch(self, 'hr.employee', self.session.user_context, [
                 ['user_id', '=', self.session.uid]
             ]);
-            return employee.read_slice(['id', 'name', 'state', 'last_sign', 'attendance_access']).pipe(function (res) {
+            return employee.read_slice(['id', 'name', 'state', 'last_sign', 'attendance_access']).then(function (res) {
                 if (_.isEmpty(res) )
                     return;
                 if (res[0].attendance_access == false){
@@ -75,7 +75,7 @@ openerp.hr_attendance = function (instance) {
         do_update: function () {
             this._super();
             var self = this;
-            this.update_promise = this.update_promise.then(function () {
+            this.update_promise = this.update_promise.done(function () {
                 if (self.attendanceslider)
                     return;
                 self.attendanceslider = new instance.hr_attendance.AttendanceSlider(self);
index b13e90c..eece0c7 100644 (file)
@@ -291,6 +291,7 @@ survey_request()
 class hr_evaluation_interview(osv.osv):
     _name = 'hr.evaluation.interview'
     _inherits = {'survey.request': 'request_id'}
+    _inherit =  'mail.thread'
     _rec_name = 'request_id'
     _description = 'Appraisal Interview'
     _columns = {
index 697ed6b..e097884 100644 (file)
                 Each employee may be assigned an Appraisal Plan. Such a plan
                 defines the frequency and the way you manage your periodic
                 personnel evaluation. You will be able to define steps and
-                attach interviews to each step. OpenERP manages all kind of
+                attach interviews to each step. OpenERP manages all kinds of
                 evaluations: bottom-up, top-down, self-evaluation and final
                 evaluation by the manager.
               </p>
index c9961fa..ccb8af6 100644 (file)
@@ -64,22 +64,22 @@ class hr_expense_expense(osv.osv):
     _description = "Expense"
     _order = "id desc"
     _columns = {
-        'name': fields.char('Description', size=128),
+        'name': fields.char('Description', size=128, required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
         'id': fields.integer('Sheet ID', readonly=True),
-        'date': fields.date('Date', select=True),
+        'date': fields.date('Date', select=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
         'journal_id': fields.many2one('account.journal', 'Force Journal', help = "The journal used when the expense is done."),
-        'employee_id': fields.many2one('hr.employee', "Employee", required=True),
+        'employee_id': fields.many2one('hr.employee', "Employee", required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
         'user_id': fields.many2one('res.users', 'User', required=True),
         'date_confirm': fields.date('Confirmation Date', select=True, help="Date of the confirmation of the sheet expense. It's filled when the button Confirm is pressed."),
         'date_valid': fields.date('Validation Date', select=True, help="Date of the acceptation of the sheet expense. It's filled when the button Accept is pressed."),
-        'user_valid': fields.many2one('res.users', 'Validation By'),
+        'user_valid': fields.many2one('res.users', 'Validation By', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
         'account_move_id': fields.many2one('account.move', 'Ledger Posting'),
         'line_ids': fields.one2many('hr.expense.line', 'expense_id', 'Expense Lines', readonly=True, states={'draft':[('readonly',False)]} ),
         'note': fields.text('Note'),
         'amount': fields.function(_amount, string='Total Amount', digits_compute= dp.get_precision('Account')),
         'voucher_id': fields.many2one('account.voucher', "Employee's Receipt"),
-        'currency_id': fields.many2one('res.currency', 'Currency', required=True),
-        'department_id':fields.many2one('hr.department','Department'),
+        'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
+        'department_id':fields.many2one('hr.department','Department', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
         'company_id': fields.many2one('res.company', 'Company', required=True),
         'state': fields.selection([
             ('draft', 'New'),
@@ -100,6 +100,12 @@ class hr_expense_expense(osv.osv):
         'currency_id': _get_currency,
     }
 
+    def unlink(self, cr, uid, ids, context=None):
+        for rec in self.browse(cr, uid, ids, context=context):
+            if rec.state != 'draft':
+                raise osv.except_osv(_('Warning!'),_('You can only delete draft expenses!'))
+        return super(hr_expense_expense, self).unlink(cr, uid, ids, context)
+
     def onchange_currency_id(self, cr, uid, ids, currency_id=False, company_id=False, context=None):
         res =  {'value': {'journal_id': False}}
         journal_ids = self.pool.get('account.journal').search(cr, uid, [('type','=','purchase'), ('currency','=',currency_id), ('company_id', '=', company_id)], context=context)
@@ -236,16 +242,6 @@ class product_product(osv.osv):
         'hr_expense_ok': fields.boolean('Can be Expensed', help="Specify if the product can be selected in an HR expense line."),
     }
 
-    def on_change_hr_expense_ok(self, cr, uid, id, hr_expense_ok):
-
-        if not hr_expense_ok:
-            return {}
-        data_obj = self.pool.get('ir.model.data')
-        cat_id = data_obj._get_id(cr, uid, 'hr_expense', 'cat_expense')
-        categ_id = data_obj.browse(cr, uid, cat_id).res_id
-        res = {'value' : {'type':'service','sale_ok' :False,'categ_id':categ_id }}
-        return res
-
 product_product()
 
 class hr_expense_line(osv.osv):
index 74b7700..d4b7cba 100644 (file)
             <field name="inherit_id" ref="product.product_normal_form_view"/>
             <field name="arch" type="xml">
                 <div name="options" position="inside">
-                    <field name="hr_expense_ok" on_change="on_change_hr_expense_ok(hr_expense_ok)"/>
+                    <field name="hr_expense_ok"/>
                     <label for="hr_expense_ok"/>
                 </div>
             </field>
index f315f3d..92b1d57 100644 (file)
@@ -90,6 +90,18 @@ class hr_holidays_status(osv.osv):
         'color_name': 'red',
         'active': True,
     }
+
+    def name_get(self, cr, uid, ids, context=None):
+        if not ids:
+            return []
+        res = []
+        for record in self.browse(cr, uid, ids, context=context):
+            name = record.name
+            if not record.limit:
+                name = name + ('  (%d/%d)' % (record.leaves_taken or 0.0, record.max_leaves or 0.0))
+            res.append((record.id, name))
+        return res
+
 hr_holidays_status()
 
 class hr_holidays(osv.osv):
@@ -113,6 +125,13 @@ class hr_holidays(osv.osv):
                 result[hol.id] = hol.number_of_days_temp
         return result
 
+    def _check_date(self, cr, uid, ids):
+        for holiday in self.browse(cr, uid, ids):
+            holiday_ids = self.search(cr, uid, [('date_from', '<=', holiday.date_to), ('date_to', '>=', holiday.date_from), ('employee_id', '=', holiday.employee_id.id), ('id', '<>', holiday.id)])
+            if holiday_ids:
+                return False
+        return True
+
     _columns = {
         'name': fields.char('Description', size=64),
         'state': fields.selection([('draft', 'To Submit'), ('cancel', 'Cancelled'),('confirm', 'To Approve'), ('refuse', 'Refused'), ('validate1', 'Second Approval'), ('validate', 'Approved')],
@@ -146,6 +165,10 @@ class hr_holidays(osv.osv):
         'user_id': lambda obj, cr, uid, context: uid,
         'holiday_type': 'employee'
     }
+    _constraints = [
+        (_check_date, 'You can not have 2 leaves that overlaps on same day!', ['date_from','date_to']),
+    ] 
+    
     _sql_constraints = [
         ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL))", "The employee or employee category of this request is missing."),
         ('date_check2', "CHECK ( (type='add') OR (date_from <= date_to))", "The start date must be anterior to the end date."),
@@ -252,6 +275,13 @@ class hr_holidays(osv.osv):
             result['value']['number_of_days_temp'] = 0
 
         return result
+    
+    def write(self, cr, uid, ids, vals, context=None):
+        check_fnct = self.pool.get('hr.holidays.status').check_access_rights
+        for  holiday in self.browse(cr, uid, ids, context=context):
+            if holiday.state in ('validate','validate1') and not check_fnct(cr, uid, 'write', raise_exception=False):
+                raise osv.except_osv(_('Warning!'),_('You cannot modify a leave request that has been approved. Contact a human resource manager.'))
+        return super(hr_holidays, self).write(cr, uid, ids, vals, context=context)
 
     def set_to_draft(self, cr, uid, ids, context=None):
         self.write(cr, uid, ids, {
index 4c9d186..accf8c2 100644 (file)
@@ -34,8 +34,8 @@
     <record model="hr.holidays" id="hr_holidays_employee1_vc">
       <field name="name">Summer Vacation</field>
       <field name="holiday_status_id" ref="holiday_status_unpaid"/>
-      <field eval="time.strftime('%Y-%m-20')" name="date_from"/>
-      <field eval="time.strftime('%Y-%m-22')" name="date_to"/>
+      <field eval="time.strftime('%Y-%m-23')" name="date_from"/>
+      <field eval="time.strftime('%Y-%m-25')" name="date_to"/>
       <field name="type">add</field>
       <field name="state">draft</field>
       <field name="number_of_days_temp">7</field>
@@ -45,8 +45,8 @@
     <record model="hr.holidays" id="hr_holidays_employee1_int_tour">
       <field name="name">International Tour</field>
       <field name="holiday_status_id" ref="holiday_status_comp"/>
-      <field eval="time.strftime('%Y-%m-20')" name="date_from"/>
-      <field eval="time.strftime('%Y-%m-22')" name="date_to"/>
+      <field eval="time.strftime('%Y-%m-26')" name="date_from"/>
+      <field eval="time.strftime('%Y-%m-28')" name="date_to"/>
       <field name="type">add</field>
       <field name="number_of_days_temp">7</field>
       <field name="employee_id" ref="hr.employee_fp"/>
index 8a78207..cadd3a2 100644 (file)
@@ -31,6 +31,7 @@ import time
 from report import report_sxw
 from tools import ustr
 from tools.translate import _
+from tools import to_xml
 
 def lengthmonth(year, month):
     if month == 2 and ((year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))):
@@ -240,7 +241,7 @@ class report_custom(report_rml):
         <date>%s</date>
         <company>%s</company>
         </header>
-        '''  % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,uid).company_id.name)
+        ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),to_xml(pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,uid).company_id.name))
 
         # Computing the xml
         xml='''<?xml version="1.0" encoding="UTF-8" ?>
index 53c84c6..c0e4bc3 100644 (file)
@@ -326,6 +326,7 @@ class hr_payslip(osv.osv):
         return self.write(cr, uid, ids, {'paid': True, 'state': 'done'}, context=context)
 
     def hr_verify_sheet(self, cr, uid, ids, context=None):
+        self.compute_sheet(cr, uid, ids, context)
         return self.write(cr, uid, ids, {'state': 'verify'}, context=context)
 
     def refund_sheet(self, cr, uid, ids, context=None):
@@ -358,6 +359,12 @@ class hr_payslip(osv.osv):
     def check_done(self, cr, uid, ids, context=None):
         return True
 
+    def unlink(self, cr, uid, ids, context=None):
+        for payslip in self.browse(cr, uid, ids, context=context):
+            if payslip.state not in  ['draft','cancel']:
+                raise osv.except_osv(_('Warning!'),_('You cannot delete a payslip which is not draft or cancelled!'))
+        return super(hr_payslip, self).unlink(cr, uid, ids, context)
+
     #TODO move this function into hr_contract module, on hr.employee object
     def get_contract(self, cr, uid, employee, date_from, date_to, context=None):
         """
index 8368148..a22809b 100644 (file)
@@ -75,7 +75,7 @@
         <record id="hr_salary_rule_meal_voucher" model="hr.salary.rule">
             <field name="amount_select">fix</field>
             <field eval="10" name="amount_fix"/>
-            <field name="quantity">worked_days.WORK100.number_of_days</field>
+            <field name="quantity">worked_days.WORK100 and worked_days.WORK100.number_of_days</field>
             <field name="code">MA</field>
             <field name="category_id" ref="hr_payroll.ALW"/>
             <field name="register_id" ref="hr_meal_voucher_register"/>
@@ -89,7 +89,7 @@
             <field name="category_id" ref="hr_payroll.ALW"/>
             <field name="name">Get 1% of sales</field>
             <field name="sequence" eval="17"/>
-            <field name="amount_python_compute">result = (inputs.SALEURO.amount + inputs.SALASIA.amount) * 0.01</field>
+            <field name="amount_python_compute">result = ((inputs.SALEURO and inputs.SALEURO.amount) + (inputs.SALASIA and inputs.SALASIA.amount)) * 0.01</field>
          </record>
 
         <!-- Rule Inputs -->
index 03561da..0dd5893 100644 (file)
         <field name="arch" type="xml">
           <form string="Jobs - Recruitment Form" version="7.0">
             <header>
-                <button name="case_close_with_emp" string="Hire" type="object"
+                <button name="case_close_with_emp" string="Hire &amp; Create Employee" type="object"
                         states="draft,open,pending,done" class="oe_highlight"/>
                 <button name="case_cancel" string="Refuse" type="object"
                         states="draft,open,pending" class="oe_highlight"/>
         <field name="model">hr.job</field>
         <field name="inherit_id" ref="hr.view_hr_job_form"/>
         <field name="arch" type="xml">
-            <field name="no_of_recruitment" version="7.0" position="after">
+            <field name="expected_employees" version="7.0" position="after">
                 <label for="survey_id" groups="base.group_user"/>
                 <div groups="base.group_user">
                     <field name="survey_id" class="oe_inline" domain="[('type','=','Human Resources')]"/>
index 9a0a6e4..d11326a 100644 (file)
@@ -16,7 +16,7 @@ openerp.hr_recruitment = function(openerp) {
 
             // Find their matching names
             var dataset = new openerp.web.DataSetSearch(self, 'hr.applicant_category', self.session.context, [['id', 'in', _.uniq(categ_ids)]]);
-            dataset.read_slice(['id', 'name']).then(function(result) {
+            dataset.read_slice(['id', 'name']).done(function(result) {
                 _.each(result, function(v, k) {
                     // Set the proper value in the DOM and display the element
                     self.$el.find('span[data-categ_id=' + v.id + ']').text(v.name);
index 7699f94..8d2a7d3 100644 (file)
@@ -28,6 +28,7 @@ import time
 import pooler
 from report import report_sxw
 from tools import ustr
+from tools import to_xml
 
 def lengthmonth(year, month):
     if month == 2 and ((year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))):
@@ -94,7 +95,7 @@ class report_custom(report_rml):
             <date>%s</date>
             <company>%s</company>
             </header>
-            ''' %  (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,user_id).company_id.name)
+            ''' % (str(rml_obj.formatLang(time.strftime("%Y-%m-%d"),date=True))+' ' + str(time.strftime("%H:%M")),to_xml(pooler.get_pool(cr.dbname).get('res.users').browse(cr,uid,user_id).company_id.name))
 
         account_xml = []
         for account, telems in accounts.iteritems():
index 75bb089..96ce23f 100644 (file)
@@ -27,8 +27,9 @@ class analytical_timesheet_employee(osv.osv_memory):
     _name = 'hr.analytical.timesheet.employee'
     _description = 'Print Employee Timesheet & Print My Timesheet'
     _columns = {
-        'month': fields.selection([(x, datetime.date(2000, x, 1).strftime('%B')) for x in range(1, 13)],
-                                  'Month', required=True),
+        'month': fields.selection([(1,'January'), (2,'February'), (3,'March'), (4,'April'),
+            (5,'May'), (6,'June'), (7,'July'), (8,'August'), (9,'September'),
+            (10,'October'), (11,'November'), (12,'December')], 'Month', required=True),
         'year': fields.integer('Year', required=True),
         'employee_id': fields.many2one('hr.employee', 'Employee', required=True)
 
index 865ccac..2a91bb6 100644 (file)
@@ -27,8 +27,9 @@ class analytical_timesheet_employees(osv.osv_memory):
     _name = 'hr.analytical.timesheet.users'
     _description = 'Print Employees Timesheet'
     _columns = {
-        'month': fields.selection([(x, datetime.date(2000, x, 1).strftime('%B')) for x in range(1, 13)],
-                                  'Month', required=True),
+        'month': fields.selection([(1,'January'), (2,'February'), (3,'March'), (4,'April'),
+            (5,'May'), (6,'June'), (7,'July'), (8,'August'), (9,'September'),
+            (10,'October'), (11,'November'), (12,'December')], 'Month', required=True),
         'year': fields.integer('Year', required=True),
         'employee_ids': fields.many2many('hr.employee', 'timesheet_employee_rel', 'timesheet_id', 'employee_id', 'employees', required=True)
                 }
index cc531fc..0ebe16a 100644 (file)
@@ -7,7 +7,7 @@
             <field name="factor">50.0</field>
         </record>
         <record id="timesheet_invoice_factor3" model="hr_timesheet_invoice.factor">
-            <field name="name">Gratis</field>
+            <field name="name">Free of charge</field>
             <field name="customer_name">Offered developments</field>
             <field name="factor">100.0</field>
         </record>
index e64c4d6..efff48d 100644 (file)
@@ -46,7 +46,7 @@ openerp.hr_timesheet_sheet = function(instance) {
             var commands = this.field_manager.get_field_value("timesheet_ids");
             this.res_o2m_drop.add(new instance.web.Model(this.view.model).call("resolve_2many_commands", ["timesheet_ids", commands, [], 
                     new instance.web.CompoundContext()]))
-                .then(function(result) {
+                .done(function(result) {
                 self.querying = true;
                 self.set({sheets: result});
                 self.querying = false;
@@ -57,7 +57,7 @@ openerp.hr_timesheet_sheet = function(instance) {
             if (self.querying)
                 return;
             self.updating = true;
-            self.field_manager.set_values({timesheet_ids: self.get("sheets")}).then(function() {
+            self.field_manager.set_values({timesheet_ids: self.get("sheets")}).done(function() {
                 self.updating = false;
             });
         },
@@ -85,7 +85,7 @@ openerp.hr_timesheet_sheet = function(instance) {
             var default_get;
             return this.render_drop.add(new instance.web.Model("hr.analytic.timesheet").call("default_get", [
                 ['account_id','general_account_id', 'journal_id','date','name','user_id','product_id','product_uom_id','to_invoice','amount','unit_amount'],
-                new instance.web.CompoundContext({'user_id': self.get('user_id')})]).pipe(function(result) {
+                new instance.web.CompoundContext({'user_id': self.get('user_id')})]).then(function(result) {
                 default_get = result;
                 // calculating dates
                 dates = [];
@@ -108,9 +108,9 @@ openerp.hr_timesheet_sheet = function(instance) {
                 var account_ids = _.map(_.keys(accounts), function(el) { return el === "false" ? false : Number(el) });
 
                 return new instance.web.Model("hr.analytic.timesheet").call("multi_on_change_account_id", [[], account_ids,
-                    new instance.web.CompoundContext({'user_id': self.get('user_id')})]).pipe(function(accounts_defaults) {
+                    new instance.web.CompoundContext({'user_id': self.get('user_id')})]).then(function(accounts_defaults) {
                     accounts = _(accounts).chain().map(function(lines, account_id) {
-                        account_defaults = _.extend({}, default_get, accounts_defaults[account_id]);
+                        account_defaults = _.extend({}, default_get, (accounts_defaults[account_id] || {}).value || {});
                         // group by days
                         account_id = account_id === "false" ? false :  Number(account_id);
                         var index = _.groupBy(lines, "date");
@@ -136,7 +136,7 @@ openerp.hr_timesheet_sheet = function(instance) {
 
                     // we need the name_get of the analytic accounts
                     return new instance.web.Model("account.analytic.account").call("name_get", [_.pluck(accounts, "account"),
-                        new instance.web.CompoundContext()]).pipe(function(result) {
+                        new instance.web.CompoundContext()]).then(function(result) {
                         account_names = {};
                         _.each(result, function(el) {
                             account_names[el[0]] = el[1];
@@ -146,7 +146,7 @@ openerp.hr_timesheet_sheet = function(instance) {
                         });
                     });;
                 });
-            })).pipe(function(result) {
+            })).then(function(result) {
                 // we put all the gathered data in self, then we render
                 self.dates = dates;
                 self.accounts = accounts;
@@ -222,7 +222,7 @@ openerp.hr_timesheet_sheet = function(instance) {
                     return;
                 }
                 var ops = self.generate_o2m_value();
-                new instance.web.Model("hr.analytic.timesheet").call("on_change_account_id", [[], id]).pipe(function(res) {
+                new instance.web.Model("hr.analytic.timesheet").call("on_change_account_id", [[], id]).then(function(res) {
                     var def = _.extend({}, self.default_get, res.value, {
                         name: self.description_line,
                         unit_amount: 0,
index d15b300..5394894 100644 (file)
@@ -65,6 +65,8 @@ class partner_vat(osv.osv_memory):
 
         partners = []
         partner_ids = obj_partner.search(cr, uid, [('vat_subjected', '!=', False), ('vat','ilike','BE%')], context=context)
+        if not partner_ids:
+             raise osv.except_osv(_('Error'),_('No belgian contact with a VAT number in your database.'))
         cr.execute("""SELECT sub1.partner_id, sub1.name, sub1.vat, sub1.turnover, sub2.vat_amount
                 FROM (SELECT l.partner_id, p.name, p.vat, SUM(CASE WHEN c.code ='49' THEN -l.tax_amount ELSE l.tax_amount END) as turnover
                       FROM account_move_line l
@@ -190,7 +192,7 @@ class partner_vat_list(osv.osv_memory):
         addr = obj_partner.address_get(cr, uid, [obj_cmpny.partner_id.id], ['invoice'])
         if addr.get('invoice',False):
             ads = obj_partner.browse(cr, uid, [addr['invoice']], context=context)[0]
-            phone = ads.phone.replace(' ','') or ''
+            phone = ads.phone and ads.phone.replace(' ','') or ''
             email = ads.email or ''
             name = ads.name or ''
             city = ads.city or ''
@@ -259,6 +261,8 @@ class partner_vat_list(osv.osv_memory):
 
         # Turnover and Farmer tags are not included
         client_datas = self._get_datas(cr, uid, ids, context=context)
+        if not client_datas:
+            raise osv.except_osv(_('Data Insufficient!'),_('No data available for the client.'))
         data_client_info = ''
         for amount_data in client_datas:
             data_client_info += """
@@ -309,6 +313,8 @@ class partner_vat_list(osv.osv_memory):
         datas['year'] = context['year']
         datas['limit_amount'] = context['limit_amount']
         datas['client_datas'] = self._get_datas(cr, uid, ids, context=context)
+        if not datas['client_datas']:
+            raise osv.except_osv(_('Error!'),_('No record to print.'))
         return {
             'type': 'ir.actions.report.xml',
             'report_name': 'partner.vat.listing.print',
index 79dcb1a..4e4c0e0 100644 (file)
@@ -52,21 +52,6 @@ class ResPartnerBank(osv.osv):
         'my_bank': fields.boolean('Use my account to print BVR ?', help="Check to print BVR invoices"),
     }
 
-    def name_get(self, cursor, uid, ids, context=None):
-        if not len(ids):
-            return []
-        bank_type_obj = self.pool.get('res.partner.bank.type')
-
-        type_ids = bank_type_obj.search(cursor, uid, [])
-        bank_type_names = {}
-        for bank_type in bank_type_obj.browse(cursor, uid, type_ids,
-                context=context):
-            bank_type_names[bank_type.code] = bank_type.name
-        res = []
-        for r in self.read(cursor, uid, ids, ['name','state'], context):
-            res.append((r['id'], r['name']+' : '+bank_type_names.get(r['state'], '')))
-        return res
-
     def _prepare_name(self, bank):
         "Hook to get bank number of bank account"
         res = u''
index fc5d460..36c5feb 100644 (file)
       <field name="tax_code_id" ref="tax_acq_196"/>
       <field name="tax_sign" eval="-1"/>
 
-      <field name="account_collected_id" ref="pcg_44566"/>
-      <field name="account_paid_id" ref="pcg_44566"/>
+      <field name="account_collected_id" ref="pcg_445662"/>
+      <field name="account_paid_id" ref="pcg_445662"/>
 
       <field name="ref_base_code_id" ref="tax_acq_196_ht"/>
       <field name="ref_base_sign" eval="1"/>
index 525dabf..268f4bd 100644 (file)
       <field name="name">Associés - Comptes courants</field>
       <field name="code">455</field>
       <field name="type">view</field>
-      <field name="user_type" ref="account.data_account_type_view"/>
+      <field name="user_type" ref="account.data_account_type_payable"/>
       <field name="note">Pour frais avancés personnellement</field>
       <field name="parent_id" ref="pcg_45"/>
       <field name="reconcile" eval="True"/>
     <record id="pcg_4551" model="account.account.template">
       <field name="name">Principal</field>
       <field name="code">4551</field>
-      <field name="type">receivable</field>
-      <field name="user_type" ref="account.data_account_type_receivable"/>
+      <field name="type">payable</field>
+      <field name="user_type" ref="account.data_account_type_payable"/>
       <field name="parent_id" ref="pcg_455"/>
       <field name="reconcile" eval="True"/>
     </record>
     <record id="pcg_4558" model="account.account.template">
       <field name="name">Intérêts courus</field>
       <field name="code">4558</field>
-      <field name="type">receivable</field>
-      <field name="user_type" ref="account.data_account_type_receivable"/>
+      <field name="type">payable</field>
+      <field name="user_type" ref="account.data_account_type_payable"/>
       <field name="parent_id" ref="pcg_455"/>
       <field name="reconcile" eval="True"/>
     </record>
index 2903960..d6d6667 100644 (file)
@@ -60,6 +60,3 @@ IVC21det40,template_ivacode_pagata_21det40,IVA a credito 21% detraibile 40%,temp
 IVC21Idet40,template_impcode_pagata_21det40,IVA a credito 21% detraibile 40% (imponibile),template_impcode_pagata
 IVC21det50,template_ivacode_pagata_21det50,IVA a credito 21% detraibile 50%,template_ivacode_pagata
 IVC21Idet50,template_impcode_pagata_21det50,IVA a credito 21% detraibile 50% (imponibile),template_impcode_pagata
-Rit,template_ra,Ritenute d'acconto,vat_code_chart_root
-RitD20,template_ritcode_20,Ritenute a debito 20%,template_ra
-RitD20I,template_ritimpcode_20,Ritenute a debito 20% (imponibile),template_ra
index 36c4f99..641f495 100644 (file)
@@ -62,4 +62,3 @@ id,description,chart_template_id:id,name,sequence,amount,parent_id:id,child_depe
 21I5,21I5,l10n_it_chart_template_generic,IVA al 21% detraibile al 50%,,0.21,,True,percent,,,purchase,template_impcode_pagata_21det50,,template_impcode_pagata_21det50,,,,False,-1,-1
 21I5b,21I5b,l10n_it_chart_template_generic,IVA al 21% detraibile al 50% (I),1,0.5,21I5,False,percent,,,purchase,,,,,,,False,,
 21I5a,21I5a,l10n_it_chart_template_generic,IVA al 21% detraibile al 50% (D),2,0,21I5,False,balance,1601,1601,purchase,,template_ivacode_pagata_21det50,,template_ivacode_pagata_21det50,,,False,,
-rit-20,rit-20,l10n_it_chart_template_generic,Ritenuta d'acconto al 20% (debito),,-0.2,,False,percent,2602,2602,purchase,template_ritimpcode_20,template_ritcode_20,template_ritimpcode_20,template_ritcode_20,-1,1,False,1,-1
index 96100d9..f736c6f 100644 (file)
                     <field name="reconcile" eval="True"/>
                     <field ref="a_15" name="parent_id"/>
                 </record>
-                <record id="vat_payable6" model="account.account.template">
+                <record id="vat_payable_low" model="account.account.template">
                     <field name="name">Btw af te dragen laag</field>
                     <field name="code">1601</field>
                     <field name="type">other</field>
                     <field name="reconcile" eval="False"/>
                     <field ref="a_15" name="parent_id"/>
                 </record>
-                <record id="vat_payable19" model="account.account.template">
+                <record id="vat_payable_high" model="account.account.template">
                     <field name="name">Btw af te dragen hoog</field>
                     <field name="code">1602</field>
                     <field name="type">other</field>
                     <field name="reconcile" eval="False"/>
                     <field ref="a_15" name="parent_id"/>
                 </record>
-                <record id="vat_refund6" model="account.account.template">
+                <record id="vat_refund_low" model="account.account.template">
                     <field name="name">Btw te vorderen laag</field>
                     <field name="code">1611</field>
                     <field name="type">other</field>
                     <field name="reconcile" eval="False"/>
                     <field ref="a_15" name="parent_id"/>
                 </record>
-                <record id="vat_refund19" model="account.account.template">
+                <record id="vat_refund_high" model="account.account.template">
                     <field name="name">Btw te vorderen hoog</field>
                     <field name="code">1612</field>
                     <field name="type">other</field>
         <record id="btw_code_1a" model="account.tax.code.template">
             <field name="code">1a</field>
             <field name="parent_id" ref="btw_code_binnenland"/>
-            <field name="name">Leveringen/diensten belast met 19% (BTW)</field>
+            <field name="name">Leveringen/diensten belast met 21% (BTW)</field>
         </record>
         <record id="btw_code_1b" model="account.tax.code.template">
             <field name="code">1b</field>
         <record id="omz_code_1a" model="account.tax.code.template">
             <field name="code">1a</field>
             <field name="parent_id" ref="omz_code_binnenland"/>
-            <field name="name">Leveringen/diensten belast met 19% (omzet)</field>
+            <field name="name">Leveringen/diensten belast met 21% (omzet)</field>
         </record>
         <record id="omz_code_1b" model="account.tax.code.template">
             <field name="code">1b</field>
@@ -4149,22 +4149,22 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
             <field name="description">6% BTW</field>
             <field eval="0.06" name="amount"/>
             <field name="type">percent</field>
-            <field name="account_collected_id" ref="vat_payable6"/>
-            <field name="account_paid_id" ref="vat_refund6"/>
+            <field name="account_collected_id" ref="vat_payable_low"/>
+            <field name="account_paid_id" ref="vat_refund_low"/>
             <field name="base_code_id" ref="omz_code_1b"/>
             <field name="tax_code_id" ref="btw_code_1b"/>
             <field name="ref_base_code_id" ref="omz_code_1b"/>
             <field name="ref_tax_code_id" ref="btw_code_1b"/>
             <field name="type_tax_use">sale</field>
         </record>
-        <record id="btw_19" model="account.tax.template">
+        <record id="btw_21" model="account.tax.template">
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">Verkopen/omzet hoog</field>
-            <field name="description">19% BTW</field>
-            <field eval="0.19" name="amount"/>
+            <field name="description">21% BTW</field>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
-            <field name="account_collected_id" ref="vat_payable19"/>
-            <field name="account_paid_id" ref="vat_refund19"/>
+            <field name="account_collected_id" ref="vat_payable_high"/>
+            <field name="account_paid_id" ref="vat_refund_high"/>
             <field name="base_code_id" ref="omz_code_1a"/>
             <field name="tax_code_id" ref="btw_code_1a"/>
             <field name="ref_base_code_id" ref="omz_code_1a"/>
@@ -4176,10 +4176,10 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">Verkopen/omzet overig</field>
             <field name="description">variabel BTW</field>
-            <field eval="0.19" name="amount"/>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
-            <field name="account_collected_id" ref="vat_payable19"/>
-            <field name="account_paid_id" ref="vat_refund19"/>
+            <field name="account_collected_id" ref="vat_payable_high"/>
+            <field name="account_paid_id" ref="vat_refund_high"/>
             <field name="base_code_id" ref="omz_code_1a"/>
             <field name="tax_code_id" ref="btw_code_1a"/>
             <field name="ref_base_code_id" ref="omz_code_1c"/>
@@ -4194,20 +4194,20 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
             <field name="description">6% BTW</field>
             <field eval="0.06" name="amount"/>
             <field name="type">percent</field>
-            <field name="account_collected_id" ref="vat_refund6"/>
-            <field name="account_paid_id" ref="vat_payable6"/>
+            <field name="account_collected_id" ref="vat_refund_low"/>
+            <field name="account_paid_id" ref="vat_payable_low"/>
             <field name="tax_code_id" ref="btw_code_5b"/>
             <field name="ref_tax_code_id" ref="btw_code_5b"/>
             <field name="type_tax_use">purchase</field>
         </record>
-        <record id="btw_19_buy" model="account.tax.template">
+        <record id="btw_21_buy" model="account.tax.template">
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">BTW te vorderen hoog (inkopen)</field>
-            <field name="description">19% BTW</field>
-            <field eval="0.19" name="amount"/>
+            <field name="description">21% BTW</field>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
-            <field name="account_collected_id" ref="vat_refund19"/>
-            <field name="account_paid_id" ref="vat_payable19"/>
+            <field name="account_collected_id" ref="vat_refund_high"/>
+            <field name="account_paid_id" ref="vat_payable_high"/>
             <field name="tax_code_id" ref="btw_code_5b"/>
             <field name="ref_tax_code_id" ref="btw_code_5b"/>
             <field name="type_tax_use">purchase</field>
@@ -4216,10 +4216,10 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">BTW te vorderen overig (inkopen)</field>
             <field name="description">variabel BTW</field>
-            <field eval="0.19" name="amount"/>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
-            <field name="account_collected_id" ref="vat_refund19"/>
-            <field name="account_paid_id" ref="vat_payable19"/>
+            <field name="account_collected_id" ref="vat_refund_high"/>
+            <field name="account_paid_id" ref="vat_payable_high"/>
             <field name="tax_code_id" ref="btw_code_5b"/>
             <field name="ref_tax_code_id" ref="btw_code_5b"/>
             <field name="type_tax_use">purchase</field>
@@ -4240,8 +4240,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
         <record id="btw_ink_0" model="account.tax.template">
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">BTW af te dragen verlegd (inkopen)</field>
-            <field name="description">19% BTW verlegd</field>
-            <field eval="0.19" name="amount"/>
+            <field name="description">21% BTW verlegd</field>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
             <field name="account_collected_id" ref="vat_payable_verlegd"/>
             <field name="account_paid_id" ref="vat_refund_verlegd"/>
@@ -4277,8 +4277,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
         <record id="btw_ink2_0" model="account.tax.template">
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">BTW te vorderen verlegd (inkopen)</field>
-            <field name="description">19% BTW verlegd</field>
-            <field eval="0.19" name="amount"/>
+            <field name="description">21% BTW verlegd</field>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
             <field name="account_collected_id" ref="vat_refund_verlegd"/>
             <field name="account_paid_id" ref="vat_payable_verlegd"/>
@@ -4330,8 +4330,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="-1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_I_6"/>
-                            <field name="account_collected_id" ref="vat_payable6"/>
-                            <field name="account_paid_id" ref="vat_payable6"/>
+                            <field name="account_collected_id" ref="vat_payable_low"/>
+                            <field name="account_paid_id" ref="vat_payable_low"/>
                             <field eval="btw_code_4b" name="tax_code_id"/>
                             <field eval="btw_code_4b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4342,43 +4342,43 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_I_6"/>
-                            <field name="account_collected_id" ref="vat_refund6"/>
-                            <field name="account_paid_id" ref="vat_refund6"/>
+                            <field name="account_collected_id" ref="vat_refund_low"/>
+                            <field name="account_paid_id" ref="vat_refund_low"/>
                             <field eval="btw_code_5b" name="tax_code_id"/>
                             <field eval="btw_code_5b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
                         </record>
-        <record id="btw_I_19" model="account.tax.template">
+        <record id="btw_I_21" model="account.tax.template">
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">Inkopen import binnen EU hoog</field>
-        <field name="description">19% BTW import binnen EU</field>
-            <field eval="0.19" name="amount"/>
+        <field name="description">21% BTW import binnen EU</field>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
             <field eval="True" name="child_depend"/>
             <field eval="omz_code_4b" name="base_code_id"/>
             <field eval="omz_code_4b" name="ref_base_code_id"/>
             <field name="type_tax_use">purchase</field>
         </record>
-                        <record id="btw_I_19_1" model="account.tax.template">
+                        <record id="btw_I_21_1" model="account.tax.template">
                             <field name="chart_template_id" ref="l10nnl_chart_template"/>
                             <field name="name">Inkopen import binnen EU hoog(1)</field>
                             <field eval="-1.00" name="amount"/>
                             <field name="type">percent</field>
-                            <field name="parent_id" ref="btw_I_19"/>
-                            <field name="account_collected_id" ref="vat_payable19"/>
-                            <field name="account_paid_id" ref="vat_payable19"/>
+                            <field name="parent_id" ref="btw_I_21"/>
+                            <field name="account_collected_id" ref="vat_payable_high"/>
+                            <field name="account_paid_id" ref="vat_payable_high"/>
                             <field eval="btw_code_4b" name="tax_code_id"/>
                             <field eval="btw_code_4b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
                         </record>
-                        <record id="btw_I_19_2" model="account.tax.template">
+                        <record id="btw_I_21_2" model="account.tax.template">
                             <field name="chart_template_id" ref="l10nnl_chart_template"/>
                             <field name="name">Inkopen import binnen EU hoog(2)</field>
                             <field eval="1.00" name="amount"/>
                             <field name="type">percent</field>
-                            <field name="parent_id" ref="btw_I_19"/>
-                            <field name="account_collected_id" ref="vat_refund19"/>
-                            <field name="account_paid_id" ref="vat_refund19"/>
+                            <field name="parent_id" ref="btw_I_21"/>
+                            <field name="account_collected_id" ref="vat_refund_high"/>
+                            <field name="account_paid_id" ref="vat_refund_high"/>
                             <field eval="btw_code_5b" name="tax_code_id"/>
                             <field eval="btw_code_5b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4400,8 +4400,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="-1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_I_overig"/>
-                            <field name="account_collected_id" ref="vat_payable19"/>
-                            <field name="account_paid_id" ref="vat_payable19"/>
+                            <field name="account_collected_id" ref="vat_payable_high"/>
+                            <field name="account_paid_id" ref="vat_payable_high"/>
                             <field eval="btw_code_4b" name="tax_code_id"/>
                             <field eval="btw_code_4b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4412,8 +4412,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_I_overig"/>
-                            <field name="account_collected_id" ref="vat_refund19"/>
-                            <field name="account_paid_id" ref="vat_refund19"/>
+                            <field name="account_collected_id" ref="vat_refund_high"/>
+                            <field name="account_paid_id" ref="vat_refund_high"/>
                             <field eval="btw_code_5b" name="tax_code_id"/>
                             <field eval="btw_code_5b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4463,8 +4463,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="-1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_E1"/>
-                            <field name="account_collected_id" ref="vat_payable6"/>
-                            <field name="account_paid_id" ref="vat_payable6"/>
+                            <field name="account_collected_id" ref="vat_payable_low"/>
+                            <field name="account_paid_id" ref="vat_payable_low"/>
                             <field eval="btw_code_4a" name="tax_code_id"/>
                             <field eval="btw_code_4a" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4475,8 +4475,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_E1"/>
-                            <field name="account_collected_id" ref="vat_refund6"/>
-                            <field name="account_paid_id" ref="vat_refund6"/>
+                            <field name="account_collected_id" ref="vat_refund_low"/>
+                            <field name="account_paid_id" ref="vat_refund_low"/>
                             <field eval="btw_code_5b" name="tax_code_id"/>
                             <field eval="btw_code_5b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4485,7 +4485,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">Inkopen import buiten EU hoog</field>
         <field name="description">BTW import buiten EU</field>
-            <field eval="0.19" name="amount"/>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
             <field eval="True" name="child_depend"/>
             <field eval="omz_code_4a" name="base_code_id"/>
@@ -4498,8 +4498,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="-1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_E2"/>
-                            <field name="account_collected_id" ref="vat_payable19"/>
-                            <field name="account_paid_id" ref="vat_payable19"/>
+                            <field name="account_collected_id" ref="vat_payable_high"/>
+                            <field name="account_paid_id" ref="vat_payable_high"/>
                             <field eval="btw_code_4a" name="tax_code_id"/>
                             <field eval="btw_code_4a" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4510,8 +4510,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_E2"/>
-                            <field name="account_collected_id" ref="vat_refund19"/>
-                            <field name="account_paid_id" ref="vat_refund19"/>
+                            <field name="account_collected_id" ref="vat_refund_high"/>
+                            <field name="account_paid_id" ref="vat_refund_high"/>
                             <field eval="btw_code_5b" name="tax_code_id"/>
                             <field eval="btw_code_5b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4520,7 +4520,7 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
             <field name="chart_template_id" ref="l10nnl_chart_template"/>
             <field name="name">Inkopen import buiten EU overig</field>
         <field name="description">BTW import buiten EU</field>
-            <field eval="0.19" name="amount"/>
+            <field eval="0.21" name="amount"/>
             <field name="type">percent</field>
             <field eval="True" name="child_depend"/>
             <field eval="omz_code_4a" name="base_code_id"/>
@@ -4533,8 +4533,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="-1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_E_overig"/>
-                            <field name="account_collected_id" ref="vat_payable19"/>
-                            <field name="account_paid_id" ref="vat_payable19"/>
+                            <field name="account_collected_id" ref="vat_payable_high"/>
+                            <field name="account_paid_id" ref="vat_payable_high"/>
                             <field eval="btw_code_4a" name="tax_code_id"/>
                             <field eval="btw_code_4a" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
@@ -4545,8 +4545,8 @@ TODO rubriek 1c en 1d moeten nog, zijn variabel, dus BTW percentage moet door ge
                             <field eval="1.00" name="amount"/>
                             <field name="type">percent</field>
                             <field name="parent_id" ref="btw_E_overig"/>
-                            <field name="account_collected_id" ref="vat_refund19"/>
-                            <field name="account_paid_id" ref="vat_refund19"/>
+                            <field name="account_collected_id" ref="vat_refund_high"/>
+                            <field name="account_paid_id" ref="vat_refund_high"/>
                             <field eval="btw_code_5b" name="tax_code_id"/>
                             <field eval="btw_code_5b" name="ref_tax_code_id"/>
                             <field name="type_tax_use">purchase</field>
index aea7ee6..b34993f 100644 (file)
 import mail_message_subtype
 import mail_alias
 import mail_followers
+import mail_vote
+import mail_favorite
 import mail_message
 import mail_mail
 import mail_thread
 import mail_group
-import mail_vote
-import mail_favorite
 import res_partner
 import res_users
 import report
index bda93fc..f189b10 100644 (file)
@@ -2,6 +2,12 @@
 <openerp>
     <data noupdate="1">
 
+        <!-- Update demo user to avoid mail bombing -->
+        <record id="base.partner_demo" model="res.partner">
+            <field name="notification_email_send">none</field>
+        </record>
+
+        <!-- Pushed to all employees -->
         <record id="message_blogpost0" model="mail.message">
             <field name="model">mail.group</field>
             <field name="res_id" ref="mail.group_all_employees"/>
@@ -10,7 +16,6 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
             <field name="type">comment</field>
             <field name="subtype_id" ref="mt_comment"/>
         </record>
-
         <record id="message_blogpost0_comment0" model="mail.message">
             <field name="model">mail.group</field>
             <field name="res_id" ref="group_all_employees"/>
@@ -19,7 +24,6 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
             <field name="type">comment</field>
             <field name="subtype_id" ref="mt_comment"/>
         </record>
-
         <record id="message_blogpost0_comment1" model="mail.message">
             <field name="model">mail.group</field>
             <field name="res_id" ref="group_all_employees"/>
@@ -28,7 +32,7 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
             <field name="type">comment</field>
             <field name="subtype_id" ref="mt_comment"/>
         </record>
-
+        <!-- This one is starred for having mailboxes with demo data -->
         <record id="message_blogpost0_comment2" model="mail.message">
             <field name="model">mail.group</field>
             <field name="res_id" ref="group_all_employees"/>
@@ -36,8 +40,8 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
             <field name="parent_id" ref="message_blogpost0"/>
             <field name="type">comment</field>
             <field name="subtype_id" ref="mt_comment"/>
+            <field name="favorite_user_ids" eval="[(6, 0, [ref('base.user_root'), ref('base.user_demo')])]"/>
         </record>
-
         <record id="message_blogpost0_comment3" model="mail.message">
             <field name="model">mail.group</field>
             <field name="res_id" ref="group_all_employees"/>
@@ -47,5 +51,196 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company
             <field name="subtype_id" ref="mt_comment"/>
         </record>
 
+        <!-- Demo user and admin conversation -->
+        <record id="message_discussion" model="mail.message">
+            <field name="body">Hello Demo User! I was wondering whether you had some issues with our secret task about putting cats everywhere in OpenERP.</field>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="author_id" ref="base.partner_root"/>
+            <field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]"/>
+        </record>
+        <record id="message_discussion_answer1" model="mail.message">
+            <field name="body">No specific issues, I think everything is clear.</field>
+            <field name="parent_id" ref="message_discussion"/>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="author_id" ref="base.partner_demo"/>
+            <field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
+        </record>
+        <record id="message_discussion_answer2" model="mail.message">
+            <field name="body">Ow, just to be sure... we were talking about lolcats, right ?</field>
+            <field name="parent_id" ref="message_discussion"/>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="author_id" ref="base.partner_demo"/>
+            <field name="partner_ids" eval="[(6, 0, [ref('base.partner_root')])]"/>
+        </record>
+        <record id="message_discussion_answer3" model="mail.message">
+            <field name="body">Absolutely!</field>
+            <field name="parent_id" ref="message_discussion"/>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="author_id" ref="base.partner_root"/>
+            <field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]"/>
+        </record>
+
+        <!-- External mail + reply with attachment conversation -->
+        <record id="message_video2" model="mail.message">
+            <field name="subject">Plan to install OpenERP</field>
+            <field name="model">mail.message</field>
+            <field name="body">
+                &lt;![CDATA[Email0 inquiry]]&gt;
+                &lt;div&gt;&lt;font size="2"&gt;Hello,&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font size="2"&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;
+                &lt;div&gt;&lt;font size="2"&gt;I am interested in your company's product and I plan to install OpenERP for my company and affordable price.&lt;/font&gt;&lt;/div&gt;&lt;br/&gt;
+                &lt;div&gt;&lt;font size="2"&gt;Can you please send me services catalogue?&lt;/font&gt;&lt;/div&gt;&lt;br/&gt;
+                Sophie
+            </field>
+            <field name="type">email</field>
+            <field name="author_id" ref="base.res_partner_2"/>
+            <field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
+        </record>
+        <record id="message_video2_attachment1" model="ir.attachment">
+            <field name="model">ir.attachment</field>
+            <field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
+            <field name="datas_fname">catalogue 2012.pdf</field>
+            <field name="name">catalogue 2012.pdf</field>
+        </record>
+        <record id="message_video2_message1" model="mail.message">
+            <field name="subject">Re: Plan to install OpenERP</field>
+            <field name="body">
+                Dear Customer,&lt;br/&gt;
+                Thanks for showing interest in our products.&lt;br/&gt;
+                We have attached the catalogue,&lt;br/&gt;
+                We would like to know your interests, so let us know when we can call you for more details.&lt;br/&gt;
+                Regards
+            </field>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="parent_id" ref="message_video2"/>
+            <field name="author_id" ref="base.partner_demo"/>
+            <field name="attachment_ids" eval="[(6, 0, [ref('message_video2_attachment1')])]"/>
+            <field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo'),ref('base.partner_root'),ref('base.res_partner_2')])]"/>
+        </record>
+
+        <!-- Employee & other + attachments conversation -->
+        <record id="message_video1_attachment1" model="ir.attachment">
+            <field name="model">ir.attachment</field>
+            <field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
+            <field name="datas_fname">migration.doc</field>
+            <field name="name">migration.doc</field>
+        </record>
+        <record id="message_video1_attachment2" model="ir.attachment">
+            <field name="model">ir.attachment</field>
+            <field name="datas">
+                /9j/4AAQSkZJRgABAQEASABIAAD/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkz
+                ODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2Nj
+                Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wAARCABkAGQDAREA
+                AhEBAxEB/8QAGgAAAwEBAQEAAAAAAAAAAAAAAAQFAwIBBv/EAD0QAAEDAgMDCAcHAwUAAAAAAAEA
+                AgMEEQUSsiExchMzNDVBUXFzIjJhkbGzwRQVJGKBwtElg6FCUlPh8P/EABoBAQADAQEBAAAAAAAA
+                AAAAAAADBAUBAgb/xAAwEQACAQIDBgYBBAMBAAAAAAAAAQIDBBEyMwUSMUFx8BMhNFGx0cEUgZGh
+                I0LhNf/aAAwDAQACEQMRAD8A+55QFzgA4lpsbDttf6qqoN8D0e5z/sf7l3w5HMQz/kd7k8OQxOJX
+                SOheIgWyFpDXFtwDbYV1U5DETLcVFwySnDQLNuHOO/Zc227LBevDGJ1kxE2DpYiA6+xrgSMzT3dw
+                d7wm4xiZtZiwLS+andZrb+g4XdcZuzdbNb22TcGINbi4YbzU7nZTuY7a6+zs3Af5HtTwxidPbihe
+                MksDWAvuCxxJF/R7O7f4puDE8DcVLiXSwZc7TZrHXDRa43du33puDEoZ/wAjvcvHhyGIZ/yO9yeH
+                IYgx4e27d1yP1BsvLWDwZ0Uyh9VVB2W4y5Mx2XLf+h7lIm1FYHBSsldTcoMkBIaXNsHG/d2+Kb7X
+                mzkvJNoq0ji6khcd5YCfcplwBtddAXQBdAF0AXQBdAF0AXQCWI17qIR5YhIX3vd+WwFvYe9V69wq
+                LimscSalR8TF48DWm5t3mP1FcnmIkZfZYqmSflWkjlG7jbc0W+JUkEnHzB6cMpizKWOItl9Y7QvW
+                5FjFoajYIo2xsFmtAA8F7OHSANqANqAEBnJPFE9jJJGtc82aCdpXUmzjkk8GabVw6Iy4i3NPFGwi
+                WJjnAuHom2z4leazdKm5ik41Km4FNVzS4Uahwj5UZ9wOW4cQNl/YvNtN1YRk+Z6uEqTaXIm108lR
+                SU8kuXOTKDlFhscB9FS2kt2pBL3+ixYycqcm++JYpubd5j9RVipmKyPafnKjzBpapafAM3Uhwxkq
+                oo6hkD3ESP8AVGU2O/t/Qrqi2sTy5pPd5my4eidh1RPNV1LJZM7WhrmjKBluXbPbuClqRUUsCClN
+                ybxGMQLhRvLXFpuNrTY7wvNNYy8z3VbUHgGHlzqGEucXEt2lxuSuTzM7T84oUxLpsHFF8wKSGRkN
+                TUR1RVM8mKVEMkmaNoJa3KBb0rb1mW9adSrOL4I0qtOMacWuLFH9Nq/Kk1BXLz0z6FK19QM0PUTv
+                GTW5Q2GlAsXuaXfInz9X0/FNrVfaerDr9Emz9KXfuXKbm3eY/UVNUzECFKwuEU+V7mEzja02PqBW
+                bdJ8SGu2o+Q5RFz6Gnc5xLnRNJPebLsuLPccqEq3rel4h8HKWOmyGWqipb2lQlgk4SL11XwR/F6n
+                q8EVqHGXfuOYgPwb9p3t1BR08xJWyMlGWRn2BrJHtb6FwDsN32P+FnXVWcbqME/Jl22hF27bXeA3
+                iXTYOKP5gWpDIzOqaiOMP65quF2pY1prVOr+TVr6Ue+Ri/p1X5MmoLSvfTPoZ9r6jv3GaLqJ3jJr
+                cobDSgWL3NLvkT5+r6fim1qvtPVh1+iTZ+lLv3LlNzbvMfqKmqZiBClZzU3njQFatiC4yjdAB930
+                1/8Aib8AkuLJIZUJ1vW9LxD4OUsdNkMtVFOwUJYJWF2FbVlxsAyPb+r1NV4IrUMz79xyvymieQbg
+                lu2/5gvFLMiStkZIf61B/a+Ysq89bDv3L9r6Z98h3Eh+Ng4o/mBbEMjMypqI4w/rmq4XaljWmtU6
+                v5NWvpR75GL+m1fkyagtK99M+hn2vqO/cYoR/QneMmtyhsNKBYvc0u+QhP1fT8U2tV9p6sOv0SbP
+                0pd+5cpubd5j9RU1TMQIUrebm88aArVsQXGU8wOpfPA6ORrAIWta0gb9ltvuVW3ruvjJor2VxKvF
+                4rgc4g9kWJ0z3uDWBzSSdw2OV1zjCk3J4ElWUYVE5PBFCCop6jNyMjH5bXt2KCFSM8rxLji0k2uJ
+                EADm14IBBbFsPG5c2l5W779iPZ+s+/cdYAMBi2D1W6gu2eSPQ9XnGXUSf61B/a+YqF562HfuWrX0
+                z75DmJW+2wW3Zo/mBbEMjMypqI5w+33zU7rZTqWNaa1Tq/k1a+lHvkYv6bV93IyagtK99M+hn2vq
+                Biit9xO3XvJrKhsNKBYvc0u+QhP1fT8U2tV9p6sOv0SbP0pd+5cpubd5j9RU1TMQIUrObm88aArV
+                sQXGUTwGojhkkjeSHSloZs37CsmwqRWMHxZT2VSm6Mp4eWJ1j22UeDfqrd/6b9/wyLaeX+Pya4Hz
+                1Tws/cqmzOEv2PpLz/UnzSvZUPY02bKGhwtvsSR8VLtWtJYUuWGP9/8ADGs60o3saa4PH4ZVZ1DH
+                wt1BXrPJHoXLzjLqThKyR9GGkkxuja7Z28oFmXNSM7yO6+H/AEmsqkZ20t3liv6KGJdNg4o/mBbc
+                MjKFTURxh/XNTwnUsa01qnV/JrV9KPfIxf02r8mTUFpXvpn0M619QMUR/oTvGTWVDYaUCxe5pd8h
+                Cfq+n4ptar7T1Ydfok2fpS79y5T827zH6ipqmYgQpWc1N540BWrYguMpJw3ptN5v7Svn7TXQ2R6G
+                XV/gcx7nR4D6rVv/AE37/hmdtPL/AB+TXA+eqeFn7lU2Zll+x9Jef6k2p6W3/wB2lNraq6flmBa/
+                +jT6P4ZXZ1DHwt1Badllh0NK74y6/kkU/SGefHrCwo+qXUj2R6ap1fwijjZIeHNJa4NaQR2EOut2
+                vOVO3lKPH/qKd7Jwi5R4pfk4wRznVz3OJc4xXJPacyy9ntynJs2HJytqbfsvgyqZhDWz5gTyjXsF
+                u8uH8K/tCtGFHcfFozaFaNO6jF82O0PUTvGTW5LDSgXr3NLvkT5+r6fim1qvtPVh1+iTZ+lLv3Ll
+                PzbvMfqKmnmIBarhmeJGxxhwdIHg5gP9IH0U1GoocSOrBzWCEKTDquCoikdE0hj8xs8dxH1WZQtn
+                TqKbYsYO3t3Slxbx+DfEqWqrHgshDdg3vHt/lXbnCtS3F74lW8tpV1hF9+Zph0FTSPlc+EHOGgAP
+                HZf+VBaUvAT3nxNWvVVTDAUlw6sknDxE0AdheEvaX6ialF8sDNo0JU7qNdvyWPw/sdEdQMNZTch6
+                QAF847DdW7eapKKfItV/8jeHMRhw2sZK1xibYSNf647HA/RZytmqyqY+WOJ5sYu3ozhLi23/AChr
+                Eaeqqz6EAHogbXjvutCtJVKMqa4v7RXuqEqsWlz+znDqWqpJnSPhBuwNADx3qpa0fBbcnxL29/hh
+                T5pJfwjKqoKuecyNhaBmJ2vHaV7vYfqN3dfAofp5fqIVcfJPEbp4qmLDjTGAFxzbQ8drifqprZql
+                CMXyL1w/Fba5k/7srM7rxtyuJIHKbrlU61CdSrvuXljiQ2MZW7qObx3sMP7+y3A1zY/TFiXOda97
+                XcT9Vbk8XiSDKsnAQAgBACAEAIAQAgBACAEAIAQAgBACAEAIAQAgBACAEAIAQAgBACAEAIAQAgBA
+                CAEAIAQAgP/Z
+            </field>
+            <field name="datas_fname">activity graph 2012.jpg</field>
+            <field name="name">activity graph 2012</field>
+        </record>
+        <record id="message_video1" model="mail.message">
+            <field name="model">mail.group</field>
+            <field name="body">
+                Hi,&lt;br/&gt;
+                The beta OpenERP 7 is scheduled for November 12.&lt;br/&gt;
+                You will find attached the document for migration from version 6 to 7, and the activity graph for the current year. Good reading.&lt;br/&gt;
+                Sincerely
+            </field>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="author_id" ref="base.partner_demo"/>
+            <field name="attachment_ids" eval="[(6, 0, [ref('message_video1_attachment1'), ref('message_video1_attachment2')])]"/>
+            <field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
+        </record>
+        <record id="message_video1_message1" model="mail.message">
+            <field name="model">mail.group</field>
+            <field name="body">
+                Thank you,&lt;br/&gt;
+                Could you prepare and send us also the document for version 7.1 which will come soon?&lt;br/&gt;
+                Sincerely
+            </field>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="parent_id" ref="message_video1"/>
+            <field name="author_id" ref="base.partner_root"/>
+            <field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
+        </record>
+
+        <!-- Network admin & employee conversation -->
+        <record id="message_video0" model="mail.message">
+            <field name="model">mail.group</field>
+            <field name="body">I changed the infrastructure of networks, if there are still changes to be made please do not hesitate to contact me.</field>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="author_id" ref="base.partner_root"/>
+            <field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
+        </record>
+        <record id="message_video0_message1" model="mail.message">
+            <field name="model">mail.group</field>
+            <field name="body">Thank you, the networks is perfect now ! Could you add a IP phone for Jhon ?</field>
+            <field name="parent_id" ref="message_video0"/>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="author_id" ref="base.partner_demo"/>
+            <field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
+        </record>
+        <record id="message_video0_message2" model="mail.message">
+            <field name="model">mail.group</field>
+            <field name="body">It's right, his internal phone number is 0093</field>
+            <field name="parent_id" ref="message_video0"/>
+            <field name="type">comment</field>
+            <field name="subtype_id" ref="mt_comment"/>
+            <field name="author_id" ref="base.partner_root"/>
+            <field name="notified_partner_ids" eval="[(6, 0, [ref('base.partner_demo'), ref('base.partner_root')])]"/>
+        </record>
+
     </data>
 </openerp>
index 11d0ee7..2daa69b 100644 (file)
@@ -9,7 +9,7 @@
         <record model="mail.group" id="group_all_employees">
             <field name="name">Whole Company</field>
             <field name="group_ids" eval="[(4, ref('base.group_user'))]"/>
-            <field name="description">Discussion about best sales practices and deals.</field>
+            <field name="description">General announces for all employees.</field>
         </record>
 
         <!-- notify all employees of module installation -->
index e8cf264..6a8b24f 100644 (file)
@@ -61,10 +61,10 @@ class mail_notification(osv.Model):
 
     _columns = {
         'partner_id': fields.many2one('res.partner', string='Contact',
-                        ondelete='cascade', required=True),
-        'read': fields.boolean('Read'),
+                        ondelete='cascade', required=True, select=1),
+        'read': fields.boolean('Read', select=1),
         'message_id': fields.many2one('mail.message', string='Message',
-                        ondelete='cascade', required=True),
+                        ondelete='cascade', required=True, select=1),
     }
 
     _defaults = {
@@ -102,7 +102,7 @@ class mail_notification(osv.Model):
         # some messages do not have notifications: find which one, create notification, update read status
         exist_notification = dict.fromkeys(msg_ids, False)
         for notification in self.browse(cr, uid, notif_ids, context=context):
-            exist_notification[notification.message_id] = True
+            exist_notification[notification.message_id.id] = True
         for msg_id in exist_notification.keys():
             self.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context)
         return self.write(cr, uid, notif_ids, {'read': read}, context=context)
index 2028e8d..214f6ed 100644 (file)
                                 </div>
                                 <div class="oe_group_details">
                                     <h4><a type="open"><field name="name"/></a></h4>
-                                    <ul>
-                                        <li><t t-raw="record.message_summary.raw_value"/></li>
-                                        <li t-if="! record.message_is_follower.raw_value"><a name="action_follow" string="Join" type="object" class="oe_group_join">Not following</a></li>
-                                        <li t-if="record.message_is_follower.raw_value"><a name="action_unfollow" string="Leave" type="object" class="oe_group_leave">Following</a></li>
-                                    </ul>
+                                    <div class="oe_grey">
+                                        <field name="description"/>
+                                    </div>
+                                    <div class="oe_kanban_footer_left">
+                                        <t t-raw="record.message_summary.raw_value"/>
+                                    </div>
+                                    <div class="oe_group_button">
+                                        <button t-if="record.message_is_follower.raw_value" name="action_unfollow" type="object" class="oe_group_join">Unfollow</button>
+                                        <button t-if="! record.message_is_follower.raw_value" name="action_follow" type="object">Join Group</button>
+                                    </div>
                                 </div>
                             </div>
-                            <script>
-                                $('.oe_group_join').mouseover(function () { $(this).html('Follow'); }).mouseleave(function () { $(this).html('Not following'); });
-                                $('.oe_group_leave').mouseover(function () { $(this).html('Unfollow'); }).mouseleave(function () { $(this).html('Following'); });
-                            </script>
                         </t>
                     </templates>
                 </kanban>
index bd0ade3..c72af5e 100644 (file)
@@ -20,6 +20,7 @@
 ##############################################################################
 
 import logging
+import pdb
 import tools
 
 from email.header import decode_header
@@ -50,8 +51,9 @@ class mail_message(osv.Model):
     _description = 'Message'
     _inherit = ['ir.needaction_mixin']
     _order = 'id desc'
+    _rec_name = 'record_name'
 
-    _message_read_limit = 10
+    _message_read_limit = 30
     _message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'email_from',
         'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids']
     _message_record_name_length = 18
@@ -120,21 +122,26 @@ class mail_message(osv.Model):
                  "message, comment for other messages such as user replies"),
         'email_from': fields.char('From',
             help="Email address of the sender. This field is set when no matching partner is found for incoming emails."),
-        'author_id': fields.many2one('res.partner', 'Author',
+        'author_id': fields.many2one('res.partner', 'Author', select=1,
+            ondelete='set null',
             help="Author of the message. If not set, email_from may hold an email address that did not match any partner."),
         'partner_ids': fields.many2many('res.partner', string='Recipients'),
         'notified_partner_ids': fields.many2many('res.partner', 'mail_notification',
-            'message_id', 'partner_id', 'Recipients'),
+            'message_id', 'partner_id', 'Notified partners',
+            help='Partners that have a notification pushing this message in their mailboxes'),
         'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel',
             'message_id', 'attachment_id', 'Attachments'),
-        'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', help="Initial thread message."),
+        'parent_id': fields.many2one('mail.message', 'Parent Message', select=True,
+            ondelete='set null', help="Initial thread message."),
         'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'),
         'model': fields.char('Related Document Model', size=128, select=1),
         'res_id': fields.integer('Related Document ID', select=1),
         'record_name': fields.function(_get_record_name, type='char',
             store=True, string='Message Record Name',
             help="Name get of the related document."),
-        'notification_ids': fields.one2many('mail.notification', 'message_id', 'Notifications'),
+        'notification_ids': fields.one2many('mail.notification', 'message_id',
+            string='Notifications',
+            help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'),
         'subject': fields.char('Subject'),
         'date': fields.datetime('Date'),
         'message_id': fields.char('Message-Id', help='Message unique identifier', select=1, readonly=1),
@@ -142,7 +149,8 @@ class mail_message(osv.Model):
         'to_read': fields.function(_get_to_read, fnct_search=_search_to_read,
             type='boolean', string='To read',
             help='Functional field to search for messages the current user has to read'),
-        'subtype_id': fields.many2one('mail.message.subtype', 'Subtype'),
+        'subtype_id': fields.many2one('mail.message.subtype', 'Subtype',
+            ondelete='set null', select=1,),
         'vote_user_ids': fields.many2many('res.users', 'mail_vote',
             'message_id', 'user_id', string='Votes',
             help='Users that voted for this message'),
@@ -200,67 +208,99 @@ class mail_message(osv.Model):
     # Message loading for web interface
     #------------------------------------------------------
 
-    def _message_get_dict(self, cr, uid, message, context=None):
-        """ Return a dict representation of the message. This representation is
-            used in the JS client code, to display the messages.
+    def _message_read_dict_postprocess(self, cr, uid, messages, message_tree, context=None):
+        """ Post-processing on values given by message_read. This method will
+            handle partners in batch to avoid doing numerous queries.
 
-            :param dict message: read result of a mail.message
+            :param list messages: list of message, as get_dict result
+            :param dict message_tree: {[msg.id]: msg browse record}
         """
-        # TDE note: this method should be optimized, to lessen the number of queries, will be done ASAP
-        is_author = False
-        if message['author_id']:
-            is_author = message['author_id'][0] == self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
-            author_id = message['author_id']
-        elif message['email_from']:
-            author_id = (0, message['email_from'])
-
-        has_voted = False
-        if uid in message.get('vote_user_ids'):
-            has_voted = True
-
-        is_favorite = False
-        if uid in message.get('favorite_user_ids'):
-            is_favorite = True
-
-        is_private = True
-        if message.get('model') and message.get('res_id'):
-            is_private = False
-
-        try:
-            attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, message['attachment_ids'], context=context)]
-        except (orm.except_orm, osv.except_osv):
+        res_partner_obj = self.pool.get('res.partner')
+        ir_attachment_obj = self.pool.get('ir.attachment')
+        pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
+
+        # 1. Aggregate partners (author_id and partner_ids) and attachments
+        partner_ids = set()
+        attachment_ids = set()
+        for key, message in message_tree.iteritems():
+            if message.author_id:
+                partner_ids |= set([message.author_id.id])
+            if message.partner_ids:
+                partner_ids |= set([partner.id for partner in message.partner_ids])
+            if message.attachment_ids:
+                attachment_ids |= set([attachment.id for attachment in message.attachment_ids])
+
+        # Filter author_ids uid can see
+        # partner_ids = self.pool.get('res.partner').search(cr, uid, [('id', 'in', partner_ids)], context=context)
+        partners = res_partner_obj.name_get(cr, uid, list(partner_ids), context=context)
+        partner_tree = dict((partner[0], partner) for partner in partners)
+
+        # 2. Attachments
+        attachments = ir_attachment_obj.read(cr, uid, list(attachment_ids), ['id', 'datas_fname'], context=context)
+        attachments_tree = dict((attachment['id'], {'id': attachment['id'], 'filename': attachment['datas_fname']}) for attachment in attachments)
+
+        # 3. Update message dictionaries
+        for message_dict in messages:
+            message_id = message_dict.get('id')
+            message = message_tree[message_id]
+            if message.author_id:
+                author = partner_tree[message.author_id.id]
+            else:
+                author = (0, message.email_from)
+            partner_ids = []
+            for partner in message.partner_ids:
+                if partner.id in partner_tree:
+                    partner_ids.append(partner_tree[partner.id])
             attachment_ids = []
+            for attachment in message.attachment_ids:
+                if attachment.id in attachments_tree:
+                    attachment_ids.append(attachments_tree[attachment.id])
+            message_dict.update({
+                'is_author': pid == author[0],
+                'author_id': author,
+                'partner_ids': partner_ids,
+                'attachment_ids': attachment_ids,
+                })
+        return True
 
-        # TDE note: should we send partner_ids ?
-        # TDE note: shouldn't we separated followers and other partners ? costly to compute maybe ,
-        try:
-            partner_ids = self.pool.get('res.partner').name_get(cr, uid, message['partner_ids'], context=context)
-        except (orm.except_orm, osv.except_osv):
-            partner_ids = []
+    def _message_read_dict(self, cr, uid, message, parent_id=False, context=None):
+        """ Return a dict representation of the message. This representation is
+            used in the JS client code, to display the messages. Partners and
+            attachments related stuff will be done in post-processing in batch.
+
+            :param dict message: mail.message browse record
+        """
+        # private message: no model, no res_id
+        is_private = False
+        if not message.model or not message.res_id:
+            is_private = True
+        # votes and favorites: res.users ids, no prefetching should be done
+        vote_nb = len(message.vote_user_ids)
+        has_voted = uid in [user.id for user in message.vote_user_ids]
+        is_favorite = uid in [user.id for user in message.favorite_user_ids]
+
+        return {'id': message.id,
+                'type': message.type,
+                'body': message.body,
+                'model': message.model,
+                'res_id': message.res_id,
+                'record_name': message.record_name,
+                'subject': message.subject,
+                'date': message.date,
+                'to_read': message.to_read,
+                'parent_id': parent_id,
+                'is_private': is_private,
+                'author_id': False,
+                'is_author': False,
+                'partner_ids': [],
+                'vote_nb': vote_nb,
+                'has_voted': has_voted,
+                'is_favorite': is_favorite,
+                'attachment_ids': [],
+            }
 
-        return {
-            'id': message['id'],
-            'type': message['type'],
-            'attachment_ids': attachment_ids,
-            'body': message['body'],
-            'model': message['model'],
-            'res_id': message['res_id'],
-            'record_name': message['record_name'],
-            'subject': message['subject'],
-            'date': message['date'],
-            'author_id': author_id,
-            'is_author': is_author,
-            'partner_ids': partner_ids,
-            'parent_id': False,
-            'vote_nb': len(message['vote_user_ids']),
-            'has_voted': has_voted,
-            'is_private': is_private,
-            'is_favorite': is_favorite,
-            'to_read': message['to_read'],
-        }
-
-    def _message_read_add_expandables(self, cr, uid, message_list, read_messages,
-            thread_level=0, message_loaded_ids=[], domain=[], parent_id=False, context=None, limit=None):
+    def _message_read_add_expandables(self, cr, uid, messages, message_tree, parent_tree,
+            message_unload_ids=[], thread_level=0, domain=[], parent_id=False, context=None):
         """ Create expandables for message_read, to load new messages.
             1. get the expandable for new threads
                 if display is flat (thread_level == 0):
@@ -275,96 +315,82 @@ class mail_message(osv.Model):
                     for each hole in the child list based on message displayed,
                     create an expandable
 
-            :param list message_list:list of message structure for the Chatter
+            :param list messages: list of message structure for the Chatter
                 widget to which expandables are added
-            :param dict read_messages: dict [id]: read result of the messages to
-                easily have access to their values, given their ID
+            :param dict message_tree: dict [id]: browse record of this message
+            :param dict parent_tree: dict [parent_id]: [child_ids]
+            :param list message_unload_ids: list of message_ids we do not want
+                to load
             :return bool: True
         """
-        def _get_expandable(domain, message_nb, parent_id, id, model):
+        def _get_expandable(domain, message_nb, parent_id, max_limit):
             return {
                 'domain': domain,
                 'nb_messages': message_nb,
                 'type': 'expandable',
                 'parent_id': parent_id,
-                'id':  id,
-                # TDE note: why do we need model sometimes, and sometimes not ???
-                'model': model,
+                'max_limit':  max_limit,
             }
 
-        # all_not_loaded_ids = []
-        id_list = sorted(read_messages.keys())
-        if not id_list:
-            return message_list
+        if not messages:
+            return True
+        message_ids = sorted(message_tree.keys())
 
         # 1. get the expandable for new threads
         if thread_level == 0:
-            exp_domain = domain + [('id', '<', min(message_loaded_ids + id_list))]
+            exp_domain = domain + [('id', '<', min(message_unload_ids + message_ids))]
         else:
-            exp_domain = domain + ['!', ('id', 'child_of', message_loaded_ids + id_list)]
+            exp_domain = domain + ['!', ('id', 'child_of', message_unload_ids + parent_tree.keys())]
         ids = self.search(cr, uid, exp_domain, context=context, limit=1)
         if ids:
-            message_list.append(_get_expandable(exp_domain, -1, parent_id, -1, None))
+            # inside a thread: prepend
+            if parent_id:
+                messages.insert(0, _get_expandable(exp_domain, -1, parent_id, True))
+            # new threads: append
+            else:
+                messages.append(_get_expandable(exp_domain, -1, parent_id, True))
 
         # 2. get the expandables for new messages inside threads if display is not flat
         if thread_level == 0:
             return True
-        for message_id in id_list:
-            message = read_messages[message_id]
+        for message_id in message_ids:
+            message = message_tree[message_id]
 
-            # message is not a thread header (has a parent_id)
-            # TDE note: parent_id is false is there is a parent we can not see -> ok
-            if message.get('parent_id'):
+            # generate only for thread header messages (TDE note: parent_id may be False is uid cannot see parent_id, seems ok)
+            if message.parent_id:
                 continue
 
-            # TDE note: check search is correctly implemented in mail.message
-            not_loaded_ids = self.search(cr, uid, [
-                ('id', 'child_of', message['id']),
-                ('id', 'not in', message_loaded_ids),
-                ], context=context, limit=self._message_read_more_limit)
-            if not not_loaded_ids:
+            # check there are message for expandable
+            child_ids = set([child.id for child in message.child_ids]) - set(message_unload_ids)
+            child_ids = sorted(list(child_ids), reverse=True)
+            if not child_ids:
                 continue
 
-            # all_not_loaded_ids += not_loaded_ids
-            # group childs not read
-            id_min, id_max, nb = max(not_loaded_ids), 0, 0
-            for not_loaded_id in not_loaded_ids:
-                if not read_messages.get(not_loaded_id):
+            # make groups of unread messages
+            id_min, id_max, nb = max(child_ids), 0, 0
+            for child_id in child_ids:
+                if not child_id in message_ids:
                     nb += 1
-                    if id_min > not_loaded_id:
-                        id_min = not_loaded_id
-                    if id_max < not_loaded_id:
-                        id_max = not_loaded_id
+                    if id_min > child_id:
+                        id_min = child_id
+                    if id_max < child_id:
+                        id_max = child_id
                 elif nb > 0:
                     exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
-                    message_list.append(_get_expandable(exp_domain, nb, message_id, id_min, message.get('model')))
-                    id_min, id_max, nb = max(not_loaded_ids), 0, 0
+                    messages.append(_get_expandable(exp_domain, nb, message_id, False))
+                    id_min, id_max, nb = max(child_ids), 0, 0
                 else:
-                    id_min, id_max, nb = max(not_loaded_ids), 0, 0
+                    id_min, id_max, nb = max(child_ids), 0, 0
             if nb > 0:
                 exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
-                message_list.append(_get_expandable(exp_domain, nb, message_id, id_min, message.get('model')))
-
-        # message_loaded_ids = list(set(message_loaded_ids + read_messages.keys() + all_not_loaded_ids))
+                idx = [msg.get('id') for msg in messages].index(message_id) + 1
+                # messages.append(_get_expandable(exp_domain, nb, message_id, id_min))
+                messages.insert(idx, _get_expandable(exp_domain, nb, message_id, False))
 
         return True
 
-    def _get_parent(self, cr, uid, message, context=None):
-        """ Tools method that tries to get the parent of a mail.message. If
-            no parent, or if uid has no access right on the parent, False
-            is returned.
-
-            :param dict message: read result of a mail.message
-        """
-        if not message['parent_id']:
-            return False
-        parent_id = message['parent_id'][0]
-        try:
-            return self.read(cr, uid, parent_id, self._message_read_fields, context=context)
-        except (orm.except_orm, osv.except_osv):
-            return False
-
-    def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None, thread_level=0, context=None, parent_id=False, limit=None):
+    def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None,
+                        thread_level=0, context=None, parent_id=False, limit=None):
         """ Read messages from mail.message, and get back a list of structured
             messages to be displayed as discussion threads. If IDs is set,
             fetch these records. Otherwise use the domain to fetch messages.
@@ -388,46 +414,56 @@ class mail_message(osv.Model):
                 ancestors and expandables
             :return list: list of message structure for the Chatter widget
         """
-        # print 'message_read', ids, domain, message_unload_ids, thread_level, context, parent_id, limit
         assert thread_level in [0, 1], 'message_read() thread_level should be 0 (flat) or 1 (1 level of thread); given %s.' % thread_level
         domain = domain if domain is not None else []
         message_unload_ids = message_unload_ids if message_unload_ids is not None else []
         if message_unload_ids:
             domain += [('id', 'not in', message_unload_ids)]
         limit = limit or self._message_read_limit
-        read_messages = {}
+        message_tree = {}
         message_list = []
+        parent_tree = {}
 
         # no specific IDS given: fetch messages according to the domain, add their parents if uid has access to
         if ids is None:
             ids = self.search(cr, uid, domain, context=context, limit=limit)
-        for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
-            message_id = message['id']
-
-            # if not in tree and not in message_loaded list
-            if not message_id in read_messages and not message_id in message_unload_ids:
-                read_messages[message_id] = message
-                message_list.append(self._message_get_dict(cr, uid, message, context=context))
-
-                # get the older ancestor the user can read, update its ancestor field
-                if not thread_level:
-                    message_list[-1]['parent_id'] = parent_id
-                    continue
-                parent = self._get_parent(cr, uid, message, context=context)
-                while parent and parent.get('id') != parent_id:
-                    message_list[-1]['parent_id'] = parent.get('id')
-                    message = parent
-                    parent = self._get_parent(cr, uid, message, context=context)
-                # if in thread: add its ancestor to the list of messages
-                if not message['id'] in read_messages and not message['id'] in message_unload_ids:
-                    read_messages[message['id']] = message
-                    message_list.append(self._message_get_dict(cr, uid, message, context=context))
 
-        # get the child expandable messages for the tree
-        message_list = sorted(message_list, key=lambda k: k['id'])
-        self._message_read_add_expandables(cr, uid, message_list, read_messages, thread_level=thread_level,
-            message_loaded_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context, limit=limit)
+        # fetch parent if threaded, sort messages
+        for message in self.browse(cr, uid, ids, context=context):
+            message_id = message.id
+            if message_id in message_tree:
+                continue
+            message_tree[message_id] = message
 
+            # find parent_id
+            if thread_level == 0:
+                tree_parent_id = parent_id
+            else:
+                tree_parent_id = message_id
+                parent = message
+                while parent.parent_id and parent.parent_id.id != parent_id:
+                    parent = parent.parent_id
+                    tree_parent_id = parent.id
+                if not parent.id in message_tree:
+                    message_tree[parent.id] = parent
+            # newest messages first
+            parent_tree.setdefault(tree_parent_id, [])
+            if tree_parent_id != message_id:
+                parent_tree[tree_parent_id].append(self._message_read_dict(cr, uid, message_tree[message_id], parent_id=tree_parent_id, context=context))
+
+        if thread_level:
+            for key, message_id_list in parent_tree.iteritems():
+                message_id_list.sort(key=lambda item: item['id'])
+                message_id_list.insert(0, self._message_read_dict(cr, uid, message_tree[key], context=context))
+
+        parent_list = parent_tree.items()
+        parent_list = sorted(parent_list, key=lambda item: max([msg.get('id') for msg in item[1]]) if item[1] else item[0], reverse=True)
+        message_list = [message for (key, msg_list) in parent_list for message in msg_list]
+
+        # get the child expandable messages for the tree
+        self._message_read_dict_postprocess(cr, uid, message_list, message_tree, context=context)
+        self._message_read_add_expandables(cr, uid, message_list, message_tree, parent_tree,
+            thread_level=thread_level, message_unload_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context)
         return message_list
 
     # TDE Note: do we need this ?
@@ -461,7 +497,6 @@ class mail_message(osv.Model):
             - otherwise: remove the id
         """
         # Rules do not apply to administrator
-        # print '_search', uid, args
         if uid == SUPERUSER_ID:
             return super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order,
                 context=context, count=count, access_rights_uid=access_rights_uid)
@@ -592,11 +627,14 @@ class mail_message(osv.Model):
         other_ids = other_ids - set(document_related_ids)
         if not other_ids:
             return
+
         raise orm.except_orm(_('Access Denied'),
                             _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
                             (self._description, operation))
 
     def create(self, cr, uid, values, context=None):
+        if 'default_res_model' in context:
+            values['model']=context.get('default_res_model')
         if not values.get('message_id') and values.get('res_id') and values.get('model'):
             values['message_id'] = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values)
         newid = super(mail_message, self).create(cr, uid, values, context)
index 93b3579..7d2e948 100644 (file)
                     <field name="type"/>
                     <field name="author_id"/>
                     <field name="partner_ids"/>
+                    <filter string="Read"
+                            name="message_read" help="Show messages to read"
+                            domain="[('to_read', '=', False)]"/>
                     <filter string="Unread"
                             name="message_unread" help="Show messages to read"
                             domain="[('to_read', '=', True)]"/>
+                    <separator/>
                     <filter string="Comments"
                             name="comments" help="Comments"
                             domain="[('type', '=', 'comment')]"/>
-                    <filter string="Has attachments"
-                            name="attachments"
-                            domain="[('attachment_ids', '!=', False)]"/>
                     <filter string="Notifications"
                             name="notifications" help="Notifications"
                             domain="[('type', '=', 'notification')]"/>
                     <filter string="Emails"
                             name="emails" help="Emails"
                             domain="[('type', '=', 'email')]"/>
+                    <separator/>
+                    <filter string="Has attachments"
+                            name="attachments"
+                            domain="[('attachment_ids', '!=', False)]"/>
                 </search>
             </field>
         </record>
@@ -84,7 +89,6 @@
             <field name="view_type">form</field>
             <field name="view_mode">tree,form</field>
             <field name="search_view_id" ref="view_message_search"/>
-            <field name="context">{'search_default_to_read_message':True}</field>
         </record>
         
         <!-- Add menu entry in Settings/Email -->
index f320333..ee00b88 100644 (file)
@@ -74,17 +74,17 @@ class mail_thread(osv.AbstractModel):
             - message_unread: has uid unread message for the document
             - message_summary: html snippet summarizing the Chatter for kanban views """
         res = dict((id, dict(message_unread=False, message_summary='')) for id in ids)
+        user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
 
-        # search for unread messages, by reading directly mail.notification, as SUPERUSER
-        notif_obj = self.pool.get('mail.notification')
-        notif_ids = notif_obj.search(cr, SUPERUSER_ID, [
-            ('partner_id.user_ids', 'in', [uid]),
-            ('message_id.res_id', 'in', ids),
-            ('message_id.model', '=', self._name),
-            ('read', '=', False)
-        ], context=context)
-        for notif in notif_obj.browse(cr, SUPERUSER_ID, notif_ids, context=context):
-            res[notif.message_id.res_id]['message_unread'] = True
+        # search for unread messages, directly in SQL to improve performances
+        cr.execute("""  SELECT m.res_id FROM mail_message m
+                        RIGHT JOIN mail_notification n
+                        ON (n.message_id = m.id AND n.partner_id = %s AND n.read = False)
+                        WHERE m.model = %s AND m.res_id in %s""",
+                    (user_pid, self._name, tuple(ids),))
+        msg_ids = [result[0] for result in cr.fetchall()]
+        for msg_id in msg_ids:
+            res[msg_id]['message_unread'] = True
 
         for thread in self.browse(cr, uid, ids, context=context):
             cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or ''
index 07b2472..cdeade0 100644 (file)
         <record id="action_mail_inbox_feeds" model="ir.actions.client">
             <field name="name">Inbox</field>
             <field name="tag">mail.wall</field>
-            <field name="params" eval="&quot;{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', True)],
-                'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'inbox'} }&quot;"/>
+            <field name="res_model">mail.message</field>
+            <field name="context">{
+              'default_model': 'res.users', 
+              'default_res_id': uid
+            }</field>
+            <field name="params" eval="&quot;{
+                'domain': [
+                  ('notification_ids.partner_id.user_ids', 'in', [uid]),
+                  ('to_read', '=', True),
+                  ('favorite_user_ids', 'not in', [uid])
+                ],
+                'view_mailbox': True,
+                'view_inbox': True,
+                'read_action': 'read'
+            }&quot;"/>
+            <field name="help" type="html">
+                <p>
+                    <b>Good Job!</b> Your inbox is empty.
+                </p><p>
+                    Your inbox contains private messages or emails sent to you
+                    as well as information related to documents or people you
+                    follow.
+                </p>
+            </field>
         </record>
 
         <record id="action_mail_to_me_feeds" model="ir.actions.client">
             <field name="name">To: me</field>
             <field name="tag">mail.wall</field>
-            <field name="params" eval="&quot;{'domain': [('partner_ids.user_ids', 'in', [uid]), ('to_read', '=', True), ('author_id.user_ids', 'in', [uid])],
-                'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'inbox'} }&quot;"/>
+            <field name="res_model">mail.message</field>
+            <field name="context">{
+              'default_model': 'res.users', 
+              'default_res_id': uid, 
+              'search_default_message_unread': True
+            }</field>
+            <field name="params" eval="&quot;{
+                'domain': [('partner_ids.user_ids', 'in', [uid])],
+                'view_mailbox': True,
+                'read_action': 'read', }&quot;"/>
+            <field name="help" type="html">
+                <p>
+                    <b>No private message.</b>
+                </p><p>
+                    This list contains messages sent to you.
+                </p>
+            </field>
         </record>
 
         <record id="action_mail_star_feeds" model="ir.actions.client">
-            <field name="name">Favorites</field>
+            <field name="name">Todo</field>
             <field name="tag">mail.wall</field>
-            <field name="params" eval="&quot;{'domain': [('favorite_user_ids.user_ids', 'in', [uid])],
-                'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'stared'} }&quot;"/>
+            <field name="res_model">mail.message</field>
+            <field name="context">{
+              'default_model': 'res.users', 
+              'default_res_id': uid, 
+              'search_default_message_unread': True
+            }</field>
+            <field name="params" eval="&quot;{
+                'domain': [('favorite_user_ids', 'in', [uid])],
+                'view_mailbox': True,
+                'read_action': 'read', }&quot;"/>
+            <field name="help" type="html">
+                <p>
+                    <b>No todo!</b>
+                </p><p>
+                    When you process messages in your inbox, you can mark some
+                    as <i>todo</i>. From this menu, you can process all your todo.
+                </p>
+            </field>
         </record>
 
         <record id="action_mail_archives_feeds" model="ir.actions.client">
             <field name="name">Archives</field>
             <field name="tag">mail.wall</field>
-            <field name="params" eval="&quot;{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', False)],
-                'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'archives'} }&quot;"/>
+            <field name="context">{
+              'default_model': 'res.users', 
+              'default_res_id': uid, 
+              'search_default_message_read': True
+            }</field>
+            <field name="params" eval="&quot;{
+                'domain': [('notification_ids.partner_id.user_ids', 'in', [uid])],
+                'view_mailbox': True,
+                'read_action': 'unread', }&quot;"/>
+            <field name="help" type="html">
+                <p>
+                    No message found.
+                </p>
+            </field>
         </record>
 
         <record id="action_mail_sent_feeds" model="ir.actions.client">
             <field name="name">Sent</field>
             <field name="tag">mail.wall</field>
-            <field name="params" eval="&quot;{'domain': [('author_id.user_ids', 'in', [uid])],
-                'context': {'default_model': 'res.users', 'default_res_id': uid, 'typeof_thread': 'send'} }&quot;"/>
+            <field name="context">{
+              'default_model': 'res.users', 
+              'default_res_id': uid
+            }</field>
+            <field name="params" eval="&quot;{
+                'domain': [('author_id.user_ids', 'in', [uid])],
+                'view_mailbox': True, }&quot;"/>
+            <field name="help" type="html">
+                <p>
+                    <b>No message sent yet.</b>
+                </p><p>
+                    Click on the top-right icon to compose a message. This
+                    message will be sent by email if it's an internal contact.
+                </p>
+            </field>
         </record>
 
         <!-- MENU -->
 
         <!-- Top menu item -->
-        <menuitem name="Emails"
+        <menuitem name="Messaging"
           id="mail.mail_feeds_main"
           groups="base.group_user"
           sequence="10"/>
 
         <!-- Left-side menu: Feeds -->
-        <menuitem id="mail.mail_feeds" name="Messages" parent="mail.mail_feeds_main" groups="base.group_user" sequence="10"/>
+        <menuitem id="mail.mail_feeds" name="Messaging" parent="mail.mail_feeds_main" groups="base.group_user" sequence="10"/>
         <menuitem id="mail_my_stuff" name="Organizer" parent="mail.mail_feeds_main"/>
 
         <record id="mail_inboxfeeds" model="ir.ui.menu">
           <field name="parent_id" ref="mail.mail_feeds"/>
         </record>
         <record id="mail_starfeeds" model="ir.ui.menu">
-          <field name="name">Favorites</field>
+          <field name="name">Todo</field>
           <field name="sequence" eval="14"/>
           <field name="action" ref="action_mail_star_feeds"/>
           <field name="parent_id" ref="mail.mail_feeds"/>
index af3cc3e..18d19a8 100644 (file)
@@ -1,7 +1,36 @@
+/* ------------ TOPBAR MAIL BUTTON --------------- */
+
+/* FIXME this css is not very pretty because it uses a 
+ * 'button' element wich comes with a lot of inappropriate 
+ * styling. Entypo is also a headache to center properly
+ * */
+
+.openerp .oe_topbar_item.oe_topbar_compose_full_email{ 
+    padding: 0px;
+    width: 32px;
+    height: 32px;
+}
+.openerp .oe_topbar_item.oe_topbar_compose_full_email button{
+    position: relative;
+    top: -3px;  /* centering entypo ... urgh */
+    box-sizing: border-box;
+    border: none;
+    box-shadow: none;
+    color: white;
+    background: none;
+    text-shadow: 0px 1px 2px black;
+    width: 32px;
+    height: 32px;
+    padding: 0px;
+    margin: 0px
+    border-radius: 0px;
+}
 
 /* ------------ MAIL WIDGET --------------- */
 .openerp .oe_mail, .openerp .oe_mail *{
-    box-sizing: border-box;
+    -webkit-box-sizing: border-box;
+       -moz-box-sizing: border-box;
+            box-sizing: border-box;
 }
 .openerp .oe_mail {
     display: block;
 }
 .openerp .oe_mail .oe_msg .oe_msg_footer{
     padding-left: 4px;
+    padding-top: 3px;
     overflow: hidden;
-    opacity:0.8;
-    -webkit-transition: opacity 0.2s linear;
+    margin-bottom: 4px;
+    font-size: 11px;
 }
 .openerp .oe_mail .oe_msg .oe_msg_content{
     display: block;
 .openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_content{
     padding-top:2px;
 }
+.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_footer{
+    margin-bottom: 0px;
+}
+
 /* b) Votes (likes) */
+
 .openerp .oe_mail .oe_mail_vote_count{
     display: inline;
     position: relative;
-    background: #7C7BAD;
-    color: white;
+    background: white;
+    box-shadow: 0px 0px 0px 1px rgba(124, 123, 173, 0.36) inset;
+    color: #7c7bad;
     text-shadow: none;
     border-radius: 3px;
     margin: 0px;
     padding-left: 3px;
-    padding-right: 18px;
-    margin-right: 3px;
+    padding-right: 15px;
+    margin-right: 5px;
 }
 .openerp .oe_mail .oe_mail_vote_count .oe_e{
     position: absolute;
 
 /* c) Message action icons */
 
-.openerp .oe_mail .oe_msg.oe_msg_unread .oe_unread{
-    display:none;
-}
-.openerp .oe_mail .oe_msg.oe_msg_read .oe_read{
-    display:none;
-}
 .openerp .oe_mail .oe_msg .oe_msg_icons{
     float: right;
     margin-top: 4px;
     margin-left: 8px;
     height: 24px;
     -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
 }
 .openerp .oe_mail .oe_msg .oe_msg_icons span{
     float:right;
     color: #FFF;
     text-shadow: 0px 1px #AAA,0px -1px #AAA, -1px 0px #AAA, 1px 0px #AAA, 0px 3px 3px rgba(0,0,0,0.1);
     -webkit-transition: all 0.2s linear;
+       -moz-transition: all 0.2s linear;
+         -o-transition: all 0.2s linear;
+            transition: all 0.2s linear;
 }
 .openerp .oe_mail .oe_msg:hover .oe_msg_icons a{
     opacity: 1;
     -webkit-transition: all 0.1s linear;
+       -moz-transition: all 0.1s linear;
+         -o-transition: all 0.1s linear;
+            transition: all 0.1s linear;
 }
 .openerp .oe_mail .oe_msg .oe_msg_icons .oe_star:hover a{
     color: #FFF6C0;
 }
 .openerp .oe_mail .oe_msg .oe_msg_content textarea{
     width: 100%;
-    height: 32px;
+    height: 64px;
     margin: 0px;
     padding: 0px;
     resize: vertical;
     width: 100%;
 }
 
-/* ---------------- MESSAGE QUICK COMPOSER --------------- */
+/* --------------------- ATTACHMENTS --------------------- */
 
-.openerp .oe_mail .oe_msg_composer .oe_msg_footer{
-    padding-right:4px;
-    padding-top: 2px;
-    padding-bottom:6px;
+.openerp .oe_mail .oe_msg_attachment_list{
+    display: none;
+    margin-top: 12px;
+    margin-bottom: 12px;
 }
-.openerp .oe_mail .oe_msg_attachments.oe_hidden,
-.openerp .oe_mail .oe_msg_images.oe_hidden{
-    margin:0px;
-    border: none;
+.openerp .oe_mail .oe_msg_composer .oe_msg_attachment_list{
+    display: block;
+}
+.openerp .oe_mail .oe_attachment{
+    display: inline-block;
+    width: 100px;
+    margin: 2px;
+    min-height: 80px;
+    position: relative;
+    border-radius: 3px;
+    text-align: center;
+    vertical-align: top;
+}
+.openerp .oe_mail .oe_attachment .oe_name{
+    display: inline-block;
+    max-width: 100%;
+    padding: 1px 3px;
+    margin-top: 50px;
+    margin-bottom: 5px;
+    background: rgba(124, 123, 173, 0.13);
+    overflow: hidden;
+    color: #4c4c4c;
+    text-shadow: none;
+    border-radius: 3px;
+}
+
+.openerp .oe_mail .oe_attachment.oe_preview{
+    background: url( data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAJ0lEQVQYV2MsLS39z4AGLCws0IUYGIeCwrVr12J45sSJE5ieGQIKAbuZKf/EMCs7AAAAAElFTkSuQmCC );
+}
+.openerp .oe_mail .oe_attachment .oe_progress_bar{
     display: none;
+    position: absolute;
+    top: 18px;
+    left: 16px;
+    right: 16px;
+    height: 17px;
+    line-height: 13px;
+    padding: 0px;
+    background: #4BBD00;
+    color: white;
+    text-align: center;
+    border-radius: 3px;
+    border: solid 1px rgba(0,0,0,0.2);
+    box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.34);
+    -webkit-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
+       -moz-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
+         -o-animation: oe_mail_attach_loading_anim 0.75s infinite linear;
+            animation: oe_mail_attach_loading_anim 0.75s infinite linear;
+}
+.openerp .oe_mail .oe_attachment.oe_uploading .oe_progress_bar{
+    display: block;
 }
-.openerp .oe_mail .oe_msg_attachments{
-    margin-bottom: 4px;
-    margin-right: 0px;
-    font-size: 12px;
-    border-radius: 2px;
-    border: solid 1px rgba(124,123,173,0.14);
+@-webkit-keyframes oe_mail_attach_loading_anim{
+    0% { background: #4BBD00 }
+    50% { background: #009123 } 
+    100% { background: #4BBD00 }
 }
-.openerp .oe_mail .oe_msg_attachments .oe_attachment{
-    padding: 2px;
-    padding-left: 4px;
-    padding-right: 4px;
+@-moz-keyframes oe_mail_attach_loading_anim{
+    0% { background: #4BBD00 }
+    50% { background: #009123 } 
+    100% { background: #4BBD00 }
 }
-.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e{
-    font-size: 23px;
-    margin-top: -5px;
+@-o-keyframes oe_mail_attach_loading_anim{
+    0% { background: #4BBD00 }
+    50% { background: #009123 } 
+    100% { background: #4BBD00 }
 }
-.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e:hover{
-    text-decoration: none;
+@keyframes oe_mail_attach_loading_anim{
+    0% { background: #4BBD00 }
+    50% { background: #009123 } 
+    100% { background: #4BBD00 }
 }
-.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(odd){
-    background:white;
+.openerp .oe_mail .oe_attachment.oe_preview .oe_name{
+    position: absolute;
+    bottom: 0px;
+    margin: 0px;
+    left: 0px;
+    right: 0px;
+    max-height: 64px;
+    background: rgba(0,0,0,0.8); 
+    color: white;
+    border-top-left-radius: 0px;
+    border-top-right-radius: 0px;
+    opacity: 0;
+    -webkit-transition: opacity 0.2s linear; 
+       -moz-transition: opacity 0.2s linear; 
+         -o-transition: opacity 0.2s linear; 
+            transition: opacity 0.2s linear; 
+}
+.openerp .oe_mail .oe_attachment.oe_preview:hover .oe_name{
+    opacity: 1;
+    -webkit-transition: opacity 0.2s linear; 
+       -moz-transition: opacity 0.2s linear; 
+         -o-transition: opacity 0.2s linear; 
+            transition: opacity 0.2s linear; 
 }
-.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(even){
-    background: #F4F5FA;
+.openerp .oe_mail .oe_attachment img{
+    position: absolute;
+    width: 48px;
+    height: 48px;
+    top: 0px;
+    left: 50%;
+    margin-left: -24px;
+}
+.openerp .oe_mail .oe_attachment.oe_preview img{
+    display: block;
+    position: relative;
+    margin:0px;
+    width: 100px;
+    height: 100px;
+    border-radius: 3px;
+    margin-left: -50px;
+}
+.openerp .oe_mail .oe_attachment .oe_delete{
+    display: none;
+}
+.openerp .oe_mail .oe_msg_composer .oe_attachment .oe_delete{
+    display: block;
+    position: absolute;
+    top: -7px;
+    right: 0px;
+    color: black;
+    text-shadow: 1px 0px white, -1px 0px white, 0px 1px white, 0px -1px white;
+    cursor: pointer;
+    opacity: 0;
+    -webkit-transition: opacity 0.2s linear; 
+       -moz-transition: opacity 0.2s linear; 
+         -o-transition: opacity 0.2s linear; 
+            transition: opacity 0.2s linear; 
+}
+.openerp .oe_mail .oe_msg_composer .oe_attachment:hover .oe_delete{
+    opacity: 1;
+    -webkit-transition: opacity 0.2s linear; 
+       -moz-transition: opacity 0.2s linear; 
+         -o-transition: opacity 0.2s linear; 
+            transition: opacity 0.2s linear; 
+}
+/* ---------------- MESSAGE QUICK COMPOSER --------------- */
+
+.openerp .oe_mail .oe_msg_composer .oe_msg_footer{
+    padding-right:4px;
+    padding-top: 2px;
+    padding-bottom:6px;
 }
 .openerp .oe_mail .oe_msg_images {
     display: block;
 
 .openerp .oe_mail .oe_msg_content.oe_msg_more_message{
     text-align: right;
+    cursor: pointer;
 }
 .openerp .oe_mail .oe_msg_content.oe_msg_more_message .oe_separator{
     height: 0;
 }
 .openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more {
     background: white;
-    margin-right: 280px;
+    margin-right: 210px;
     padding-left: 8px;
     padding-right: 8px;
     text-decoration: none;
     padding-top: 5px;
     width: 160px;
     float: right;
+    margin-right: 16px;
 }
 
 /*  a) THE FOLLOW BUTTON */
     width:100%;
 }
 .openerp .oe_followers button.oe_follower.oe_following{
+    color: white;
     background-color: #3465A4;
     background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
+    background-image:    -moz-linear-gradient(top, #729FCF, #3465A4);
+    background-image:     -ms-linear-gradient(top, #729FCF, #3465A4);
+    background-image:      -o-linear-gradient(top, #729FCF, #3465A4);
+    background-image:         linear-gradient(to bottom, #729FCF, #3465A4);
+}
+.openerp .oe_followers button.oe_follower.oe_following:hover{
     color: white;
+    background-color: #A21A1A;
+    background-image: -webkit-linear-gradient(top, #DF3F3F, #A21A1A);
+    background-image:    -moz-linear-gradient(top, #DF3F3F, #A21A1A);
+    background-image:     -ms-linear-gradient(top, #DF3F3F, #A21A1A);
+    background-image:      -o-linear-gradient(top, #DF3F3F, #A21A1A);
+    background-image:         linear-gradient(to bottom, #DF3F3F, #A21A1A);
 }
 
 .openerp .oe_followers button.oe_follower .oe_follow, 
 
 .openerp .oe_record_thread{
     display: block;
-    margin-right: 180px;
+    margin-left: 16px;
+    margin-right: 212px;
 }
 
 /* ----------- INBOX INTEGRATION ----------- */
 
 .openerp .oe_mail_wall .oe_mail{
     margin: 16px;
-    width: 720px;
+    width: 600px;
+}
+
+.openerp .oe_mail .oe_view_nocontent > p {
+    padding-left: 15px;
 }
index b700cb8..3146247 100644 (file)
   min-height: 120px;
 }
 
-.oe_group_details a, .oe_group_details a:hover {
-  font-weight: bold;
-  color: #4c4c4c;
-}
-
 .oe_group_details h4 {
   margin: 0;
   font-size: 13px;
 }
 
-.oe_group_details h4 a {
-  color: #4c4c4c;
-}
-
-.oe_group_details h4 a:hover {
-  text-decoration: underline;
-}
-
 .oe_group_details ul {
   margin: 3px 0 5px;
   padding: 0;
   list-style: none;
 }
 
-.oe_group_details li {
+.openerp .oe_group_details li {
   margin: 2px 0;
 }
+
+.openerp .oe_group_button {
+    padding-top: 7px;
+}
+
+.openerp .oe_group_button .oe_group_join {
+    color: white;
+    background-color: #3465A4;
+    background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
+    background-image:    -moz-linear-gradient(top, #729FCF, #3465A4);
+    background-image:     -ms-linear-gradient(top, #729FCF, #3465A4);
+    background-image:      -o-linear-gradient(top, #729FCF, #3465A4);
+    background-image:         linear-gradient(to bottom, #729FCF, #3465A4);
+}
index 1eb3c29..e214168 100644 (file)
@@ -26,7 +26,8 @@ openerp.mail = function (session) {
                     'default_use_template', 'default_partner_ids', 'default_model',
                     'default_res_id', 'default_content_subtype', , 'default_subject',
                     'default_body', 'active_id', 'lang', 'bin_raw', 'tz',
-                    'active_model', 'edi_web_url_view', 'active_ids']
+                    'active_model', 'edi_web_url_view', 'active_ids', 
+                    'default_attachment_ids']
                 for (var key in action.context) {
                     if (_.indexOf(context_keys, key) == -1) {
                         action.context[key] = null;
@@ -54,8 +55,8 @@ openerp.mail = function (session) {
     mail.ChatterUtils = {
 
         /* Get an image in /web/binary/image?... */
-        get_image: function (session, model, field, id) {
-            return session.prefix + '/web/binary/image?session_id=' + session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
+        get_image: function (session, model, field, id, resize) {
+            return session.prefix + '/web/binary/image?session_id=' + session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '') + '&resize=' + (resize ? encodeURIComponent(resize) : '');
         },
 
         /* Get the url of an attachment {'id': id} */
@@ -111,12 +112,267 @@ openerp.mail = function (session) {
             }
 
             return domain;
-        }
+        },
+
+        // inserts zero width space between each letter of a string so that
+        // the word will correctly wrap in html boxes smaller than the text
+        breakword: function(str){
+            var out = '';
+            if (!str) {
+                return str;
+            }
+            for(var i = 0, len = str.length; i < len; i++){
+                out += _.str.escapeHTML(str[i]) + '&#8203;';
+            }
+            return out;
+        },
+
+        // returns the file type of a file based on its extension 
+        // As it only looks at the extension it is quite approximative. 
+        filetype: function(url){
+            url = url.filename || url;
+            var tokens = url.split('.');
+            if(tokens.length <= 1){
+                return 'unknown';
+            }
+            var extension = tokens[tokens.length -1];
+            if(extension.length === 0){
+                return 'unknown';
+            }else{
+                extension = extension.toLowerCase();
+            }
+            var filetypes = {
+                'webimage':     ['png','jpg','jpeg','jpe','gif'], // those have browser preview
+                'image':        ['tif','tiff','tga',
+                                 'bmp','xcf','psd','ppm','pbm','pgm','pnm','mng',
+                                 'xbm','ico','icon','exr','webp','psp','pgf','xcf',
+                                 'jp2','jpx','dng','djvu','dds'],
+                'vector':       ['ai','svg','eps','vml','cdr','xar','cgm','odg','sxd'],
+                'print':        ['dvi','pdf','ps'],
+                'document':     ['doc','docx','odm','odt'],
+                'presentation': ['key','keynote','odp','pps','ppt'],
+                'font':         ['otf','ttf','woff','eot'],
+                'archive':      ['zip','7z','ace','apk','bzip2','cab','deb','dmg','gzip','jar',
+                                 'rar','tar','gz','pak','pk3','pk4','lzip','lz','rpm'],
+                'certificate':  ['cer','key','pfx','p12','pem','crl','der','crt','csr'],
+                'audio':        ['aiff','wav','mp3','ogg','flac','wma','mp2','aac',
+                                 'm4a','ra','mid','midi'],
+                'video':        ['asf','avi','flv','mkv','m4v','mpeg','mpg','mpe','wmv','mp4','ogm'],
+                'text':         ['txt','rtf','ass'],
+                'html':         ['html','xhtml','xml','htm','css'],
+                'disk':         ['iso','nrg','img','ccd','sub','cdi','cue','mds','mdx'],
+                'script':       ['py','js','c','cc','cpp','cs','h','java','bat','sh',
+                                 'd','rb','pl','as','cmd','coffee','m','r','vbs','lisp'],
+                'spreadsheet':  ['123','csv','ods','numbers','sxc','xls','vc','xlsx'],
+                'binary':       ['exe','com','bin','app'],
+            };
+            for(filetype in filetypes){
+                var ext_list = filetypes[filetype];
+                for(var i = 0, len = ext_list.length; i < len; i++){
+                    if(extension === ext_list[i]){
+                        return filetype;
+                    }
+                }
+            }
+            return 'unknown';
+        },
+
     };
 
 
     /**
      * ------------------------------------------------------------
+     * MessageCommon
+     * ------------------------------------------------------------
+     * 
+     * Common base for expandables, chatter messages and composer. It manages
+     * the various variables common to those models.
+     */
+
+    mail.MessageCommon = session.web.Widget.extend({
+
+    /**
+     * ------------------------------------------------------------
+     * FIXME: this comment was moved as is from the ThreadMessage Init as
+     * part of a refactoring. Check that it is still correct
+     * ------------------------------------------------------------
+     * This widget handles the display of a messages in a thread. 
+     * Displays a record and performs some formatting on the record :
+     * - record.date: formatting according to the user timezone
+     * - record.timerelative: relative time givein by timeago lib
+     * - record.avatar: image url
+     * - record.attachment_ids[].url: url of each attachmentThe
+     * thread view :
+     * - root thread
+     * - - sub message (parent_id = root message)
+     * - - - sub thread
+     * - - - - sub sub message (parent id = sub thread)
+     * - - sub message (parent_id = root message)
+     * - - - sub thread
+     */
+        
+        init: function (parent, datasets, options) {
+            this._super(parent, options);
+
+            // record options
+            this.options = datasets.options || options || {};
+            // record domain and context
+            this.domain = datasets.domain || options.domain || [];
+            this.context = _.extend({
+                default_model: false,
+                default_res_id: 0,
+                default_parent_id: false }, options.context || {});
+
+            // data of this message
+            this.id = datasets.id ||  false,
+            this.last_id = this.id,
+            this.model = datasets.model || this.context.default_model || false,
+            this.res_id = datasets.res_id || this.context.default_res_id ||  false,
+            this.parent_id = datasets.parent_id ||  false,
+            this.type = datasets.type ||  false,
+            this.is_author = datasets.is_author ||  false,
+            this.is_private = datasets.is_private ||  false,
+            this.subject = datasets.subject ||  false,
+            this.name = datasets.name ||  false,
+            this.record_name = datasets.record_name ||  false,
+            this.body = datasets.body ||  false,
+            this.vote_nb = datasets.vote_nb || 0,
+            this.has_voted = datasets.has_voted ||  false,
+            this.is_favorite = datasets.is_favorite ||  false,
+            this.thread_level = datasets.thread_level ||  0,
+            this.to_read = datasets.to_read || false,
+            this.author_id = datasets.author_id || false,
+            this.attachment_ids = datasets.attachment_ids ||  [],
+            this.partner_ids = datasets.partner_ids || [];
+            this._date = datasets.date;
+
+            this.format_data();
+
+            // record options and data
+            this.show_record_name = this.record_name && !this.thread_level && this.model != 'res.partner';
+            this.options.show_read = false;
+            this.options.show_unread = false;
+            if (this.options.show_read_unread_button) {
+                if (this.options.read_action == 'read') this.options.show_read = true;
+                else if (this.options.read_action == 'unread') this.options.show_unread = true;
+                else {
+                    this.options.show_read = this.to_read;
+                    this.options.show_unread = !this.to_read;
+                    this.options.rerender = true;
+                    this.options.toggle_read = true;
+                }
+            }
+            this.parent_thread = parent.messages != undefined ? parent : this.options.root_thread;
+            this.thread = false;
+        },
+
+        /* Convert date, timerelative and avatar in displayable data. */
+        format_data: function () {
+
+            //formating and add some fields for render
+            if (this._date) {
+                this.date = session.web.format_value(this._date, {type:"datetime"});
+                this.timerelative = $.timeago(this.date);
+            } 
+            if (this.type == 'email' && (!this.author_id || !this.author_id[0])) {
+                this.avatar = ('/mail/static/src/img/email_icon.png');
+            } else if (this.author_id && this.template != 'mail.compose_message') {
+                this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]);
+            } else {
+                this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
+            }
+
+        },
+
+
+        /* upload the file on the server, add in the attachments list and reload display
+         */
+        display_attachments: function () {
+            for (var l in this.attachment_ids) {
+                var attach = this.attachment_ids[l];
+                if (!attach.formating) {
+                    attach.url = mail.ChatterUtils.get_attachment_url(this.session, attach);
+                    attach.filetype = mail.ChatterUtils.filetype(attach.filename);
+                    attach.name = mail.ChatterUtils.breakword(attach.name || attach.filename);
+                    attach.formating = true;
+                }
+            }
+            this.$(".oe_msg_attachment_list").html( session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) );
+        },
+
+        /* return the link to resized image
+         */
+        attachments_resize_image: function (id, resize) {
+            return mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', id, resize);
+        },
+
+        /* get all child message id linked.
+         * @return array of id
+        */
+        get_child_ids: function () {
+            return _.map(this.get_childs(), function (val) { return val.id; });
+        },
+
+        /* get all child message linked.
+         * @return array of message object
+        */
+        get_childs: function (nb_thread_level) {
+            var res=[];
+            if (arguments[1] && this.id) res.push(this);
+            if ((isNaN(nb_thread_level) || nb_thread_level>0) && this.thread) {
+                _(this.thread.messages).each(function (val, key) {
+                    res = res.concat( val.get_childs((isNaN(nb_thread_level) ? undefined : nb_thread_level-1), true) );
+                });
+            }
+            return res;
+        },
+
+        /**
+         * search a message in all thread and child thread.
+         * This method return an object message.
+         * @param {object}{int} option.id
+         * @param {object}{string} option.model
+         * @param {object}{boolean} option._go_thread_wall
+         *      private for check the top thread
+         * @return thread object
+         */
+        browse_message: function (options) {
+            // goto the wall thread for launch browse
+            if (!options._go_thread_wall) {
+                options._go_thread_wall = true;
+                for (var i in this.options.root_thread.messages) {
+                    var res=this.options.root_thread.messages[i].browse_message(options);
+                    if (res) return res;
+                }
+            }
+
+            if (this.id==options.id)
+                return this;
+
+            for (var i in this.thread.messages) {
+                if (this.thread.messages[i].thread) {
+                    var res=this.thread.messages[i].browse_message(options);
+                    if (res) return res;
+                }
+            }
+
+            return false;
+        },
+
+        /**
+         * call on_message_delete on his parent thread
+        */
+        destroy: function () {
+
+            this._super();
+            this.parent_thread.on_message_detroy(this);
+
+        }
+
+    });
+
+    /**
+     * ------------------------------------------------------------
      * ComposeMessage widget
      * ------------------------------------------------------------
      * 
@@ -126,7 +382,7 @@ openerp.mail = function (session) {
      * When the user focuses the textarea, the compose message is instantiated.
      */
     
-    mail.ThreadComposeMessage = session.web.Widget.extend({
+    mail.ThreadComposeMessage = mail.MessageCommon.extend({
         template: 'mail.compose_message',
 
         /**
@@ -138,45 +394,22 @@ openerp.mail = function (session) {
          */
 
         init: function (parent, datasets, options) {
-            var self = this;
-            this._super(parent);
-            this.context = options.context || {};
-            this.options = options.options;
-
+            this._super(parent, datasets, options);
             this.show_compact_message = false;
+            this.show_delete_attachment = true;
+        },
 
-            // data of this compose message
-            this.attachment_ids = [];
-            this.id = datasets.id;
-            this.model = datasets.model;
-            this.res_model = datasets.res_model;
-            this.is_private = datasets.is_private || false;
-            this.partner_ids = datasets.partner_ids || [];
-            this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
-            this.thread_level = datasets.thread_level;
-            this.parent_thread= parent.messages!= undefined ? parent : false;
+        start: function () {
+            this._super.apply(this, arguments);
 
             this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment');
-            this.show_delete_attachment = true;
-
             this.fileupload_id = _.uniqueId('oe_fileupload_temp');
-            $(window).on(self.fileupload_id, self.on_attachment_loaded);
-        },
+            $(window).on(this.fileupload_id, this.on_attachment_loaded);
 
-        start: function () {
             this.display_attachments();
             this.bind_events();
         },
 
-        /* upload the file on the server, add in the attachments list and reload display
-         */
-        display_attachments: function () {
-            this.$(".oe_msg_attachment_list").html( 
-                session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) );
-            // event: delete an attachment
-            this.$(".oe_msg_attachment_list").on('click', '.oe_mail_attachment_delete', this.on_attachment_delete);
-        },
-
         /* when a user click on the upload button, send file read on_attachment_loaded
         */
         on_attachment_change: function (event) {
@@ -263,24 +496,31 @@ openerp.mail = function (session) {
             this.$('textarea.oe_compact').on('focus', _.bind( this.on_compose_expandable, this));
 
             // set the function called when attachments are added
-            this.$el.on('change', 'input.oe_form_binary_file', _.bind( this.on_attachment_change, this) );
+            this.$('input.oe_form_binary_file').on('change', _.bind( this.on_attachment_change, this) );
 
-            this.$el.on('click', '.oe_cancel', _.bind( this.on_cancel, this) );
-            this.$el.on('click', '.oe_post', _.bind( this.on_message_post, this) );
-            this.$el.on('click', '.oe_full', _.bind( this.on_compose_fullmail, this, 'reply') );
+            this.$('.oe_cancel').on('click', _.bind( this.on_cancel, this) );
+            this.$('.oe_post').on('click', _.bind( this.on_message_post, this) );
+            this.$('.oe_full').on('click', _.bind( this.on_compose_fullmail, this, this.id ? 'reply' : 'comment') );
 
             /* stack for don't close the compose form if the user click on a button */
-            this.$el.on('mousedown', '.oe_msg_footer', _.bind( function () { this.stay_open = true; }, this));
-            this.$('textarea:not(.oe_compact):first').on('focus, mouseup, keydown', _.bind( function () { this.stay_open = false; }, this));
-            this.$('textarea:not(.oe_compact):first').autosize();
+            this.$('.oe_msg_footer').on('mousedown', _.bind( function () { this.stay_open = true; }, this));
+            var ev_stay = {};
+            ev_stay.mouseup = ev_stay.keydown = ev_stay.focus = function () { self.stay_open = false; };
+            this.$('textarea:not(.oe_compact)').on(ev_stay);
+            this.$('textarea:not(.oe_compact)').autosize();
 
             // auto close
-            this.$el.on('blur', 'textarea:not(.oe_compact):first', _.bind( this.on_compose_expandable, this));
+            this.$('textarea:not(.oe_compact)').on('blur', _.bind( this.on_compose_expandable, this));
+
+            // event: delete child attachments off the oe_msg_attachment_list box
+            this.$(".oe_msg_attachment_list").on('click', '.oe_delete', this.on_attachment_delete);
         },
 
         on_compose_fullmail: function (default_composition_mode) {
             if (default_composition_mode == 'reply') {
                 var context = {
+                    'default_model': this.context.default_model,
+                    'default_res_id': this.context.default_res_id,
                     'default_composition_mode': default_composition_mode,
                     'default_parent_id': this.id,
                     'default_body': mail.ChatterUtils.get_text2html(this.$el ? (this.$el.find('textarea:not(.oe_compact)').val() || '') : ''),
@@ -357,10 +597,15 @@ openerp.mail = function (session) {
                         this.context.default_parent_id, 
                         attachments,
                         this.parent_thread.context
-                    ]).then(function (record) {
+                    ]).done(function (record) {
                         var thread = self.parent_thread;
+
+                        if (self.options.display_indented_thread < self.thread_level && thread.parent_message) {
+                            thread = thread.parent_message.parent_thread;
+                        }
                         // create object and attach to the thread object
-                        thread.message_fetch(false, false, [record], function (arg, data) {
+                        thread.message_fetch([['id', 'child_of', [self.id]]], false, [record], function (arg, data) {
+                            data[0].no_sorted = true;
                             var message = thread.create_message_object( data[0] );
                             // insert the message on dom
                             thread.insert_message( message, self.$el );
@@ -419,30 +664,16 @@ openerp.mail = function (session) {
      * - - visible message
      * - - expandable
      */
-    mail.ThreadExpandable = session.web.Widget.extend({
+    mail.ThreadExpandable = mail.MessageCommon.extend({
         template: 'mail.thread.expandable',
 
-        init: function (parent, datasets, context) {
-            this._super(parent);
-            this.domain = datasets.domain || [];
-            this.options = datasets.options;
-            this.context = _.extend({
-                default_model: 'mail.thread',
-                default_res_id: 0,
-                default_parent_id: false }, context || {});
-
-            // data of this expandable message
-            this.id = datasets.id || -1,
-            this.model = datasets.model || false,
-            this.parent_id = datasets.parent_id || false,
-            this.nb_messages = datasets.nb_messages || 0,
-            this.thread_level = datasets.thread_level || 0,
-            this.type = 'expandable',
-            this.max_limit = this.id < 0 || false,
-            this.flag_used = false,
-            this.parent_thread= parent.messages!= undefined ? parent : this.options._parents[0];
+        init: function (parent, datasets, options) {
+            this._super(parent, datasets, options);
+            this.type = 'expandable';
+            this.max_limit = datasets.max_limit;
+            this.nb_messages = datasets.nb_messages;
+            this.flag_used = false;
         },
-
         
         start: function () {
             this._super.apply(this, arguments);
@@ -460,7 +691,7 @@ openerp.mail = function (session) {
          * Bind events in the widget. Each event is slightly described
          * in the function. */
         bind_events: function () {
-            this.$el.on('click', 'a.oe_msg_fetch_more', this.on_expandable);
+            this.$('.oe_msg_more_message').on('click', this.on_expandable);
         },
 
         animated_destroy: function (fadeTime) {
@@ -480,154 +711,36 @@ openerp.mail = function (session) {
             }
             this.flag_used = true;
 
-            this.animated_destroy(200);
-            this.parent_thread.message_fetch(this.domain, this.context);
-            return false;
-        },
+            var self = this;
 
-        /**
-         * call on_message_delete on his parent thread
-        */
-        destroy: function () {
+            // read messages
+            self.parent_thread.message_fetch(this.domain, this.context, false, function (arg, data) {
+                // insert the message on dom after this message
+                self.id = false;
+                self.parent_thread.switch_new_message( data, self.$el );
+                self.animated_destroy(200);
+            });
 
-            this._super();
-            this.parent_thread.on_message_detroy(this);
+            return false;
+        },
 
-        }
     });
 
-    /**
-     * ------------------------------------------------------------
-     * Thread Message Widget
-     * ------------------------------------------------------------
-     * This widget handles the display of a messages in a thread. 
-     * Displays a record and performs some formatting on the record :
-     * - record.date: formatting according to the user timezone
-     * - record.timerelative: relative time givein by timeago lib
-     * - record.avatar: image url
-     * - record.attachment_ids[].url: url of each attachmentThe
-     * thread view :
-     * - root thread
-     * - - sub message (parent_id = root message)
-     * - - - sub thread
-     * - - - - sub sub message (parent id = sub thread)
-     * - - sub message (parent_id = root message)
-     * - - - sub thread
-     */
-    mail.ThreadMessage = session.web.Widget.extend({
+    mail.ThreadMessage = mail.MessageCommon.extend({
         template: 'mail.thread.message',
 
-        /**
-         * @param {Object} parent parent
-         * @param {Array} [domain]
-         * @param {Object} [context] context of the thread. It should
-            contain at least default_model, default_res_id. Please refer to
-            the ComposeMessage widget for more information about it.
-         * @param {Object} [options]
-         *      @param {Object} [thread] read obout mail.Thread object
-         *      @param {Object} [message]
-         *          @param {Number} [truncate_limit=250] number of character to
-         *              display before having a "show more" link; note that the text
-         *              will not be truncated if it does not have 110% of the parameter
-         *          @param {Boolean} [show_record_name]
-         *...  @param {int} [show_reply_button] number thread level to display the reply button
-         *...  @param {int} [show_read_unread_button] number thread level to display the read/unread button
-         */
-        init: function (parent, datasets, context) {
-            this._super(parent);
-
-            // record domain and context
-            this.domain = datasets.domain || [];
-            this.context = _.extend({
-                default_model: 'mail.thread',
-                default_res_id: 0,
-                default_parent_id: false }, context || {});
-
-            // record options
-            this.options = datasets.options;
-
-            // data of this message
-            this.id = datasets.id ||  -1,
-            this.model = datasets.model ||  false,
-            this.parent_id = datasets.parent_id ||  false,
-            this.res_id = datasets.res_id ||  false,
-            this.type = datasets.type ||  false,
-            this.is_author = datasets.is_author ||  false,
-            this.is_private = datasets.is_private ||  false,
-            this.subject = datasets.subject ||  false,
-            this.name = datasets.name ||  false,
-            this.record_name = datasets.record_name ||  false,
-            this.body = datasets.body ||  false,
-            this.vote_nb = datasets.vote_nb || 0,
-            this.has_voted = datasets.has_voted ||  false,
-            this.is_favorite = datasets.is_favorite ||  false,
-            this.thread_level = datasets.thread_level ||  0,
-            this.to_read = datasets.to_read || false,
-            this.author_id = datasets.author_id ||  [],
-            this.attachment_ids = datasets.attachment_ids ||  [],
-            this._date = datasets.date;
-
-
-            this.show_reply_button = this.options.show_compose_message && this.options.show_reply_button > this.thread_level;
-            this.show_read_unread_button = this.options.show_read_unread_button > this.thread_level;
-
-            // record options and data
-            this.parent_thread= parent.messages!= undefined ? parent : this.options._parents[0];
-            this.thread = false;
-
-            if ( this.id > 0 ) {
-                this.formating_data();
-            }
-
-            this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
-            this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
-            this.ds_follow = new session.web.DataSetSearch(this, 'mail.followers');
-        },
-
-        /* Convert date, timerelative and avatar in displayable data. */
-        formating_data: function () {
-
-            //formating and add some fields for render
-            this.date = session.web.format_value(this._date, {type:"datetime"});
-            this.timerelative = $.timeago(this.date);
-            if (this.type == 'email') {
-                this.avatar = ('/mail/static/src/img/email_icon.png');
-            } else {
-                this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]);
-            }
-            for (var l in this.attachment_ids) {
-                var attach = this.attachment_ids[l];
-                attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
-
-                if ((attach.filename || attach.name).match(/[.](jpg|jpg|gif|png|tif|svg)$/i)) {
-                    attach.is_image = true;
-                    attach['url'] = mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', attach.id); 
-                }
-            }
-        },
         
         start: function () {
             this._super.apply(this, arguments);
             this.expender();
-            this.$el.hide().fadeIn(750, function () {$(this).css('display', '');});
-            this.resize_img();
             this.bind_events();
             if(this.thread_level < this.options.display_indented_thread) {
                 this.create_thread();
             }
             this.$('.oe_msg_attachments, .oe_msg_images').addClass("oe_hidden");
-        },
 
-        resize_img: function () {
-            var resize = function () {
-                var h = $(this).height();
-                var w = $(this).width();
-                if ( h > 100 || w >100 ) {
-                    var ratio = 100 / (h > w ? h : w);
-                    $(this).attr("width", parseInt( w*ratio )).attr("height", parseInt( h*ratio ));
-                }
-            };
-            this.$("img").load(resize).each(resize);
+            this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
+            this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
         },
 
         /**
@@ -635,30 +748,15 @@ openerp.mail = function (session) {
          * in the function. */
         bind_events: function () {
             var self = this;
+            // header icons bindings
+            this.$('.oe_read').on('click', this.on_message_read);
+            this.$('.oe_unread').on('click', this.on_message_unread);
+            this.$('.oe_msg_delete').on('click', this.on_message_delete);
+            this.$('.oe_reply').on('click', this.on_message_reply);
+            this.$('.oe_star').on('click', this.on_star);
+            this.$('.oe_msg_vote').on('click', this.on_vote);
+            this.$('.oe_view_attachments').on('click', this.on_view_attachments);
 
-            // event: click on 'Attachment(s)' in msg
-            this.$('.oe_mail_msg_view_attachments').on('click', function (event) {
-                var attach = self.$('.oe_msg_attachments:first, .oe_msg_images:first');
-                if ( self.$('.oe_msg_attachments:first').hasClass("oe_hidden") ) {
-                    attach.removeClass("oe_hidden");
-                } else {
-                    attach.addClass("oe_hidden");
-                }
-                self.resize_img();
-            });
-            // event: click on icone 'Read' in header
-            this.$el.on('click', '.oe_read', this.on_message_read_unread);
-            // event: click on icone 'UnRead' in header
-            this.$el.on('click', '.oe_unread', this.on_message_read_unread);
-            // event: click on 'Delete' in msg side menu
-            this.$el.on('click', '.oe_msg_delete', this.on_message_delete);
-
-            // event: click on 'Reply' in msg
-            this.$el.on('click', '.oe_reply', this.on_message_reply);
-            // event: click on 'Vote' button
-            this.$el.on('click', '.oe_msg_vote', this.on_vote);
-            // event: click on 'starred/favorite' button
-            this.$el.on('click', '.oe_star', this.on_star);
         },
 
         /* Call the on_compose_message on the thread of this message. */
@@ -673,7 +771,7 @@ openerp.mail = function (session) {
             this.$('.oe_msg_body:first').expander({
                 slicePoint: this.options.truncate_limit,
                 expandText: 'read more',
-                userCollapseText: '[^]',
+                userCollapseText: 'read less',
                 detailClass: 'oe_msg_tail',
                 moreClass: 'oe_mail_expand',
                 lessClass: 'oe_mail_reduce',
@@ -717,6 +815,17 @@ openerp.mail = function (session) {
             }
         },
 
+        /* Call the on_compose_message on the thread of this message. */
+        on_view_attachments:function (event) {
+            event.stopPropagation();
+            var self = this;
+            if (!this.toggle_attachment) {
+                self.display_attachments();
+                this.toggle_attachment = true;
+            } 
+            this.$('.oe_msg_attachment_list').toggle(200);
+        },
+
         /**
          * Wait a confirmation for delete the message on the DB.
          * Make an animate destroy
@@ -732,68 +841,76 @@ openerp.mail = function (session) {
             return false;
         },
 
-        /*The selected thread and all childs (messages/thread) became read
-        * @param {object} mouse envent
+        /* Check if the message must be destroy and detroy it or check for re render widget
+        * @param {callback} apply function
         */
-        on_message_read_unread: function (event) {
-            event.stopPropagation();
-            var self=this;
+        check_for_rerender: function () {
+            var self = this;
 
-            if ( (this.to_read && this.options.typeof_thread == 'inbox') ||
-                (!this.to_read && this.options.typeof_thread == 'archives')) {
-                this.animated_destroy(150);
-            }
+            var messages = [this].concat(this.get_childs());
+            var message_ids = _.map(messages, function (msg) { return msg.id;});
+            var domain = mail.ChatterUtils.expand_domain( this.options.root_thread.domain )
+                .concat([["id", "in", message_ids ]]);
+
+            return this.parent_thread.ds_message.call('message_read', [undefined, domain, [], !!this.parent_thread.options.display_indented_thread, this.context, this.parent_thread.id])
+                .then( function (records) {
+                    // remove message not loaded
+                    _.map(messages, function (msg) {
+                        if(!_.find(records, function (record) { return record.id == msg.id; })) {
+                            msg.animated_destroy(150);
+                        } else {
+                            msg.renderElement();
+                            msg.start()
+                        }
+                    });
 
-            // if this message is read, all childs message display is read
-            this.ds_notification.call('set_message_read', [ [this.id].concat( this.get_child_ids() ) , this.to_read, this.context]).pipe(function () {
-                self.$el.removeClass(self.to_read ? 'oe_msg_unread':'oe_msg_read').addClass(self.to_read ? 'oe_msg_read':'oe_msg_unread');
-                self.to_read = !self.to_read;
-            });
-            return false;
+                });
         },
 
-        /**
-         * search a message in all thread and child thread.
-         * This method return an object message.
-         * @param {object}{int} option.id
-         * @param {object}{string} option.model
-         * @param {object}{boolean} option._go_thread_wall
-         *      private for check the top thread
-         * @return thread object
-         */
-        browse_message: function (options) {
-            // goto the wall thread for launch browse
-            if (!options._go_thread_wall) {
-                options._go_thread_wall = true;
-                for (var i in this.options._parents[0].messages) {
-                    var res=this.options._parents[0].messages[i].browse_message(options);
-                    if (res) return res;
-                }
-            }
-
-            if (this.id==options.id)
-                return this;
-
-            for (var i in this.thread.messages) {
-                if (this.thread.messages[i].thread) {
-                    var res=this.thread.messages[i].browse_message(options);
-                    if (res) return res;
-                }
-            }
+        on_message_read: function (event) {
+            event.stopPropagation();
+            this.on_message_read_unread(true);
+            return false;
+        },
 
+        on_message_unread: function (event) {
+            event.stopPropagation();
+            this.on_message_read_unread(false);
             return false;
         },
 
-        /* get all child message id linked.
-         * @return array of id
+        /*The selected thread and all childs (messages/thread) became read
+        * @param {object} mouse envent
         */
-        get_child_ids: function () {
-            var res=[]
-            if (arguments[0]) res.push(this.id);
-            if (this.thread) {
-                res = res.concat( this.thread.get_child_ids(true) );
+        on_message_read_unread: function (read_value) {
+            var self = this;
+            var messages = [this].concat(this.get_childs());
+
+            // inside the inbox, when the user mark a message as read/done, don't apply this value
+            // for the stared/favorite message
+            if (this.options.view_inbox && read_value) {
+                var messages = _.filter(messages, function (val) { return !val.is_favorite && val.id; });
+                if (!messages.length) {
+                    this.check_for_rerender();
+                    return false;
+                }
             }
-            return res;
+            var message_ids = _.map(messages, function (val) { return val.id; });
+
+            this.ds_notification.call('set_message_read', [message_ids, read_value, this.context])
+                .then(function () {
+                    // apply modification
+                    _.each(messages, function (msg) {
+                        msg.to_read = !read_value;
+                        if (msg.options.toggle_read) {
+                            msg.options.show_read = msg.to_read;
+                            msg.options.show_unread = !msg.to_read;
+                        }
+                    });
+                    // check if the message must be display, destroy or rerender
+                    self.check_for_rerender();
+                });
+            return false;
         },
 
         /**
@@ -801,12 +918,13 @@ openerp.mail = function (session) {
         */
         on_vote: function (event) {
             event.stopPropagation();
-            var self=this;
-            return this.ds_message.call('vote_toggle', [[self.id]]).pipe(function (vote) {
-                self.has_voted = vote;
-                self.vote_nb += self.has_voted ? 1 : -1;
-                self.display_vote();
-            });
+            this.ds_message.call('vote_toggle', [[this.id]])
+                .then(
+                    _.bind(function (vote) {
+                        this.has_voted = vote;
+                        this.vote_nb += this.has_voted ? 1 : -1;
+                        this.display_vote();
+                    }, this));
             return false;
         },
 
@@ -814,10 +932,10 @@ openerp.mail = function (session) {
          * Display the render of this message's vote
         */
         display_vote: function () {
-            var self = this;
-            var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': self});
-            self.$(".oe_msg_vote:first").remove();
-            self.$(".oe_mail_vote_count:first").replaceWith(vote_element);
+            var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': this});
+            this.$(".oe_msg_footer:first .oe_mail_vote_count").remove();
+            this.$(".oe_msg_footer:first .oe_msg_vote").replaceWith(vote_element);
+            this.$('.oe_msg_vote').on('click', this.on_vote);
         },
 
         /**
@@ -827,34 +945,29 @@ openerp.mail = function (session) {
             event.stopPropagation();
             var self=this;
             var button = self.$('.oe_star:first');
-            return this.ds_message.call('favorite_toggle', [[self.id]]).pipe(function (star) {
-                self.is_favorite=star;
-                if (self.is_favorite) {
-                    button.addClass('oe_starred');
-                } else {
-                    button.removeClass('oe_starred');
-                    if ( self.options.typeof_thread == 'stared' ) {
-                        self.animated_destroy(150);
+
+            this.ds_message.call('favorite_toggle', [[self.id]])
+                .then(function (star) {
+                    self.is_favorite=star;
+                    if (self.is_favorite) {
+                        button.addClass('oe_starred');
+                    } else {
+                        button.removeClass('oe_starred');
                     }
-                }
-            });
+
+                    if (self.options.view_inbox && self.is_favorite) {
+                        self.on_message_read_unread(true);
+                    } else {
+                        self.check_for_rerender();
+                    }
+                });
             return false;
         },
 
-        /**
-         * call on_message_delete on his parent thread
-        */
-        destroy: function () {
-
-            this._super();
-            this.parent_thread.on_message_detroy(this);
-
-        }
-
     });
 
     /**
-         * ------------------------------------------------------------
+     * ------------------------------------------------------------
      * Thread Widget
      * ------------------------------------------------------------
      *
@@ -881,14 +994,11 @@ openerp.mail = function (session) {
          *      @param {Object} [thread]
          *          @param {int} [display_indented_thread] number thread level to indented threads.
          *              other are on flat mode
-         *          @param {Select} [typeof_thread] inbox/archives/stared/sent
-         *              type of thread and option for user application like animate
-         *              destroy for read/unread
          *          @param {Array} [parents] liked with the parents thread
          *              use with browse, fetch... [O]= top parent
          */
         init: function (parent, datasets, options) {
-            this._super(parent);
+            this._super(parent, options);
             this.domain = options.domain || [];
             this.context = _.extend({
                 default_model: 'mail.thread',
@@ -896,21 +1006,24 @@ openerp.mail = function (session) {
                 default_parent_id: false }, options.context || {});
 
             this.options = options.options;
-            this.options._parents = (options.options._parents != undefined ? options.options._parents : []).concat( [this] );
-
+            this.options.root_thread = (options.options.root_thread != undefined ? options.options.root_thread : this);
+            this.options.show_compose_message = this.options.show_compose_message && (this.options.display_indented_thread >= this.thread_level || !this.thread_level);
+            
             // record options and data
             this.parent_message= parent.thread!= undefined ? parent : false ;
 
             // data of this thread
             this.id =  datasets.id || false,
-            this.model =  datasets.model || false,
+            this.last_id =  datasets.last_id || false,
             this.parent_id =  datasets.parent_id || false,
+
             this.is_private =  datasets.is_private || false,
             this.author_id =  datasets.author_id || false,
             this.thread_level =  (datasets.thread_level+1) || 0,
             this.partner_ids =  _.filter(datasets.partner_ids, function (partner) { return partner[0]!=datasets.author_id[0]; } ) 
             this.messages = [];
-            this.show_compose_message = this.options.show_compose_message && (this.options.show_reply_button > this.thread_level || !this.thread_level);
+
+            this.options.flat_mode = !!(this.options.display_indented_thread > this.thread_level ? this.options.display_indented_thread - this.thread_level : 0);
 
             // object compose message
             this.compose_message = false;
@@ -934,13 +1047,10 @@ openerp.mail = function (session) {
                     'context': this.context,
                     'options': this.options,
                 });
-                if (!this.thread_level) {
-                    // root view
+                if (!this.thread_level || this.thread_level > this.options.display_indented_thread) {
                     this.compose_message.insertBefore(this.$el);
-                } else if (this.thread_level > this.options.display_indented_thread) {
-                    this.compose_message.insertAfter(this.$el);
                 } else {
-                    this.compose_message.appendTo(this.$el);
+                    this.compose_message.prependTo(this.$el);
                 }
             }
         },
@@ -948,21 +1058,20 @@ openerp.mail = function (session) {
         /* When the expandable object is visible on screen (with scrolling)
          * then the on_expandable function is launch
         */
-        on_scroll: function (event) {
-            if (event)event.stopPropagation();
-            this.$('.oe_msg_expandable:last');
-
-            var message = this.messages[this.messages.length-1];
-            if (message && message.type=="expandable" && message.max_limit) {
-                var pos = message.$el.position();
+        on_scroll: function () {
+            var expandables = 
+            _.each( _.filter(this.messages, function (val) {return val.max_limit && !val.parent_id;}), function (val) {
+                var pos = val.$el.position();
                 if (pos.top) {
                     /* bottom of the screen */
                     var bottom = $(window).scrollTop()+$(window).height()+200;
                     if (bottom > pos.top) {
-                        message.on_expandable();
+                        val.on_expandable();
+                        // load only one time
+                        val.loading = true;
                     }
                 }
-            }
+            });
         },
 
         /**
@@ -970,8 +1079,8 @@ openerp.mail = function (session) {
          * in the function. */
         bind_events: function () {
             var self = this;
-            self.$el.on('click', '.oe_mail_list_recipients .oe_more', self.on_show_recipients);
-            self.$el.on('click', '.oe_mail_compose_textarea .oe_more_hidden', self.on_hide_recipients);
+            self.$('.oe_mail_list_recipients .oe_more').on('click', self.on_show_recipients);
+            self.$('.oe_mail_compose_textarea .oe_more_hidden').on('click', self.on_hide_recipients);
         },
 
         /**
@@ -981,6 +1090,7 @@ openerp.mail = function (session) {
             var p=$(this).parent(); 
             p.find('.oe_more_hidden, .oe_hidden').show(); 
             p.find('.oe_more').hide(); 
+            return false;
         },
 
         /**
@@ -990,15 +1100,14 @@ openerp.mail = function (session) {
             var p=$(this).parent(); 
             p.find('.oe_more_hidden, .oe_hidden').hide(); 
             p.find('.oe_more').show(); 
+            return false;
         },
 
         /* get all child message/thread id linked.
          * @return array of id
         */
         get_child_ids: function () {
-            var res=[];
-            _(this.get_childs()).each(function (val, key) { res.push(val.id); });
-            return res;
+            return _.map(this.get_childs(), function (val) { return val.id; });
         },
 
         /* get all child message/thread linked.
@@ -1033,17 +1142,17 @@ openerp.mail = function (session) {
             // goto the wall thread for launch browse
             if (!options._go_thread_wall) {
                 options._go_thread_wall = true;
-                return this.options._parents[0].browse_thread(options);
+                return this.options.root_thread.browse_thread(options);
             }
 
-            if (this.id==options.id) {
+            if (this.id == options.id) {
                 return this;
             }
 
             if (options.id) {
                 for (var i in this.messages) {
                     if (this.messages[i].thread) {
-                        var res=this.messages[i].thread.browse_thread({'id':options.id, '_go_thread_wall':true});
+                        var res = this.messages[i].thread.browse_thread({'id':options.id, '_go_thread_wall':true});
                         if (res) return res;
                     }
                 }
@@ -1067,8 +1176,8 @@ openerp.mail = function (session) {
          * @return message object
          */
         browse_message: function (options) {
-            if (this.options._parents[0].messages[0])
-                return this.options._parents[0].messages[0].browse_message(options);
+            if (this.options.root_thread.messages[0])
+                return this.options.root_thread.messages[0].browse_message(options);
         },
 
         /**
@@ -1079,6 +1188,7 @@ openerp.mail = function (session) {
         on_compose_message: function () {
             this.instantiate_compose_message();
             this.compose_message.on_compose_expandable();
+            return false;
         },
 
         /**
@@ -1086,8 +1196,8 @@ openerp.mail = function (session) {
         */
         no_message: function () {
             var no_message = $(session.web.qweb.render('mail.wall_no_message', {}));
-            if (this.options.no_message) {
-                no_message.html(this.options.no_message);
+            if (this.options.help) {
+                no_message.html(this.options.help);
             }
             no_message.appendTo(this.$el);
         },
@@ -1101,18 +1211,20 @@ openerp.mail = function (session) {
          * @param {Array} ids read (if the are some ids, the method don't use the domain)
          */
         message_fetch: function (replace_domain, replace_context, ids, callback) {
-            var self = this;
-
-            // domain and context: options + additional
-            fetch_domain = replace_domain ? replace_domain : this.domain;
-            fetch_context = replace_context ? replace_context : this.context;
-            var message_loaded_ids = this.id ? [this.id].concat( self.get_child_ids() ) : self.get_child_ids();
-
-            // CHM note : option for sending in flat mode by server
-            var thread_level = this.options.display_indented_thread > this.thread_level ? this.options.display_indented_thread - this.thread_level : 0;
-
-            return this.ds_message.call('message_read', [ids, fetch_domain, message_loaded_ids, thread_level, fetch_context, this.context.default_parent_id || undefined])
-                .then(callback ? _.bind(callback, this, arguments) : this.proxy('switch_new_message'));
+            return this.ds_message.call('message_read', [
+                    // ids force to read
+                    ids == false ? undefined : ids, 
+                    // domain + additional
+                    (replace_domain ? replace_domain : this.domain), 
+                    // ids allready loaded
+                    (this.id ? [this.id].concat( this.get_child_ids() ) : this.get_child_ids()), 
+                    // option for sending in flat mode by server
+                    this.options.flat_mode, 
+                    // context + additional
+                    (replace_context ? replace_context : this.context), 
+                    // parent_id
+                    this.context.default_parent_id || undefined
+                ]).done(callback ? _.bind(callback, this, arguments) : this.proxy('switch_new_message'));
         },
 
         /**
@@ -1128,22 +1240,23 @@ openerp.mail = function (session) {
             data.options = _.extend(self.options, data.options);
 
             if (data.type=='expandable') {
-                var message = new mail.ThreadExpandable(self, data, {
+                var message = new mail.ThreadExpandable(self, data, {'context':{
                     'default_model': data.model || self.context.default_model,
                     'default_res_id': data.res_id || self.context.default_res_id,
                     'default_parent_id': self.id,
-                });
+                }});
             } else {
-                var message = new mail.ThreadMessage(self, data, {
+                var message = new mail.ThreadMessage(self, data, {'context':{
                     'default_model': data.model,
                     'default_res_id': data.res_id,
                     'default_parent_id': data.id,
-                });
+                }});
             }
 
             // check if the message is already create
             for (var i in self.messages) {
                 if (self.messages[i] && self.messages[i].id == message.id) {
+                    console.log('Reload message', message.id);
                     self.messages[i].destroy();
                 }
             }
@@ -1163,74 +1276,18 @@ openerp.mail = function (session) {
          */
         insert_message: function (message, dom_insert_after) {
             var self=this;
-
-            if (this.show_compose_message && this.options.show_compact_message) {
+            if (this.options.show_compact_message > this.thread_level) {
                 this.instantiate_compose_message();
                 this.compose_message.do_show_compact();
             }
 
-            this.$('.oe_wall_no_message').remove();
+            this.$('.oe_view_nocontent').remove();
 
 
             if (dom_insert_after) {
                 message.insertAfter(dom_insert_after);
-                return message
-            } 
-
-            // check older and newer message for insertion
-            var message_newer = false;
-            var message_older = false;
-            if (message.id > 0) {
-                for (var i in self.messages) {
-                    if (self.messages[i].id > message.id) {
-                        if (!message_newer || message_newer.id > self.messages[i].id) {
-                            message_newer = self.messages[i];
-                        }
-                    } else if (self.messages[i].id > 0 && self.messages[i].id < message.id) {
-                        if (!message_older || message_older.id < self.messages[i].id) {
-                            message_older = self.messages[i];
-                        }
-                    }
-                }
-            }
-
-            var sort = (!!self.thread_level || message.id<0);
-
-            if (sort) {
-                if (message_older) {
-
-                    message.insertAfter(message_older.thread ? (message_older.thread.compose_message ? message_older.thread.compose_message.$el : message_older.thread.$el) : message_older.$el);
-
-                } else if (message_newer) {
-
-                    message.insertBefore(message_newer.$el);
-
-                } else if (message.id < 0) {
-
-                    message.appendTo(self.$el);
-
-                } else {
-
-                    message.prependTo(self.$el);
-                }
             } else {
-                if (message_older) {
-
-                    message.insertBefore(message_older.$el);
-
-                } else if (message_newer) {
-
-                    message.insertAfter(message_newer.thread ? (message_newer.thread.compose_message ? message_newer.thread.compose_message.$el : message_newer.thread.$el) : message_newer.$el );
-
-                } else if (message.id < 0) {
-
-                    message.prependTo(self.$el);
-
-                } else {
-
-                    message.appendTo(self.$el);
-
-                }
+                message.appendTo(self.$el);
             }
 
             return message
@@ -1241,7 +1298,7 @@ openerp.mail = function (session) {
          * Each message is send to his parent object (or parent thread flat mode) for creating the object message.
          * @param : {Array} datas from calling RPC to "message_read"
          */
-        switch_new_message: function (records) {
+        switch_new_message: function (records, dom_insert_after) {
             var self=this;
             _(records).each(function (record) {
                 var thread = self.browse_thread({
@@ -1251,7 +1308,7 @@ openerp.mail = function (session) {
                 // create object and attach to the thread object
                 var message = thread.create_message_object( record );
                 // insert the message on dom
-                thread.insert_message( message );
+                thread.insert_message( message, typeof dom_insert_after == 'object' ? dom_insert_after : false);
             });
         },
 
@@ -1262,6 +1319,10 @@ openerp.mail = function (session) {
         on_message_detroy: function (message) {
 
             this.messages = _.filter(this.messages, function (val) { return !val.isDestroyed(); });
+            if (this.options.root_thread == this && !this.messages.length) {
+                this.no_message();
+            }
+            return false;
 
         },
 
@@ -1323,7 +1384,6 @@ openerp.mail = function (session) {
             } else {
                 // create a expandable message
                 var expandable = new mail.ThreadExpandable(this, {
-                    'id': message.id,
                     'model': message.model,
                     'parent_id': message.parent_id,
                     'nb_messages': 1,
@@ -1332,9 +1392,11 @@ openerp.mail = function (session) {
                     'domain': message_dom,
                     'options': message.options,
                     }, {
-                    'default_model': message.model || this.context.default_model,
-                    'default_res_id': message.res_id || this.context.default_res_id,
-                    'default_parent_id': this.id,
+                    'context':{
+                        'default_model': message.model || this.context.default_model,
+                        'default_res_id': message.res_id || this.context.default_res_id,
+                        'default_parent_id': this.id,
+                    }
                 });
 
                 // add object on array and DOM
@@ -1350,7 +1412,7 @@ openerp.mail = function (session) {
     });
 
     /**
-         * ------------------------------------------------------------
+     * ------------------------------------------------------------
      * mail : root Widget
      * ------------------------------------------------------------
      *
@@ -1360,7 +1422,7 @@ openerp.mail = function (session) {
      */
     session.web.client_actions.add('mail.Widget', 'session.mail.Widget');
     mail.Widget = session.web.Widget.extend({
-        template: 'mail.Widget',
+        template: 'mail.Root',
 
         /**
          * @param {Object} parent parent
@@ -1368,15 +1430,13 @@ openerp.mail = function (session) {
          * @param {Object} [context] context of the thread. It should
          *   contain at least default_model, default_res_id. Please refer to
          *   the compose_message widget for more information about it.
-         * ... @param {Select} [typeof_thread=(mail|stared|archives|send|other)]
-         *       options for destroy message when the user click on a button
          * @param {Object} [options]
          *...  @param {Number} [truncate_limit=250] number of character to
          *      display before having a "show more" link; note that the text
          *      will not be truncated if it does not have 110% of the parameter
          *...  @param {Boolean} [show_record_name] display the name and link for do action
-         *...  @param {int} [show_reply_button] number thread level to display the reply button
-         *...  @param {int} [show_read_unread_button] number thread level to display the read/unread button
+         *...  @param {boolean} [show_reply_button] display the reply button
+         *...  @param {boolean} [show_read_unread_button] display the read/unread button
          *...  @param {int} [display_indented_thread] number thread level to indented threads.
          *      other are on flat mode
          *...  @param {Boolean} [show_compose_message] allow to display the composer
@@ -1387,35 +1447,25 @@ openerp.mail = function (session) {
          *     @param {String} [no_message] Message to display when there are no message
          */
         init: function (parent, action) {
-            var options = action.params || {};
-            this._super(parent);
-            this.domain = options.domain || [];
-            this.context = options.context || {};
-            this.search_results = {'domain': [], 'context': {}, 'groupby': {}};
+            this._super(parent, action);
+            var self = this;
+            this.action = _.clone(action);
+            this.domain = this.action.domain || this.action.params.domain || [];
+            this.context = this.action.context || this.action.params.context || {};
 
-            this.options = _.extend({
-                'typeof_thread' : 'inbox',
+            this.action.params = _.extend({
                 'display_indented_thread' : -1,
-                'show_reply_button' : -1,
-                'show_read_unread_button' : -1,
+                'show_reply_button' : false,
+                'show_read_unread_button' : false,
                 'truncate_limit' : 250,
                 'show_record_name' : false,
                 'show_compose_message' : false,
                 'show_compact_message' : false,
+                'view_inbox': false,
                 'message_ids': undefined,
-                'no_message': false
-            }, options);
+            }, this.action.params);
 
-            if (this.display_indented_thread === false) {
-                this.display_indented_thread = -1;
-            }
-            if (this.show_reply_button === false) {
-                this.show_reply_button = -1;
-            }
-            if (this.show_read_unread_button === false) {
-                this.show_read_unread_button = -1;
-            }
-            
+            this.action.params.help = this.action.help || false;
         },
 
         start: function (options) {
@@ -1423,7 +1473,6 @@ openerp.mail = function (session) {
             this.message_render();
             this.bind_events();
         },
-
         
         /**
          *Create the root thread and display this object in the DOM.
@@ -1435,35 +1484,32 @@ openerp.mail = function (session) {
             this.thread = new mail.Thread(this, {}, {
                 'domain' : this.domain,
                 'context' : this.context,
-                'options': this.options,
+                'options': this.action.params,
             });
 
             this.thread.appendTo( this.$el );
             this.thread.no_message();
-            this.thread.message_fetch(null, null, this.options.message_ids);
 
-            if (this.options.show_compose_message) {
+            if (this.action.params.show_compose_message) {
                 this.thread.instantiate_compose_message();
-                if (this.options.show_compact_message) {
-                    this.thread.compose_message.do_show_compact();
-                } else {
-                    this.thread.compose_message.do_hide_compact();
-                }
+                this.thread.compose_message.do_show_compact();
             }
+
+            this.thread.message_fetch(null, null, this.action.params.message_ids);
+
         },
 
         bind_events: function () {
-            if (this.context['typeof_thread']!='other') {
-                $(document).scroll( this.thread.on_scroll );
-                $(window).resize( this.thread.on_scroll );
-                window.setTimeout( this.thread.on_scroll, 500 );
-            }
+            $(document).scroll( _.bind(this.thread.on_scroll, this.thread) );
+            $(window).resize( _.bind(this.thread.on_scroll, this.thread) );
+            this.$el.resize( _.bind(this.thread.on_scroll, this.thread) );
+            window.setTimeout( _.bind(this.thread.on_scroll, this.thread), 500 );
         }
     });
 
 
     /**
-         * ------------------------------------------------------------
+     * ------------------------------------------------------------
      * mail_thread Widget
      * ------------------------------------------------------------
      *
@@ -1476,10 +1522,19 @@ openerp.mail = function (session) {
     mail.RecordThread = session.web.form.AbstractField.extend({
         template: 'mail.record_thread',
 
-        init: function () {
+        init: function (parent, node) {
             this._super.apply(this, arguments);
-            this.options.domain = this.options.domain || [];
-            this.options.context = {'default_model': 'mail.thread', 'default_res_id': false};
+            this.node = _.clone(node);
+
+            this.node.params = _.extend({
+                'display_indented_thread': -1,
+                'show_reply_button': false,
+                'show_read_unread_button': false,
+                'show_compose_message': this.view.is_action_enabled('edit'),
+                'show_compact_message': 1,
+            }, this.node.params);
+
+            this.domain = this.node.params && this.node.params.domain || [];
         },
 
         start: function () {
@@ -1492,42 +1547,32 @@ openerp.mail = function (session) {
         _check_visibility: function () {
             this.$el.toggle(this.view.get("actual_mode") !== "create");
         },
+
         render_value: function () {
             var self = this;
+
             if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
                 this.$('oe_mail_thread').hide();
                 return;
             }
-            // update context
-            _.extend(this.options.context, {
-                default_res_id: this.view.datarecord.id,
-                default_model: this.view.model,
-                default_is_private: false });
-            // update domain
-            var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]);
-
-            var show_compose_message = this.view.is_action_enabled('edit') ||
-                (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
 
-            var message_ids = this.getParent().fields.message_ids && this.getParent().fields.message_ids.get_value();
+            this.node.params = _.extend({
+                'message_ids': this.getParent().fields.message_ids ? this.getParent().fields.message_ids.get_value() : undefined,
+            }, this.node.params);
+            this.node.context = {
+                'default_res_id': this.view.datarecord.id || false,
+                'default_model': this.view.model || false,
+            };
 
             if (this.root) {
+                $('<span class="oe_mail-placeholder"/>').insertAfter(this.root.$el);
                 this.root.destroy();
             }
             // create and render Thread widget
-            this.root = new mail.Widget(this, { params: {
-                'domain' : domain,
-                'context' : this.options.context,
-                'typeof_thread': this.options.context['typeof_thread'] || 'other',
-                'display_indented_thread': -1,
-                'show_reply_button': 0,
-                'show_read_unread_button': -1,
-                'show_compose_message': show_compose_message,
-                'message_ids': message_ids,
-                'show_compact_message': true,
-                'no_message': this.node.attrs.help
-                }}
-            );
+            this.root = new mail.Widget(this, _.extend(this.node, {
+                'domain' : (this.domain || []).concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]),
+                
+            }));
 
             return this.root.replace(this.$('.oe_mail-placeholder'));
         },
@@ -1535,7 +1580,7 @@ openerp.mail = function (session) {
 
 
     /**
-         * ------------------------------------------------------------
+     * ------------------------------------------------------------
      * Wall Widget
      * ------------------------------------------------------------
      *
@@ -1543,6 +1588,7 @@ openerp.mail = function (session) {
      * use is to receive a context and a domain, and to delegate the message
      * fetching and displaying to the Thread widget.
      */
+
     session.web.client_actions.add('mail.wall', 'session.mail.Wall');
     mail.Wall = session.web.Widget.extend({
         template: 'mail.wall',
@@ -1555,35 +1601,53 @@ openerp.mail = function (session) {
          *      contain default_model, default_res_id, to give it to the threads.
          */
         init: function (parent, action) {
-            this._super(parent);
-            var options = action.params || {};
-            this.options = options;
-            this.options.domain = options.domain || [];
-            this.options.context = options.context || {};
-            this.search_results = {'domain': [], 'context': {}, 'groupby': {}}
-            this.ds_msg = new session.web.DataSetSearch(this, 'mail.message');
+            this._super(parent, action);
+
+            this.action = _.clone(action);
+            this.domain = this.action.params.domain || this.action.domain || [];
+            this.context = this.action.params.context || this.action.context || {};
+
+            this.defaults = {};
+            for (var key in this.context) {
+                if (key.match(/^search_default_/)) {
+                    this.defaults[key.replace(/^search_default_/, '')] = this.context[key];
+                }
+            }
+
+            this.action.params = _.extend({
+                'display_indented_thread': 1,
+                'show_reply_button': true,
+                'show_read_unread_button': true,
+                'show_compose_message': true,
+                'show_compact_message': this.action.params.view_mailbox ? false : 1,
+                'view_inbox': false,
+            }, this.action.params);
         },
 
         start: function () {
-            this._super.apply(this, arguments);
-            var searchview_ready = this.load_searchview({}, false);
-            var thread_displayed = this.message_render();
-            this.options.domain = this.options.domain.concat(this.search_results['domain']);
+            this._super.apply(this);
             this.bind_events();
-            return $.when(searchview_ready, thread_displayed);
+            var searchview_loaded = this.load_searchview(this.defaults);
+            if (! this.searchview.has_defaults) {
+                this.message_render();
+            }
+            
         },
 
         /**
          * Load the mail.message search view
          * @param {Object} defaults ??
-         * @param {Boolean} hidden some kind of trick we do not care here
          */
-        load_searchview: function (defaults, hidden) {
+        load_searchview: function (defaults) {
             var self = this;
-            this.searchview = new session.web.SearchView(this, this.ds_msg, false, defaults || {}, hidden || false);
-            return this.searchview.appendTo(this.$('.oe_view_manager_view_search')).then(function () {
-                self.searchview.on('search_data', self, self.do_searchview_search);
-            });
+            var ds_msg = new session.web.DataSetSearch(this, 'mail.message');
+            this.searchview = new session.web.SearchView(this, ds_msg, false, defaults || {}, false);
+            this.searchview.appendTo(this.$('.oe_view_manager_view_search'))
+                .then(function () { self.searchview.on('search_data', self, self.do_searchview_search); });
+            if (this.searchview.has_defaults) {
+                this.searchview.ready.then(this.searchview.do_search);
+            }
+            return this.searchview
         },
 
         /**
@@ -1600,32 +1664,25 @@ openerp.mail = function (session) {
                 contexts: contexts || [],
                 group_by_seq: groupbys || []
             }).then(function (results) {
-                self.search_results['context'] = results.context;
-                self.search_results['domain'] = results.domain;
-                self.root.destroy();
-                return self.message_render();
+                if(self.root) {
+                    $('<span class="oe_mail-placeholder"/>').insertAfter(self.root.$el);
+                    self.root.destroy();
+                }
+                return self.message_render(results);
             });
         },
 
-
         /**
-         *Create the root thread widget and display this object in the DOM
-          */
+         * Create the root thread widget and display this object in the DOM
+         */
         message_render: function (search) {
-            var domain = this.options.domain.concat(this.search_results['domain']);
-            var context = _.extend(this.options.context, search&&search.search_results['context'] ? search.search_results['context'] : {});
-            this.root = new mail.Widget(this, { params: {
+            var domain = this.domain.concat(search && search['domain'] ? search['domain'] : []);
+            var context = _.extend(this.context, search && search['context'] ? search['context'] : {});
+
+            this.root = new mail.Widget(this, _.extend(this.action, {
                 'domain' : domain,
                 'context' : context,
-                'typeof_thread': context['typeof_thread'] || 'other',
-                'display_indented_thread': 1,
-                'show_reply_button': 10,
-                'show_read_unread_button': 11,
-                'show_compose_message': true,
-                'show_compact_message': false,
-                }}
-            );
-
+            }));
             return this.root.replace(this.$('.oe_mail-placeholder'));
         },
 
@@ -1653,19 +1710,18 @@ openerp.mail = function (session) {
 
 
     /**
-         * ------------------------------------------------------------
+     * ------------------------------------------------------------
      * UserMenu
      * ------------------------------------------------------------
      * 
      * Add a link on the top user bar for write a full mail
      */
     session.web.ComposeMessageTopButton = session.web.Widget.extend({
-        template:'mail.compose_message.button_top_bar',
+        template:'mail.ComposeMessageTopButton',
 
-        start: function (parent, params) {
-            var self = this;
-            this.$el.on('click', 'button', self.on_compose_message );
-            this._super(parent, params);
+        start: function () {
+            this.$('button').on('click', this.on_compose_message );
+            this._super();
         },
 
         on_compose_message: function (event) {
@@ -1675,26 +1731,22 @@ openerp.mail = function (session) {
                 res_model: 'mail.compose.message',
                 view_mode: 'form',
                 view_type: 'form',
-                action_from: 'mail.ThreadComposeMessage',
                 views: [[false, 'form']],
                 target: 'new',
-                context: {
-                    'default_model': '',
-                    'default_res_id': false,
-                    'default_content_subtype': 'html',
-                },
+                context: { 'default_content_subtype': 'html' },
             };
             session.client.action_manager.do_action(action);
         },
-
     });
 
-    session.web.UserMenu = session.web.UserMenu.extend({
-        start: function (parent, params) {
-            var render = new session.web.ComposeMessageTopButton();
-            render.insertAfter(this.$el);
-            this._super(parent, params);
-        }
+    session.web.UserMenu.include({
+        do_update: function(){
+            var self = this;
+            this._super.apply(this, arguments);
+            this.update_promise.then(function() {
+                var mail_button = new session.web.ComposeMessageTopButton();
+                mail_button.appendTo(session.webclient.$el.find('.oe_systray'));
+            });
+        },
     });
-
 };
index a13d204..7bb019c 100644 (file)
@@ -30,6 +30,8 @@ openerp_mail_followers = function(session, mail) {
             this.ds_model = new session.web.DataSetSearch(this, this.view.model);
             this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
             this.ds_users = new session.web.DataSetSearch(this, 'res.users');
+
+            this.value = [];
         },
 
         start: function() {
@@ -41,6 +43,11 @@ openerp_mail_followers = function(session, mail) {
             this._super();
         },
 
+        set_value: function(_value) {
+            this.value = _value;
+            this._super(_value);
+        },
+
         _check_visibility: function() {
             this.$el.toggle(this.view.get("actual_mode") !== "create");
         },
@@ -85,22 +92,23 @@ openerp_mail_followers = function(session, mail) {
 
         read_value: function () {
             var self = this;
-            return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).pipe(function (results) {
-                self.set_value(results[0].message_follower_ids);
+            return this.ds_model.read_ids([this.view.datarecord.id], ['message_follower_ids']).then(function (results) {
+                self.value = results[0].message_follower_ids;
+                self.render_value();
             });
         },
 
         render_value: function () {
             this.reinit();
-            return this.fetch_followers(this.get("value"));
+            return this.fetch_followers(this.value);
         },
 
         fetch_followers: function (value_) {
             this.value = value_ || {};
             return this.ds_follow.call('read', [this.value, ['name', 'user_ids']])
-                .pipe(this.proxy('display_followers'), this.proxy('fetch_generic'))
-                .pipe(this.proxy('display_buttons'))
-                .pipe(this.proxy('fetch_subtypes'));
+                .then(this.proxy('display_followers'), this.proxy('fetch_generic'))
+                .then(this.proxy('display_buttons'))
+                .then(this.proxy('fetch_subtypes'));
         },
 
         /** Read on res.partner failed: fall back on a generic case
@@ -109,10 +117,10 @@ openerp_mail_followers = function(session, mail) {
         fetch_generic: function (error, event) {
             var self = this;
             event.preventDefault();
-            return this.ds_users.call('read', [this.session.uid, ['partner_id']]).pipe(function (results) {
+            return this.ds_users.call('read', [this.session.uid, ['partner_id']]).then(function (results) {
                 var pid = results['partner_id'][0];
-                self.message_is_follower = (_.indexOf(self.get('value'), pid) != -1);
-            }).pipe(self.proxy('display_generic'));
+                self.message_is_follower = (_.indexOf(self.value, pid) != -1);
+            }).then(self.proxy('display_generic'));
         },
         _format_followers: function(count){
             // TDE note: why redefining _t ?
@@ -131,7 +139,7 @@ openerp_mail_followers = function(session, mail) {
         display_generic: function () {
             var self = this;
             var node_user_list = this.$('.oe_follower_list').empty();
-            this.$('.oe_follower_title').html(this._format_followers(this.get('value').length));
+            this.$('.oe_follower_title').html(this._format_followers(this.value.length));
         },
 
         /** Display the followers */
@@ -179,7 +187,7 @@ openerp_mail_followers = function(session, mail) {
             var subtype_list_ul = this.$('.oe_subtype_list').empty();
             if (! this.message_is_follower) return;
             var context = new session.web.CompoundContext(this.build_context(), {});
-            this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).pipe(this.proxy('display_subtypes'));
+            this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).then(this.proxy('display_subtypes'));
         },
 
         /** Display subtypes: {'name': default, followed} */
@@ -206,7 +214,8 @@ openerp_mail_followers = function(session, mail) {
                 $(record).attr('checked',false);
             });
             var context = new session.web.CompoundContext(this.build_context(), {});
-            return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context]).pipe(this.proxy('read_value'));
+            return this.ds_model.call('message_unsubscribe_users', [[this.view.datarecord.id], [this.session.uid], context])
+                .then(this.proxy('read_value'));
         },
 
         do_update_subscription: function (event) {
@@ -220,8 +229,8 @@ openerp_mail_followers = function(session, mail) {
             });
 
             var context = new session.web.CompoundContext(this.build_context(), {});
-            return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist:undefined, context])
-                .pipe(this.proxy('read_value'));
+            return this.ds_model.call('message_subscribe_users', [[this.view.datarecord.id], [this.session.uid], this.message_is_follower ? checklist : undefined, context])
+                .then(this.proxy('read_value'));
         },
     });
 };
index 52dcbf7..cc701e8 100644 (file)
@@ -46,7 +46,7 @@ instance.web.form.FieldMany2ManyTagsEmail = instance.web.form.FieldMany2ManyTags
                 ["email", "=", false], 
                 ["notification_email_send", "in", ['all', 'comment']] ]], 
                 {context: this.build_context()})
-            .pipe(function (record_ids) {
+            .then(function (record_ids) {
                 // valid partner
                 var valid_partner = _.difference(ids, record_ids);
                 self.values = self.values.concat(valid_partner);
index caa85cd..2ba1733 100644 (file)
@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <template>
 
-    <!-- this template contains the mail widget and is used to namespace the css -->
-    <t t-name="mail.Widget">
+    <!--
+        mail.Widget template used to namespace the css -->
+    <t t-name="mail.Root">
         <div class="oe_mail">
         </div>
     </t>
@@ -38,7 +39,8 @@
             </div>
         </div>
         <div t-if="widget.show_compact_message and !widget.show_composer" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
-            <textarea class="field_text oe_compact" placeholder="Write a reply..."/>
+            <textarea t-if="!widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to the followers of this docmuent..."/>
+            <textarea t-if="widget.options.view_mailbox" class="field_text oe_compact" placeholder="Write to my followers..."/>
         </div>
         <span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span>
     </t>
         Template used to display attachments in a mail.message
         -->
     <t t-name="mail.thread.message.attachments">
-        <span class="oe_msg_attachments">
-            <t t-foreach="widget.attachment_ids" t-as="attachment" t-if="!attachment.is_image">
-                <div class="oe_attachment">
-                    <span t-if="(attachment.upload and attachment.percent_loaded&lt;100)" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-attf-name="{attachment.name || attachment.filename}">
-                        <div class="oe_upload_in_process">
-                            <span>...Upload in progress...</span>
-                        </div>
-                        <t t-raw="attachment.name || attachment.filename"/>
-                    </span>
-                    <t t-if="(!attachment.upload or attachment.percent_loaded&gt;=100)">
-                        <a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
-                            <t t-raw="attachment.name || attachment.filename"/>
-                        </a>
-                    </t>
-                    <t t-if="(widget.show_delete_attachment and (!attachment.upload or attachment.percent_loaded&gt;=100))">
-                        <a class="oe_right oe_mail_attachment_delete oe_e" title="Delete this attachment" t-attf-data-id="{attachment.id}">[</a>
-                    </t>
+        <t t-foreach='widget.attachment_ids' t-as='attachment'>
+            <t t-if="attachment.filetype !== 'webimage'">
+                <div t-attf-class="oe_attachment #{attachment.upload ? 'oe_uploading' : ''}">
+                    <a t-att-href='attachment.url'><img t-att-src="'/mail/static/src/img/mimetypes/' + attachment.filetype + '.png'"></img></a>
+                    <div class='oe_delete oe_e' title="Delete this attachment" t-attf-data-id="{attachment.id}">[</div>
+                    <div class='oe_name'><t t-raw='attachment.name' /></div>
+                    <div class='oe_progress_bar'>
+                        uploading
+                    </div>
                 </div>
             </t>
-        </span>
-        <span class="oe_msg_images">
-            <t t-foreach="widget.attachment_ids" t-as="attachment" t-if="attachment.is_image">
-                <a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
-                    <img t-if="attachment.is_image" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-att-src="attachment.url"/>
-                </a>
+            <t t-if="attachment.filetype === 'webimage'">
+                <div t-attf-class="oe_attachment oe_preview #{attachment.upload ? 'oe_uploading' : ''}">
+                    <a t-att-href='attachment.url'><img t-att-src="widget.attachments_resize_image(attachment.id, [100,100])"></img></a>
+                    <div class='oe_delete oe_e'>[</div>
+                    <div class='oe_name'><t t-raw='attachment.name' /></div>
+                    <div class='oe_progress_bar'>
+                        uploading
+                    </div>
+                </div>
             </t>
-        </span>
+        </t>
     </t>
 
     <t t-name="mail.thread.message.private">
     <t t-name="mail.thread.list_recipients">
         <div class="oe_mail_list_recipients">
             To:
-            <span t-if="!widget.is_private" class="oe_all_follower">Everyone</span> 
+            <span t-if="!widget.is_private" class="oe_all_follower">Followers <t t-raw="' of ' + (widget.parent_thread.parent_message.record_name ? '&quot;' + widget.parent_thread.parent_message.record_name + '&quot;' : 'this document')"/></span>
             <t t-if="!widget.is_private and widget.partner_ids.length"> and </t>
             <t t-set="inc" t-value="0"/>
             <t t-if="widget.partner_ids.length" t-foreach="widget.partner_ids" t-as="partner"><span t-attf-class="oe_partner_follower #{inc>=3?'oe_hidden':''}"><t t-if="inc" t-raw="', '"/><a t-attf-href="#model=res.partner&amp;id=#{partner[0]}"><t t-raw="partner[1]"/></a></span><t t-set="inc" t-value="inc+1"/>
                 <td colspan="2">
                     <h2 class="oe_view_title">
                         <span class="oe_view_title_text">
-                            News Feed
-                            <t t-if="widget.options.res_model"> / </t>
-                            <t t-if="widget.options.res_model" t-esc="widget.options.res_model"/>
+                            Email box
                         </span>
                     </h2>
                 </td>
                 <td><div class="oe_view_manager_view_search" t-opentag="true"/></td>
             </tr>
-            <tr class="oe_header_row">
+            <tr class="oe_header_row" t-if="widget.action.params.view_inbox" >
                 <td colspan="2">
                     <button type="button" class="oe_write_full oe_highlight">
                         Compose a new message
                     </button>
                     <span class='oe_alternative'>
                         or
-                        <a href='#'  class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write to your followers</a>
+                        <a href='#'  class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write to my followers</a>
                     </span>
                 </td>
             </tr>
         display message on the wall when there are no message
         -->
     <t t-name="mail.wall_no_message">
-        <div class="oe_wall_no_message">You have no messages</div>
+        <div class="oe_view_nocontent">No messages.</div>
     </t>
 
     <!--
 
     <!-- default layout -->
     <t t-name="mail.thread.message">
-        <div t-attf-class="oe_msg #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_#{widget.type} oe_msg_#{widget.to_read?'unread':'read'}">
+        <div t-attf-class="oe_msg #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_#{widget.type}">
 
             <div class='oe_msg_left'>
                 <a t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}" t-att-title="widget.author_id[1]">
 
             <div class="oe_msg_center">
                 <div class='oe_msg_icons'>
-                    <span class='oe_read' t-if="widget.show_read_unread_button"><a title="Read" class="oe_e">X</a></span>
-                    <span class='oe_unread' t-if="widget.show_read_unread_button"><a title="Set back to unread" class="oe_e">v</a></span>
-                    <span class='oe_reply' t-if="widget.show_reply_button"><a title="Reply" class="oe_e">(</a></span>
-                    <span t-attf-class="oe_star #{widget.is_favorite?'oe_starred':''}"><a title="Add To Favorites" class="oe_e">7</a></span>
+                    <span class='oe_read' t-if="widget.options.show_read"><a title="Done" class="oe_e">X</a></span>
+                    <span class='oe_unread' t-if="widget.is_favorite and widget.options.show_unread"><a title="Set back to Todo" class="oe_e">v</a></span>
+                    <span class='oe_unread' t-if="!widget.is_favorite and widget.options.show_unread"><a title="Set back to Inbox" class="oe_e">v</a></span>
+                    <span class='oe_reply' t-if="widget.options.show_reply_button"><a title="Reply" class="oe_e">(</a></span>
+                    <span t-attf-class="oe_star #{widget.is_favorite?'oe_starred':''}"><a title="Mark as Todo" class="oe_e">7</a></span>
                 </div>
                 <!-- message itself -->
                 <div class="oe_msg_content">
                         <t t-raw="widget.subject"/>
                     </h1>
                     <div class="oe_msg_body">
-                        <t t-if="widget.options.show_record_name and widget.record_name and (!widget.subject) and !widget.options.thread_level and !widget.options.display_on_thread[0] and widget.model!='res.partner'">
+                        <t t-if="widget.show_record_name">
                             <a class="oe_mail_action_model" t-attf-href="#model=#{widget.model}&amp;id=#{widget.res_id}"><t t-raw="widget.record_name"/></a>
                         </t>
                         <t t-raw="widget.body"/>
                     </div>
-                    <t t-if="widget.attachment_ids.length > 0">
-                        <t t-call="mail.thread.message.attachments"/>
-                    </t>
                 </div>
                 <div class="oe_msg_footer">
+                    <t t-if="widget.attachment_ids.length > 0">
+                        <div class="oe_msg_attachment_list"></div>
+                    </t>
                     <a t-if="widget.author_id" t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}"><t t-raw="widget.author_id[1]"/></a> 
                     <span class='oe_subtle'>•</span>
                     <span t-att-title="widget.date"><t t-raw="widget.timerelative"/></span>
-                        <a t-if="widget.attachment_ids.length > 0" class="oe_mail_msg_view_attachments">
+                    <t t-if='widget.attachment_ids.length > 0'>
+                        <span class='oe_subtle'>•</span>
+                        <a class="oe_view_attachments">
                             <t t-if="widget.attachment_ids.length == 1">1 Attachment</t>
                             <t t-if="widget.attachment_ids.length > 1"><t t-raw="widget.attachment_ids.length"/> Attachments</t>
                         </a>
+                    </t>
                     <span class='oe_subtle'>•</span>
                     <t t-call="mail.thread.message.vote"/>
                 </div>
 
     <!-- expandable message layout -->
     <t t-name="mail.thread.expandable">
-        <div t-attf-class="oe_msg oe_msg_#{widget.type} #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''} oe_msg_unread">
+        <div t-attf-class="oe_msg oe_msg_#{widget.type} #{widget.max_limit ? 'oe_max_limit' : ''} #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
             <div class="oe_msg_content oe_msg_more_message">
                 <div class='oe_separator'></div>
+                <a t-if="widget.nb_messages &lt;= 0" class="oe_msg_fetch_more">show more message</a>
                 <a t-if="widget.nb_messages === 1" class="oe_msg_fetch_more">show one more message</a>
-                <a t-if="widget.nb_messages !== 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a>
+                <a t-if="widget.nb_messages &gt; 1" class="oe_msg_fetch_more">show <t t-raw="widget.nb_messages" /> more messages</a>
             </div>
         </div>
     </t>
         mail.compose_message.button_top_bar
         render of the button on the user bar for open wizard compose message
         -->
-    <t t-name="mail.compose_message.button_top_bar">
-        <div class="oe_topbar_compose_full_email">
-            <!-- <button class="oe_button oe_highlight">Write an email</button> -->
+    <t t-name="mail.ComposeMessageTopButton">
+        <div title='Compose new Message' class="oe_topbar_item oe_topbar_compose_full_email">
+            <button class="oe_e">%</button>
         </div>
     </t>
 
         Template used to display Like/Unlike in a mail.message
     -->
     <span t-name="mail.thread.message.vote">
-        
         <span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
             <t t-raw='widget.vote_nb' />
             <span class='oe_e'>8</span>
         </span>
-        <a href='#' t-attf-class="oe_msg_vote">
+        <a href='#' class="oe_msg_vote">
             <t t-if="!widget.has_voted">like</t>
             <t t-if="widget.has_voted">unlike</t>
         </a>
index 8e880a6..aea67dc 100644 (file)
@@ -576,7 +576,7 @@ class test_mail(TestMailMockups):
         cr, uid, user_admin, group_pigs = self.cr, self.uid, self.user_admin, self.group_pigs
         pigs_domain = [('model', '=', 'mail.group'), ('res_id', '=', self.group_pigs_id)]
 
-        # Data: create a discussion in Pigs (2 messages, one with 2 and one with 3 answers)
+        # Data: create a discussion in Pigs (3 threads, with respectively 0, 4 and 4 answers)
         msg_id0 = self.group_pigs.message_post(body='0', subtype='mt_comment')
         msg_id1 = self.group_pigs.message_post(body='1', subtype='mt_comment')
         msg_id2 = self.group_pigs.message_post(body='2', subtype='mt_comment')
@@ -588,7 +588,8 @@ class test_mail(TestMailMockups):
         msg_id8 = self.group_pigs.message_post(body='2-1-1', subtype='mt_comment', parent_id=msg_id4)
         msg_id9 = self.group_pigs.message_post(body='1-1-1', subtype='mt_comment', parent_id=msg_id3)
         msg_id10 = self.group_pigs.message_post(body='2-1-1', subtype='mt_comment', parent_id=msg_id4)
-        msg_ids = [msg_id0, msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8, msg_id9, msg_id10]
+        msg_ids = [msg_id10, msg_id9, msg_id8, msg_id7, msg_id6, msg_id5, msg_id4, msg_id3, msg_id2, msg_id1, msg_id0]
+        ordered_msg_ids = [msg_id2, msg_id4, msg_id6, msg_id8, msg_id10, msg_id1, msg_id3, msg_id5, msg_id7, msg_id9, msg_id0]
 
         # Test: read some specific ids
         read_msg_list = self.mail_message.message_read(cr, uid, ids=msg_ids[2:4], domain=[('body', 'like', 'dummy')])
@@ -601,7 +602,8 @@ class test_mail(TestMailMockups):
         self.assertEqual(msg_ids, read_msg_ids, 'message_read flat with domain on Pigs should equal all messages of Pigs')
         read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=200, thread_level=1)
         read_msg_ids = [msg.get('id') for msg in read_msg_list]
-        self.assertEqual(msg_ids, read_msg_ids, 'message_read threaded with domain on Pigs should equal all messages of Pigs')
+        self.assertEqual(ordered_msg_ids, read_msg_ids,
+            'message_read threaded with domain on Pigs should equal all messages of Pigs, and sort them with newer thread first, last message last in thread')
 
         # ----------------------------------------
         # CASE1: message_read with domain, threaded
@@ -611,6 +613,8 @@ class test_mail(TestMailMockups):
         # Do: read last message, threaded
         read_msg_list = self.mail_message.message_read(cr, uid, domain=pigs_domain, limit=1, thread_level=1)
         read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
+        # TDE TODO: test expandables order
+        type_list = map(lambda item: item.get('type'), read_msg_list)
         # Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
         self.assertEqual(len(read_msg_list), 4, 'message_read on last Pigs message should return 2 messages and 2 expandables')
         self.assertEqual(set([msg_id2, msg_id10]), set(read_msg_ids), 'message_read on the last Pigs message should also get its parent')
@@ -618,7 +622,7 @@ class test_mail(TestMailMockups):
         # Data: get expandables
         new_threads_exp, new_msg_exp = None, None
         for msg in read_msg_list:
-            if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
+            if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
                 new_threads_exp = msg
             elif msg.get('type') == 'expandable':
                 new_msg_exp = msg
@@ -630,12 +634,20 @@ class test_mail(TestMailMockups):
         self.assertIn(('id', 'child_of', msg_id2), domain, 'new messages expandable domain should contain a child_of condition')
         self.assertIn(('id', '>=', msg_id4), domain, 'new messages expandable domain should contain an id greater than condition')
         self.assertIn(('id', '<=', msg_id8), domain, 'new messages expandable domain should contain an id less than condition')
-        self.assertEqual(new_msg_exp.get('parent_id'), msg_id2, 'new messages expandable should have ancestor_id set to the thread header')
-        # Do: message_read with domain, thread_level=0, parent_id=msg_id2 (should be imposed by JS)
-        read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=200, thread_level=0, parent_id=msg_id2)
+        self.assertEqual(new_msg_exp.get('parent_id'), msg_id2, 'new messages expandable should have parent_id set to the thread header')
+        # Do: message_read with domain, thread_level=0, parent_id=msg_id2 (should be imposed by JS), 2 messages
+        read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=2, thread_level=0, parent_id=msg_id2)
         read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
-        # Test: other message in thread have been fetch
-        self.assertEqual(set([msg_id4, msg_id6, msg_id8]), set(read_msg_ids), 'message_read in Pigs thread should return all the previous messages')
+        new_msg_exp = [msg for msg in read_msg_list if msg.get('type') == 'expandable'][0]
+        # Test: structure content, 2 messages and 1 thread expandable
+        self.assertEqual(len(read_msg_list), 3, 'message_read in Pigs thread should return 2 messages and 1 expandables')
+        self.assertEqual(set([msg_id6, msg_id8]), set(read_msg_ids), 'message_read in Pigs thread should return 2 more previous messages in thread')
+        # Do: read the last message
+        read_msg_list = self.mail_message.message_read(cr, uid, domain=new_msg_exp.get('domain'), limit=2, thread_level=0, parent_id=msg_id2)
+        read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
+        # Test: structure content, 1 message
+        self.assertEqual(len(read_msg_list), 1, 'message_read in Pigs thread should return 1 message')
+        self.assertEqual(set([msg_id4]), set(read_msg_ids), 'message_read in Pigs thread should return the last message in thread')
 
         # Do: fetch a new thread, domain from expandable
         self.assertIsNotNone(new_threads_exp, 'message_read on last Pigs message should have returned a new threads expandable')
@@ -643,7 +655,7 @@ class test_mail(TestMailMockups):
         # Test: expandable, conditions in domain
         for condition in pigs_domain:
             self.assertIn(condition, domain, 'new threads expandable domain should contain the message_read domain parameter')
-        self.assertFalse(new_threads_exp.get('parent_id'), 'new threads expandable should not have an ancestor_id')
+        self.assertFalse(new_threads_exp.get('parent_id'), 'new threads expandable should not have an parent_id')
         # Do: message_read with domain, thread_level=1 (should be imposed by JS)
         read_msg_list = self.mail_message.message_read(cr, uid, domain=domain, limit=1, thread_level=1)
         read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
@@ -654,7 +666,7 @@ class test_mail(TestMailMockups):
         # Data: get expandables
         new_threads_exp, new_msg_exp = None, None
         for msg in read_msg_list:
-            if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
+            if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
                 new_threads_exp = msg
             elif msg.get('type') == 'expandable':
                 new_msg_exp = msg
@@ -701,7 +713,7 @@ class test_mail(TestMailMockups):
         # Data: get expandables
         new_threads_exp, new_msg_exp = None, None
         for msg in read_msg_list:
-            if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('id') == -1:
+            if msg.get('type') == 'expandable' and msg.get('nb_messages') == -1 and msg.get('max_limit'):
                 new_threads_exp = msg
 
         # Do: fetch new messages, domain from expandable
@@ -715,7 +727,7 @@ class test_mail(TestMailMockups):
         read_msg_ids = [msg.get('id') for msg in read_msg_list if msg.get('type') != 'expandable']
         # Test: structure content, ancestor is added to the read messages, ordered by id, ancestor is set, 2 expandables
         self.assertEqual(len(read_msg_list), 9, 'message_read on Pigs should return 9 messages and 0 expandable')
-        self.assertEqual([msg_id0, msg_id1, msg_id2, msg_id3, msg_id4, msg_id5, msg_id6, msg_id7, msg_id8], read_msg_ids,
+        self.assertEqual([msg_id8, msg_id7, msg_id6, msg_id5, msg_id4, msg_id3, msg_id2, msg_id1, msg_id0], read_msg_ids,
             'message_read, More on flat, should return all remaning messages')
 
     def test_40_needaction(self):
index 50ecf94..1d495fd 100644 (file)
@@ -33,6 +33,7 @@ class invite_wizard(osv.osv_memory):
         result = super(invite_wizard, self).default_get(cr, uid, fields, context=context)
         if 'message' in fields and result.get('res_model') and result.get('res_id'):
             document_name = self.pool.get(result.get('res_model')).name_get(cr, uid, [result.get('res_id')], context=context)[0][1]
+            print "DOCUMENT_NAME: ",document_name            
             message = _('<div>You have been invited to follow %s.</div>' % document_name)
             result['message'] = message
         elif 'message' in fields:
index 3371284..45082b9 100644 (file)
@@ -135,7 +135,12 @@ class mail_compose_message(osv.TransientModel):
                 related to.
             :param int res_id: id of the document record this mail is related to
         """
-        return {'model': model, 'res_id': res_id}
+        doc_name_get = self.pool.get(model).name_get(cr, uid, res_id, context=context)
+        if doc_name_get:
+            record_name = doc_name_get[0][1]
+        else:
+            record_name = False
+        return {'model': model, 'res_id': res_id, 'record_name': record_name}
 
     def get_message_data(self, cr, uid, message_id, context=None):
         """ Returns a defaults-like dict with initial values for the composition
@@ -161,6 +166,7 @@ class mail_compose_message(osv.TransientModel):
 
         # update the result
         result = {
+            'record_name': message_data.record_name,
             'model': message_data.model,
             'res_id': message_data.res_id,
             'parent_id': message_data.id,
index b9c3d23..86b1be6 100644 (file)
                         <field name="parent_id" invisible="1"/>
                         <field name="content_subtype" invisible="1"/>
                         <!-- visible wizard -->
-                        <field name="partner_ids" widget="many2many_tags_email" placeholder="Add contacts to notify..."
+                        <label for="partner_ids" string="Recipients"/>
+                        <div>
+                            <span attrs="{'invisible':[('model', '=', False)]}">
+                                Followers of
+                                <field name="record_name" readonly="1" class="oe_inline"
+                                    attrs="{'invisible':[('model', '=', False)]}"/>
+                                and 
+                            </span>
+                            <field name="partner_ids" widget="many2many_tags_email" placeholder="Add contacts to notify..."
                                 context="{'force_email':True}" required="1"/>
+                        </div>
                         <field name="subject" placeholder="Subject..."
-                                attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
+                            attrs="{'invisible':[('content_subtype', '=', 'plain')]}"/>
                     </group>
                     <field name="body"/>
                     <field name="attachment_ids" widget="one2many_binary" blockui="0"/>
index 189bd82..ab45f80 100644 (file)
                         <field name="signal" groups="base.group_no_one"/>
                         <field name="start"/>
                         <field name="object_id" invisible="1"/>
-                        <field name="campaign_id" invisible="1"/>
+                        <field name="campaign_id" invisible="1" required="0"/>
                     </group>
                     <group>
                         <field name="variable_cost"/>
index 83d8082..c36878f 100644 (file)
@@ -7,14 +7,14 @@ msgstr ""
 "Project-Id-Version: OpenERP Server 6.0dev\n"
 "Report-Msgid-Bugs-To: support@openerp.com\n"
 "POT-Creation-Date: 2012-02-08 00:49+0000\n"
-"PO-Revision-Date: 2012-11-01 19:12+0000\n"
+"PO-Revision-Date: 2012-11-07 23:15+0000\n"
 "Last-Translator: Dusan Laznik <laznik@mentis.si>\n"
 "Language-Team: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-11-03 05:03+0000\n"
-"X-Generator: Launchpad (build 16218)\n"
+"X-Launchpad-Export-Date: 2012-11-09 04:39+0000\n"
+"X-Generator: Launchpad (build 16250)\n"
 
 #. module: mrp
 #: view:mrp.routing.workcenter:0
@@ -34,7 +34,7 @@ msgstr ""
 #. module: mrp
 #: help:mrp.production,location_src_id:0
 msgid "Location where the system will look for components."
-msgstr ""
+msgstr "Lokacija za iskanje komponent"
 
 #. module: mrp
 #: field:mrp.production,workcenter_lines:0
@@ -67,11 +67,13 @@ msgid ""
 "The 'Minimum stock rule' allows the system to create procurement orders "
 "automatically as soon as the minimum stock is reached."
 msgstr ""
+"Pravilo \"Minimalna zaloga\" omogoča avtomatsko kreiranje naročil "
+"dobaviteljem."
 
 #. module: mrp
 #: field:mrp.production,picking_id:0
 msgid "Picking list"
-msgstr ""
+msgstr "Dobavnica"
 
 #. module: mrp
 #: code:addons/mrp/report/price.py:130
@@ -83,18 +85,18 @@ msgstr "Urna postavka"
 #: code:addons/mrp/report/price.py:139
 #, python-format
 msgid "Cost Price per Uom"
-msgstr ""
+msgstr "Stroškovna cena na enoto"
 
 #. module: mrp
 #: view:mrp.production:0
 msgid "Scrap Products"
-msgstr ""
+msgstr "Odpis izdelkov"
 
 #. module: mrp
 #: model:ir.actions.act_window,name:mrp.mrp_routing_action
 #: model:ir.ui.menu,name:mrp.menu_mrp_routing_action
 msgid "Routings"
-msgstr ""
+msgstr "Delovni tok"
 
 #. module: mrp
 #: field:mrp.workcenter,product_id:0
@@ -139,7 +141,7 @@ msgstr "Dokončani proizvodi"
 #. module: mrp
 #: view:mrp.production:0
 msgid "Manufacturing Orders which are currently in production."
-msgstr ""
+msgstr "Delovni nalogi trenutno v proizvodnji"
 
 #. module: mrp
 #: model:process.transition,name:mrp.process_transition_servicerfq0
@@ -204,6 +206,7 @@ msgid ""
 "Fill this product to track easily your production costs in the analytic "
 "accounting."
 msgstr ""
+"Izpolnite ta izdelek , za spremljavo stroškov v analitičnem računovodstvu"
 
 #. module: mrp
 #: model:process.node,note:mrp.process_node_purchaseprocure0
@@ -246,12 +249,12 @@ msgstr "Nabava storitev"
 #. module: mrp
 #: view:mrp.workcenter:0
 msgid "Capacity Information"
-msgstr ""
+msgstr "Informacije o kapacitetah"
 
 #. module: mrp
 #: field:mrp.production,move_created_ids2:0
 msgid "Produced Products"
-msgstr ""
+msgstr "Končani izdelki"
 
 #. module: mrp
 #: report:mrp.production.order:0
@@ -294,7 +297,7 @@ msgstr ""
 #. module: mrp
 #: help:mrp.bom,position:0
 msgid "Reference to a position in an external plan."
-msgstr ""
+msgstr "Referenca na pozicijo v drugem planu"
 
 #. module: mrp
 #: constraint:stock.move:0
@@ -309,12 +312,12 @@ msgstr ""
 #. module: mrp
 #: model:ir.model,name:mrp.model_mrp_product_produce
 msgid "Product Produce"
-msgstr ""
+msgstr "Končani izdelki"
 
 #. module: mrp
 #: constraint:mrp.bom:0
 msgid "Error ! You cannot create recursive BoM."
-msgstr ""
+msgstr "Napaka ! (Rekurzivna kosovnica)"
 
 #. module: mrp
 #: model:ir.model,name:mrp.model_mrp_routing_workcenter
@@ -324,7 +327,7 @@ msgstr ""
 #. module: mrp
 #: model:process.transition,name:mrp.process_transition_procurestockableproduct0
 msgid "Procurement of stockable Product"
-msgstr ""
+msgstr "Nabava izdelka , ki se skladišči"
 
 #. module: mrp
 #: view:mrp.bom:0
@@ -361,7 +364,7 @@ msgstr "Potrdi Proizvodnjo"
 msgid ""
 "The system creates an order (production or purchased) depending on the sold "
 "quantity and the products parameters."
-msgstr ""
+msgstr "Sistem kreira nalog(delovni ali nabavni), odvisno od parametrov"
 
 #. module: mrp
 #: model:process.transition,note:mrp.process_transition_stockproduction0
@@ -386,7 +389,7 @@ msgstr ""
 msgid ""
 "This is the Internal Picking List that brings the finished product to the "
 "production plan"
-msgstr ""
+msgstr "To je interna dobavnica , ki prenese izdelke v plan proizvodnje"
 
 #. module: mrp
 #: model:ir.ui.menu,name:mrp.menu_view_resource_calendar_search_mrp
@@ -396,7 +399,7 @@ msgstr "Delovni čas"
 #. module: mrp
 #: model:ir.actions.act_window,name:mrp.action_report_in_out_picking_tree
 msgid "Weekly Stock Value Variation"
-msgstr ""
+msgstr "Tedensko nihanje vrednosti zalog"
 
 #. module: mrp
 #: view:mrp.production:0
@@ -426,7 +429,7 @@ msgstr ""
 #. module: mrp
 #: view:board.board:0
 msgid "Stock Value Variation"
-msgstr ""
+msgstr "Nihanje vrednosti zalog"
 
 #. module: mrp
 #: model:ir.actions.act_window,name:mrp.action2
@@ -436,7 +439,7 @@ msgstr "Struktura kosovnice"
 #. module: mrp
 #: model:process.node,note:mrp.process_node_serviceproduct0
 msgid "Product type is service"
-msgstr ""
+msgstr "Vrsta izdelka je usluga"
 
 #. module: mrp
 #: sql_constraint:res.company:0
@@ -505,12 +508,12 @@ msgstr ""
 #: view:mrp.routing:0
 #: field:mrp.routing,location_id:0
 msgid "Production Location"
-msgstr ""
+msgstr "Lokacija proizvodnje"
 
 #. module: mrp
 #: view:mrp.production:0
 msgid "Change Qty"
-msgstr ""
+msgstr "Spremeni količino"
 
 #. module: mrp
 #: model:ir.actions.act_window,name:mrp.action_configure_workcenter
@@ -520,7 +523,7 @@ msgstr ""
 #. module: mrp
 #: view:mrp.production:0
 msgid "Force Reservation"
-msgstr ""
+msgstr "Vsili rezervacijo"
 
 #. module: mrp
 #: field:report.mrp.inout,value:0
@@ -579,7 +582,7 @@ msgstr ""
 #. module: mrp
 #: field:mrp.workcenter,time_start:0
 msgid "Time before prod."
-msgstr ""
+msgstr "Čas pred proizvodnjo"
 
 #. module: mrp
 #: help:mrp.routing,active:0
@@ -591,14 +594,14 @@ msgstr ""
 #. module: mrp
 #: model:process.transition,name:mrp.process_transition_billofmaterialrouting0
 msgid "Material Routing"
-msgstr ""
+msgstr "Tok materiala"
 
 #. module: mrp
 #: view:mrp.production:0
 #: field:mrp.production,move_lines2:0
 #: report:mrp.production.order:0
 msgid "Consumed Products"
-msgstr ""
+msgstr "Porabljeni izdelki"
 
 #. module: mrp
 #: model:ir.actions.act_window,name:mrp.action_mrp_workcenter_load_wizard
@@ -617,7 +620,7 @@ msgstr "Izdelek nima kosovnice"
 #: model:ir.actions.act_window,name:mrp.mrp_bom_form_action2
 #: model:ir.ui.menu,name:mrp.menu_mrp_bom_form_action2
 msgid "Bill of Material Components"
-msgstr ""
+msgstr "Komponente sestavnice"
 
 #. module: mrp
 #: model:ir.model,name:mrp.model_stock_move
@@ -651,7 +654,7 @@ msgstr ""
 #. module: mrp
 #: help:mrp.workcenter,time_cycle:0
 msgid "Time in hours for doing one cycle."
-msgstr ""
+msgstr "Čas za končanje cikla"
 
 #. module: mrp
 #: constraint:mrp.bom:0
@@ -666,7 +669,7 @@ msgstr "V proizvodnji"
 #. module: mrp
 #: model:ir.ui.menu,name:mrp.menu_mrp_property
 msgid "Master Bill of Materials"
-msgstr ""
+msgstr "Glavna sastavnica"
 
 #. module: mrp
 #: help:mrp.bom,product_uos:0
@@ -695,7 +698,7 @@ msgstr ""
 #. module: mrp
 #: selection:mrp.workcenter.load,time_unit:0
 msgid "Per month"
-msgstr ""
+msgstr "Mesečno"
 
 #. module: mrp
 #: code:addons/mrp/wizard/change_production_qty.py:78
@@ -707,13 +710,13 @@ msgstr ""
 #. module: mrp
 #: report:bom.structure:0
 msgid "Product Name"
-msgstr ""
+msgstr "Ime Izdelka"
 
 #. module: mrp
 #: code:addons/mrp/mrp.py:503
 #, python-format
 msgid "Invalid action !"
-msgstr ""
+msgstr "Nepravilno dejanje!"
 
 #. module: mrp
 #: help:mrp.bom,product_efficiency:0
@@ -725,7 +728,7 @@ msgstr ""
 #: code:addons/mrp/mrp.py:762
 #, python-format
 msgid "Warning!"
-msgstr ""
+msgstr "Opozorilo!"
 
 #. module: mrp
 #: report:mrp.production.order:0
@@ -741,17 +744,17 @@ msgstr ""
 #. module: mrp
 #: model:process.transition,name:mrp.process_transition_producttostockrules0
 msgid "Procurement rule"
-msgstr ""
+msgstr "Pravilo nabave"
 
 #. module: mrp
 #: view:mrp.production:0
 msgid "Partial"
-msgstr ""
+msgstr "Delno"
 
 #. module: mrp
 #: report:mrp.production.order:0
 msgid "WorkCenter"
-msgstr ""
+msgstr "Proizvodni center"
 
 #. module: mrp
 #: model:process.transition,note:mrp.process_transition_procureserviceproduct0
@@ -769,7 +772,7 @@ msgstr "Nujno"
 #. module: mrp
 #: view:mrp.production:0
 msgid "Manufacturing Orders which are waiting for raw materials."
-msgstr ""
+msgstr "Delovni nalogi , ki čakajo na material"
 
 #. module: mrp
 #: model:ir.actions.act_window,help:mrp.mrp_workcenter_action
@@ -780,16 +783,18 @@ msgid ""
 "resource leave are not taken into account in the time computation of the "
 "work center."
 msgstr ""
+"Delovno enoto sestavljajo delavci in/ali stroji , ki predstavljajo eno enoto "
+"za planiranje kapacitet."
 
 #. module: mrp
 #: model:ir.model,name:mrp.model_mrp_production
 msgid "Manufacturing Order"
-msgstr ""
+msgstr "Delovni nalog"
 
 #. module: mrp
 #: model:process.transition,name:mrp.process_transition_productionprocureproducts0
 msgid "Procurement of raw material"
-msgstr ""
+msgstr "Nabava surovin"
 
 #. module: mrp
 #: view:mrp.production:0
@@ -806,7 +811,7 @@ msgstr ""
 #: view:mrp.bom:0
 #: view:mrp.production:0
 msgid "Date"
-msgstr ""
+msgstr "Datum"
 
 #. module: mrp
 #: field:mrp.bom,type:0
@@ -823,7 +828,7 @@ msgstr ""
 #. module: mrp
 #: view:mrp.property:0
 msgid "Search"
-msgstr ""
+msgstr "Iskanje"
 
 #. module: mrp
 #: code:addons/mrp/mrp.py:626
@@ -884,7 +889,7 @@ msgstr "Možno dati v zalogo"
 #: code:addons/mrp/report/price.py:130
 #, python-format
 msgid "Work Center name"
-msgstr ""
+msgstr "Ime delovne eote"
 
 #. module: mrp
 #: field:mrp.routing,code:0
@@ -1034,7 +1039,7 @@ msgstr ""
 #. module: mrp
 #: help:mrp.workcenter,costs_hour:0
 msgid "Specify Cost of Work Center per hour."
-msgstr ""
+msgstr "Cena delovne enote na uro"
 
 #. module: mrp
 #: help:mrp.workcenter,capacity_per_cycle:0
@@ -1095,7 +1100,7 @@ msgstr "Analitični dnevnik"
 #: model:ir.ui.menu,name:mrp.menu_view_resource_search_mrp
 #: field:mrp.routing,workcenter_lines:0
 msgid "Work Centers"
-msgstr ""
+msgstr "Delovne enote"
 
 #. module: mrp
 #: selection:mrp.workcenter.load,time_unit:0
@@ -1110,6 +1115,8 @@ msgid ""
 "They are attached to bills of materials that will define the required raw "
 "materials."
 msgstr ""
+"Delovni tok opredeljuje zaporedje delovnih enot in operacij za izdelavo "
+"določenega izdelka."
 
 #. module: mrp
 #: model:ir.actions.act_window,name:mrp.product_form_config_action
@@ -1152,7 +1159,7 @@ msgstr ""
 #. module: mrp
 #: view:report.workcenter.load:0
 msgid "Work Center load"
-msgstr ""
+msgstr "Zasedenost delovnega centra"
 
 #. module: mrp
 #: help:mrp.production,location_dest_id:0
@@ -1853,7 +1860,7 @@ msgstr ""
 #. module: mrp
 #: field:mrp.routing.workcenter,routing_id:0
 msgid "Parent Routing"
-msgstr ""
+msgstr "Nadrejeni delovni tok"
 
 #. module: mrp
 #: help:mrp.workcenter,time_start:0
@@ -2013,7 +2020,7 @@ msgstr ""
 #: view:mrp.routing:0
 #: model:process.node,name:mrp.process_node_routing0
 msgid "Routing"
-msgstr "Usmerjanje"
+msgstr "Delovni tok"
 
 #. module: mrp
 #: field:mrp.production,date_planned:0
index 527505d..b1e0474 100644 (file)
@@ -111,7 +111,7 @@ class report_custom(report_rml):
             <PageWidth>595.27</PageWidth>
             <PageHeight>841.88</PageHeight>
             <tableSize>55.00mm,58.00mm,29.00mm,29.00mm,29.00mm</tableSize>
-            """ % (user_pool.browse(cr, uid, uid).company_id.name)
+            """ % to_xml(user_pool.browse(cr, uid, uid).company_id.name)
         config_stop = """
             <report-footer>Generated by OpenERP</report-footer>
         </config>
index c1712b8..ec3deb5 100644 (file)
@@ -28,10 +28,6 @@ class mrp_config_settings(osv.osv_memory):
     _inherit = 'res.config.settings'
 
     _columns = {
-        'module_stock_planning': fields.boolean('Manage master production shedule',
-            help ="""This allows to create a manual procurement plan apart of the normal MRP scheduling,
-                which works automatically based on minimum stock rules.
-                This installs the module stock_planning."""),
         'module_mrp_repair': fields.boolean("Manage repairs of products ",
             help="""Allows to manage all product repairs.
                     * Add/remove products in the reparation
index 6b4058f..2c70473 100644 (file)
                                 <field name="module_mrp_operations" class="oe_inline"/>
                                 <label for="module_mrp_operations" />
                             </div>
-                            <div>
-                                <field name="module_stock_planning" class="oe_inline"/>
-                                <label for="module_stock_planning"/>
-                            </div>
                         </div>
                     </group>
                     <separator string="Master Data"/>
index bc9b941..d79620c 100644 (file)
@@ -690,7 +690,7 @@ class mrp_repair_line(osv.osv, ProductChangeMixin):
         'repair_id': fields.many2one('mrp.repair', 'Repair Order Reference',ondelete='cascade', select=True),
         'type': fields.selection([('add','Add'),('remove','Remove')],'Type', required=True),
         'to_invoice': fields.boolean('To Invoice'),
-        'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok','=',True)], required=True),
+        'product_id': fields.many2one('product.product', 'Product', required=True),
         'invoiced': fields.boolean('Invoiced',readonly=True),
         'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
         'price_subtotal': fields.function(_amount_line, string='Subtotal',digits_compute= dp.get_precision('Account')),
index 18a3ec8..5aae620 100644 (file)
                 <field name="memo" widget="html" class="oe_memo" editor_height="450px" />
                 <div class="oe_chatter">
                     <field name="message_follower_ids" widget="mail_followers"/>
-                    <field class="oe_chatter" name="message_ids" widget="mail_thread"/>
+                    <field name="message_ids" widget="mail_thread"/>
                 </div>
             </form>
         </field>
index 1b7e931..16d71e2 100644 (file)
@@ -49,8 +49,8 @@
   padding: 8px;
   margin-left: 3px;
   margin-right: 3px;
-  padding-bottom: 16px;
-  margin-bottom: 16px;
+  padding-bottom: 8px;
+  margin-bottom: 10px;
   -webkit-transform: rotate(-2deg);
   -o-transform: rotate(-2deg);
   -moz-transform: rotate(-2deg);
index b5bc28a..6f0c088 100644 (file)
@@ -53,8 +53,8 @@
       padding: 8px
       margin-left: 3px
       margin-right: 3px
-      padding-bottom: 16px
-      margin-bottom: 16px
+      padding-bottom: 8px
+      margin-bottom: 10px
       @include rotate(-2deg)
       @include transition(all, 300ms)
   .oe_kanban_record:nth-of-type(even)
index e5b36e5..932fc5a 100644 (file)
@@ -19,7 +19,7 @@ class pad_common(osv.osv_memory):
 
         pad = {
             "server" : company.pad_server,
-            "key" : company.pad_key or "4DxmsNIbnQUVQMW9S9tx2oLOSjFdrx1l",
+            "key" : company.pad_key,
         }
 
         # make sure pad server in the form of http://hostname
index 407f90e..99c4b38 100644 (file)
@@ -1,9 +1,13 @@
 <openerp>
-  <data noupdate="1">
+  <data>
 
     <record id="base.main_company" model="res.company">
       <field name="pad_server">pad.openerp.com</field>
     </record>
+    
+    <record id="base.main_company" model="res.company">
+      <field name="pad_key">4DxmsNIbnQUVQMW9S9tx2oLOSjFdrx1l</field>
+    </record>
 
   </data>
 </openerp>
index c125264..929d626 100644 (file)
@@ -12,8 +12,9 @@ openerp.pad = function(instance) {
                         model: self.view.model,
                         field_name: self.name,
                         object_id: self.view.datarecord.id
-                    }}).then(function(data) {
+                    }}).done(function(data) {
                     if(data&&data.url){
+                        self.set({value: data.url});
                         _super(data.url);
                         self.renderElement();
                     }
index e99fbf7..91c51e8 100644 (file)
                     <form position="attributes">
                         <attribute name="string">Install Outlook Plug-In</attribute>
                     </form>
-                    <xpath expr="//footer" position="replace"/>
+                    <xpath expr="//footer" position="replace">
+                    <footer>
+                        <button string="Close" special="Cancel" class="oe_highlight"/>
+                    </footer>
+                    </xpath>
                     <xpath expr="//separator[@string='title']" position="after" version="7.0">
                         <group>
                             <field name="plugin32" widget="url"/>
                         <separator string="Installation and Configuration Steps"/>
                         <p>Click on the link above to download the installer for either 32 or 64 bits, and execute it.</p>
                         <p>System requirements:</p>
-                        <ul>
-                            <li>1. MS Outlook 2005 or above.</li>
-                            <li>2. MS .Net Framework 3.5 or above.</li>
-                        </ul>
+                        <ol>
+                            <li> MS Outlook 2005 or above.</li>
+                            <li> MS .Net Framework 3.5 or above.</li>
+                        </ol>
                     </xpath>
                     <separator string="title" position="attributes">
                         <attribute name="string"></attribute>
@@ -51,7 +55,7 @@
                     <div>
                         <field name="module_plugin_outlook" class="oe_inline"/>
                         <label for="module_plugin_outlook"/>
-                        <button name="%(action_outlook_installer)d" type="action" string="Download and install the plugin" icon="gtk-execute"
+                        <button name="%(action_outlook_installer)d" type="action" string="Download and install the plug-in" icon="gtk-execute"
                             attrs="{'invisible': [('module_plugin_outlook','=',False)]}" class="oe_link"/>
                     </div>
                 </div>
index 6473758..923611b 100644 (file)
@@ -30,13 +30,11 @@ class plugin_thunderbird_installer(osv.osv_memory):
         'thunderbird': fields.boolean('Thunderbird Plug-in', help="Allows you to select an object that you would like to add to your email and its attachments."),
         'plugin_name': fields.char('File name', size=64),
         'plugin_file': fields.char('Thunderbird Plug-in', size=256, readonly=True, help="Thunderbird plug-in file. Save this file and install it in Thunderbird."),
-        'pdf_file': fields.char('Installation Manual', size=256, help="The documentation file :- how to install Thunderbird Plug-in.", readonly=True),
     }
 
     _defaults = {
         'thunderbird': True,
         'plugin_name': 'openerp_plugin.xpi',
-        'pdf_file': 'http://doc.openerp.com/book/2/2_6_Comms/2_6_Comms_thunderbird.html',
     }
 
     def default_get(self, cr, uid, fields, context=None):
@@ -45,4 +43,4 @@ class plugin_thunderbird_installer(osv.osv_memory):
         res['plugin_file'] = base_url + '/plugin_thunderbird/static/openerp_plugin.xpi'
         return res
 
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 5ea5fc3..3774a36 100644 (file)
                     <form position="attributes">
                         <attribute name="string">Install Thunderbird Plug-In</attribute>
                     </form>
-                    <xpath expr="//footer" position="replace"/>
+                    <xpath expr="//footer" position="replace">
+                    <footer>
+                        <button string="Close" special="Cancel" class="oe_highlight"/>
+                    </footer>
+                    </xpath>
                     <xpath expr="//separator[@string='title']" position="after">
                         <group>
                             <field name="thunderbird" invisible="1"/>
                             <field name="plugin_name" invisible="1"/>
                             <field name="plugin_file" filename="plugin_name" widget="url"/>
-                            <field name="pdf_file" widget="url"/>
                         </group>
                         <separator string="Installation and Configuration Steps"/>
-                        <p>Thunderbird plugin installation:</p>
-                        <ul>
-                            <li>1. Save the Thunderbird plug-in.</li>
-                            <li>2. From the Thunderbird menubar: Tools ­> Add-ons -> Screwdriver/Wrench Icon -> Install add-on from file...</li>
-                            <li>3. Select the plug-in (the file named openerp_plugin.xpi).</li>
-                            <li>4. Click "Install Now".</li>
-                            <li>5. Restart Thunderbird.</li>
-                            <li>6. From the Thunderbird menubar: OpenERP -> Configuration.</li>
-                            <li>7. Configure your openerp server.</li>
-                        </ul>
+                        <p>Thunderbird plug-in installation:</p>
+                        <ol>
+                            <li> Save the Thunderbird plug-in.</li>
+                            <li> From the Thunderbird menubar: Tools ­> Add-ons -> Screwdriver/Wrench Icon -> Install add-on from file...</li>
+                            <li> Select the plug-in (the file named openerp_plugin.xpi).</li>
+                            <li> Click "Install Now".</li>
+                            <li> Restart Thunderbird.</li>
+                            <li> From the Thunderbird menubar: OpenERP -> Configuration.</li>
+                            <li> Configure your openerp server.</li>
+                        </ol>
                     </xpath>
                     <separator string="title" position="attributes">
                         <attribute name="string"></attribute>
@@ -57,7 +60,7 @@
                     <div>
                         <field name="module_plugin_thunderbird" class="oe_inline"/>
                         <label for="module_plugin_thunderbird"/>
-                        <button name="%(action_thunderbird_installer)d" type="action" string="Download and install the plugin" icon="gtk-execute"
+                        <button name="%(action_thunderbird_installer)d" type="action" string="Download and install the plug-in" icon="gtk-execute"
                             attrs="{'invisible': [('module_plugin_thunderbird','=',False)]}" class="oe_link"/>
                     </div>
                 </div>
index 2ee6b2e..58044c2 100644 (file)
@@ -34,7 +34,7 @@ class order(report_sxw.rml_parse):
     def __init__(self, cr, uid, name, context):
         super(order, self).__init__(cr, uid, name, context=context)
 
-        user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid)
+        user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, uid, context=context)
         partner = user.company_id.partner_id
 
         self.localcontext.update({
index c96aff3..c5580ec 100644 (file)
@@ -37,12 +37,11 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
                 callbacks[i](params);
             }
 
-            this.connection.rpc('/pos/'+name, params || {}).then(function(result){
-                        ret.resolve(result);
-                    },
-                    function(error){
-                        ret.reject(error);
-                    });
+            this.connection.rpc('/pos/' + name, params || {}).done(function(result) {
+                ret.resolve(result);
+            }).fail(function(error) {
+                ret.reject(error);
+            });
             return ret;
         },
 
@@ -96,7 +95,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
                 return this.weight;
             }else{
                 this.message('weighting_read_kg',{})
-                    .then(function(weight){
+                    .done(function(weight){
                         if(self.weighting && !self.bypass_proxy){
                             self.weight = weight;
                         }
index d7d6d31..7301a85 100644 (file)
@@ -59,10 +59,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
             // Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched. 
             // when all the data has loaded, we compute some stuff, and declare the Pos ready to be used. 
             $.when(this.load_server_data())
-                .then(function(){ 
+                .done(function(){
                     //self.log_loaded_data(); //Uncomment if you want to log the data to the console for easier debugging
                     self.ready.resolve();
-                },function(){
+                }).fail(function(){
                     //we failed to load some backend data, or the backend was badly configured.
                     //the error messages will be displayed in PosWidget
                     self.ready.reject();
@@ -78,7 +78,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
             var self = this;
 
             var loaded = self.fetch('res.users',['name','company_id'],[['id','=',this.session.uid]]) 
-                .pipe(function(users){
+                .then(function(users){
                     self.set('user',users[0]);
 
                     return self.fetch('res.company',
@@ -93,19 +93,19 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                         'partner_id',
                     ],
                     [['id','=',users[0].company_id[0]]]);
-                }).pipe(function(companies){
+                }).then(function(companies){
                     self.set('company',companies[0]);
 
                     return self.fetch('res.partner',['contact_address'],[['id','=',companies[0].partner_id[0]]]);
-                }).pipe(function(company_partners){
+                }).then(function(company_partners){
                     self.get('company').contact_address = company_partners[0].contact_address;
 
                     return self.fetch('res.currency',['symbol','position'],[['id','=',self.get('company').currency_id[0]]]);
-                }).pipe(function(currencies){
+                }).then(function(currencies){
                     self.set('currency',currencies[0]);
 
                     return self.fetch('product.uom', null, null);
-                }).pipe(function(units){
+                }).then(function(units){
                     self.set('units',units);
                     var units_by_id = {};
                     for(var i = 0, len = units.length; i < len; i++){
@@ -114,15 +114,15 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                     self.set('units_by_id',units_by_id);
                     
                     return self.fetch('product.packaging', null, null);
-                }).pipe(function(packagings){
+                }).then(function(packagings){
                     self.set('product.packaging',packagings);
 
                     return self.fetch('res.users', ['name','ean13'], [['ean13', '!=', false]]);
-                }).pipe(function(users){
+                }).then(function(users){
                     self.set('user_list',users);
 
                     return self.fetch('account.tax', ['amount', 'price_include', 'type']);
-                }).pipe(function(taxes){
+                }).then(function(taxes){
                     self.set('taxes', taxes);
 
                     return self.fetch(
@@ -130,7 +130,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                         ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at'],
                         [['state', '=', 'opened'], ['user_id', '=', self.session.uid]]
                     );
-                }).pipe(function(sessions){
+                }).then(function(sessions){
                     self.set('pos_session', sessions[0]);
 
                     return self.fetch(
@@ -141,7 +141,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                          'iface_print_via_proxy','iface_cashdrawer','state','sequence_id','session_ids'],
                         [['id','=', self.get('pos_session').config_id[0]]]
                     );
-                }).pipe(function(configs){
+                }).then(function(configs){
                     var pos_config = configs[0];
                     self.set('pos_config', pos_config);
                     self.iface_electronic_scale    =  !!pos_config.iface_electronic_scale;  
@@ -151,15 +151,15 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                     self.iface_cashdrawer          =  !!pos_config.iface_cashdrawer;
 
                     return self.fetch('sale.shop',[],[['id','=',pos_config.shop_id[0]]]);
-                }).pipe(function(shops){
+                }).then(function(shops){
                     self.set('shop',shops[0]);
 
                     return self.fetch('product.packaging',['ean','product_id']);
-                }).pipe(function(packagings){
+                }).then(function(packagings){
                     self.db.add_packagings(packagings);
 
                     return self.fetch('pos.category', ['id','name','parent_id','child_id','image'])
-                }).pipe(function(categories){
+                }).then(function(categories){
                     self.db.add_categories(categories);
 
                     return self.fetch(
@@ -169,7 +169,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                         [['pos_categ_id','!=', false],['sale_ok','=',true]],
                         {pricelist: self.get('shop').pricelist_id[0]} // context for price
                     );
-                }).pipe(function(products){
+                }).then(function(products){
                     self.db.add_products(products);
 
                     return self.fetch(
@@ -177,14 +177,14 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
                         ['account_id','currency','journal_id','state','name','user_id','pos_session_id'],
                         [['state','=','open'],['pos_session_id', '=', self.get('pos_session').id]]
                     );
-                }).pipe(function(bank_statements){
+                }).then(function(bank_statements){
                     var journals = new Array();
                     _.each(bank_statements,function(statement) {
                         journals.push(statement.journal_id[0])
                     });
                     self.set('bank_statements', bank_statements);
                     return self.fetch('account.journal', undefined, [['id','in', journals]]);
-                }).pipe(function(journals){
+                }).then(function(journals){
                     self.set('journals',journals);
 
                     // associate the bank statements with their journals. 
index 8028349..0b8ab70 100644 (file)
@@ -557,7 +557,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                 setTimeout(function(){
                     var def = job.fun();
                     if(def){
-                        def.then(run); 
+                        def.done(run);
                     }else{
                         run();
                     }
@@ -615,7 +615,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                 var def = new $.Deferred();
                 console.log("START");
                 self.pos.proxy.payment_request(self.pos.get('selectedOrder').getDueLeft())
-                    .then(function(ack){
+                    .done(function(ack){
                         if(ack === 'ok'){
                             self.queue.schedule(self.update);
                         }else if(ack.indexOf('error') === 0){
@@ -638,7 +638,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                     return def.resolve();
                 }
                 self.pos.proxy.payment_status()
-                    .then(function(status){
+                    .done(function(status){
                         if(status.status === 'paid'){
 
                             var currentOrder = self.pos.get('selectedOrder');
@@ -941,8 +941,10 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             var self = this;
             var x = new module.PaymentlineWidget(null, {
                     payment_line: newPaymentLine
-                });
-            x.on('delete_payment_line', self, self.deleteLine);
+            });
+            x.on('delete_payment_line', self, function(r) {
+                self.deleteLine(r);
+            });
             x.appendTo(this.$('#paymentlines'));
         },
         renderElement: function() {
index ed9b1fd..c7874a4 100644 (file)
@@ -315,7 +315,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             this._super();
             this.$('input').keyup(_.bind(this.changeAmount, this));
             this.$('.delete-payment-line').click(function() {
-                self.trigger('delete_payment_line');
+                self.trigger('delete_payment_line', self);
             });
         },
     });
@@ -806,7 +806,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
       
         start: function() {
             var self = this;
-            return self.pos.ready.then(function() {
+            return self.pos.ready.done(function() {
                 self.build_currency_template();
                 self.renderElement();
                 
@@ -845,7 +845,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                 self.$('.loader').animate({opacity:0},1500,'swing',function(){self.$('.loader').hide();});
                 self.$('.loader img').hide();
 
-            },function(){   // error when loading models data from the backend
+            }).fail(function(){   // error when loading models data from the backend
                 self.$('.loader img').hide();
                 return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id'])
                     .pipe( _.bind(function(res){
@@ -1051,7 +1051,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             this.pos.barcode_reader.disconnect();
             return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']).pipe(
                     _.bind(function(res) {
-                return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(action) {
+                return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(result) {
+                    var action = result;
                     action.context = _.extend(action.context || {}, {'cancel_action': {type: 'ir.actions.client', tag: 'reload'}});
                     //self.destroy();
                     this.do_action(action);
index 9511aed..70b88f3 100644 (file)
@@ -75,7 +75,7 @@ class pos_make_payment(osv.osv_memory):
 
     def launch_payment(self, cr, uid, ids, context=None):
         return {
-            'name': _('Paiement'),
+            'name': _('Payment'),
             'view_type': 'form',
             'view_mode': 'form',
             'res_model': 'pos.make.payment',
@@ -83,6 +83,7 @@ class pos_make_payment(osv.osv_memory):
             'target': 'new',
             'views': False,
             'type': 'ir.actions.act_window',
+            'context': context,
         }
 
     def print_report(self, cr, uid, ids, context=None):
index b1b6e98..51e5e59 100644 (file)
@@ -35,8 +35,8 @@ class share_wizard_portal(osv.TransientModel):
 
     def _user_type_selection(self, cr, uid, context=None):
         selection = super(share_wizard_portal, self)._user_type_selection(cr, uid, context=context)
-        selection.extend([('existing','Users you already shared with'),
-                          ('groups','Existing Groups (e.g Portal Groups)')])
+        selection.extend([('existing',_('Users you already shared with')),
+                          ('groups',_('Existing Groups (e.g Portal Groups)'))])
         return selection
 
     _columns = {
index 86be076..e08b39e 100644 (file)
             <field name="view_id" ref="crm_claim.crm_case_claims_tree_view"/>
             <field name="context">{"search_default_user_id":'', "stage_type":'claim'}</field>
             <field name="search_view_id" ref="crm_claim.view_crm_case_claims_filter"/>
-            <field name="help">Record and track your customers' claims. Claims may be linked to a sales order or a lot. You can send emails with attachments and keep the full history for a claim (emails sent, intervention type and so on). Claims may automatically be linked to an email address using the mail gateway module.</field>
+            <field name="help"  type="html">
+              <p class="oe_view_nocontent_create">
+                Click to register a new claim. 
+              </p><p>
+                You can track your claims from this menu and the action we
+                will take.
+              </p>
+            </field>
         </record>
 
         <menuitem name="Claims" id="portal_after_sales_claims" parent="portal.portal_after_sales" 
index 1e046bc..5d2397f 100755 (executable)
@@ -13,7 +13,7 @@
             <field name="view_mode">kanban,calendar,tree,form,graph</field>
             <field name="context">{"search_default_upcoming":1}</field>
             <field name="search_view_id" ref="event.view_event_search"/>
-            <field name="help">No public events.</field>
+            <field name="help">There are no public events.</field>
         </record>
 
         <menuitem name="Events" id="portal_company_events" parent="portal.portal_company"
index 6b25d87..b46175f 100644 (file)
@@ -45,7 +45,7 @@
             <field name="view_mode">kanban,tree,form</field>
             <field name="view_id" ref="product.product_kanban_view"/>
             <field name="search_view_id" ref="product.product_search_form_view"/>
-            <field name="help">No public products.</field>
+            <field name="help">There are no public products.</field>
         </record>
 
         <record id="action_invoice_tree1" model="ir.actions.act_window">
@@ -65,7 +65,7 @@
             <field name="context">{'type':'receipt'}</field>
             <field name="search_view_id" ref="account_voucher.view_voucher_filter_customer_pay"/>
             <field name="target">current</field>
-            <field name="help">You don't have any payment.</field>
+            <field name="help">You don't have any refunds or payments.</field>
         </record>
 
         <menuitem name="Quotations" id="portal_quotations" parent="portal.portal_orders"
index c4bf613..39dabf7 100644 (file)
@@ -42,6 +42,7 @@
             <field name="name">Portal Personal Contacts</field>
             <field name="model_id" ref="base.model_res_partner"/>
             <field name="domain_force">[('message_follower_ids','in',[user.partner_id.id])]</field>
+            <field eval="0" name="perm_read"/>
             <field name="groups" eval="[(4, ref('portal.group_portal'))]"/>
         </record>
 
index 2405f6d..73ca439 100644 (file)
@@ -20,7 +20,7 @@ instance.web.ViewManager.include({
         if(this.active_view == 'form') {
             this.record_id = this.views[this.active_view].controller.datarecord.id;
         }
-        this.process_get_object().pipe(function(process) {
+        this.process_get_object().then(function(process) {
             if(process && process.length) {
                 if(process.length > 1) {
                     self.process_selection = process;
@@ -30,7 +30,7 @@ instance.web.ViewManager.include({
                 }
             }
             return $.Deferred().resolve();
-        }).pipe(function() {
+        }).then(function() {
             var def = $.Deferred();
             if(self.process_id) {
                 $.when(self.process_graph_get()).done(function(res) {
@@ -221,12 +221,12 @@ instance.web.ViewManager.include({
         var dataset = new instance.web.DataSet(this, 'ir.values', this.session.user_context);
         var action_manager = new instance.web.ActionManager(self);
         dataset.call('get',
-            ['action', 'tree_but_open',[['ir.ui.menu', id]], dataset.context]).then(function(res) {
+            ['action', 'tree_but_open',[['ir.ui.menu', id]], dataset.context]).done(function(res) {
                 var action = res[0][res[0].length - 1];
                 self.rpc("/web/action/load", {
                     action_id: action.id,
                     context: dataset.context
-                    }).then(function(result) {
+                    }).done(function(result) {
                         action_manager.replace(self.$el);
                         action_manager.do_action(result.result);
                     })
index b6a58fc..27cd424 100644 (file)
@@ -657,9 +657,9 @@ class product_product(osv.osv):
                 # Performing a quick memory merge of ids in Python will give much better performance
                 ids = set()
                 ids.update(self.search(cr, user, args + [('default_code',operator,name)], limit=limit, context=context))
-                if len(ids) < limit:
+                if not limit or len(ids) < limit:
                     # we may underrun the limit because of dupes in the results, that's fine
-                    ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit-len(ids)), context=context))
+                    ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit and (limit-len(ids)) or False) , context=context))
                 ids = list(ids)
             if not ids:
                 ptrn = re.compile('(\[(.*?)\])')
index c16ccc2..e4ed28d 100644 (file)
@@ -45,12 +45,13 @@ class stock_production_lot(osv.osv):
 
     _columns = {
         'life_date': fields.datetime('End of Life Date',
-            help='The date on which the lot may become dangerous and should not be consumed.'),
+            help='This is the date on which the goods with this Serial Number may become dangerous and must not be consumed.'),
         'use_date': fields.datetime('Best before Date',
-            help='The date on which the lot starts deteriorating without becoming dangerous.'),
+            help='This is the date on which the goods with this Serial Number start deteriorating, without being dangerous yet.'),
         'removal_date': fields.datetime('Removal Date',
-            help='The date on which the lot should be removed.'),
-        'alert_date': fields.datetime('Alert Date', help="The date on which an alert should be notified about the serial number."),
+            help='This is the date on which the goods with this Serial Number should be removed from the stock.'),
+        'alert_date': fields.datetime('Alert Date',
+            help="This is the date on which an alert should be notified about the goods with this Serial Number."),
     }
     # Assign dates according to products data
     def create(self, cr, uid, vals, context=None):
@@ -78,12 +79,13 @@ class product_product(osv.osv):
     _inherit = 'product.product'
     _columns = {
         'life_time': fields.integer('Product Life Time',
-            help='The number of days before a serial number may become dangerous and should not be consumed.'),
+            help='When a new a Serial Number is issued, this is the number of days before the goods may become dangerous and must not be consumed.'),
         'use_time': fields.integer('Product Use Time',
-            help='The number of days before a serial number starts deteriorating without becoming dangerous.'),
+            help='When a new a Serial Number is issued, this is the number of days before the goods starts deteriorating, without being dangerous yet.'),
         'removal_time': fields.integer('Product Removal Time',
-            help='The number of days before a serial number should be removed.'),
-        'alert_time': fields.integer('Product Alert Time', help="The number of days after which an alert should be notified about the serial number."),
+            help='When a new a Serial Number is issued, this is the number of days before the goods should be removed from the stock.'),
+        'alert_time': fields.integer('Product Alert Time',
+            help='When a new a Serial Number is issued, this is the number of days before an alert should be notified.'),
     }
 product_product()
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 2811f63..9c28fad 100644 (file)
@@ -166,17 +166,17 @@ class project(osv.osv):
                 res[id]['progress_rate'] = 0.0
         return res
 
-    def unlink(self, cr, uid, ids, *args, **kwargs):
+    def unlink(self, cr, uid, ids, context=None):
         alias_ids = []
         mail_alias = self.pool.get('mail.alias')
-        for proj in self.browse(cr, uid, ids):
+        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)
-        res =  super(project, self).unlink(cr, uid, ids, *args, **kwargs)
-        mail_alias.unlink(cr, uid, alias_ids, *args, **kwargs)
+        res =  super(project, self).unlink(cr, uid, ids, context=context)
+        mail_alias.unlink(cr, uid, alias_ids, context=context)
         return res
     
     def _get_attached_docs(self, cr, uid, ids, field_name, arg, context):
@@ -203,7 +203,11 @@ class project(osv.osv):
     
     def attachment_tree_view(self, cr, uid, ids, context):
         task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)])
-        domain = ['|', '&', ('res_model', '=', 'project.project'), ('res_id', 'in', ids), '&', ('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)]
+        domain = [
+             '|', 
+             '&', ('res_model', '=', 'project.project'), ('res_id', 'in', ids),
+             '&', ('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)
+               ]
         res_id = ids and ids[0] or False
         return {
             'name': _('Attachments'),
@@ -470,7 +474,7 @@ def Project():
     resource = %s
 """       % (
             project.id,
-            project.date_start, working_days,
+            project.date_start or time.strftime('%Y-%m-%d'), working_days,
             '|'.join(['User_'+str(x) for x in puids])
         )
         vacation = calendar_id and tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context)) or False
@@ -1301,7 +1305,17 @@ class task(base_stage, osv.osv):
             msg = _('Task has been <b>delegated</b> to <em>%s</em>.') % (task.user_id.name)
             self.message_post(cr, uid, [task.id], body=msg, context=context)
         return True
-
+   
+    def project_task_reevaluate(self, cr, uid, ids, context=None):
+        if self.pool.get('res.users').has_group(cr, uid, 'project.group_time_work_estimation_tasks'):
+            return {
+                'view_type': 'form',
+                "view_mode": 'form',
+                'res_model': 'project.task.reevaluate',
+                'type': 'ir.actions.act_window',
+                'target': 'new',
+            }
+        return self.do_reopen(cr, uid, ids, context=context)
 
 class project_work(osv.osv):
     _name = "project.task.work"
index ec29b19..e7f12ff 100644 (file)
             <field name="model">project.project</field>
             <field name="arch" type="xml">
                 <search string="Search Project">
-                    <field name="complete_name" string="Project Name"/>
+                    <field name="name" string="Project Name"/>
                     <filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
                     <separator/>
                     <filter icon="terp-check" string="Open" name="Current" domain="[('state', '=','open')]" help="Open Projects"/>
                         <button name="do_draft" string="Draft" type="object"
                                 states="cancel,done"/>
                         -->
-                        <button name="%(action_project_task_reevaluate)d" string="Reactivate" type="action"
+                        <button name="project_task_reevaluate" string="Reactivate" type="object"
                                 states="cancelled,done" context="{'button_reactivate':True}" groups="base.group_user"/>
                         <button name="action_close" string="Done" type="object"
                                 states="draft,open,pending" groups="base.group_user"/>
index 59eeba4..38578a6 100644 (file)
@@ -16,7 +16,7 @@ openerp.project = function(openerp) {
 
             // Find their matching names
             var dataset = new openerp.web.DataSetSearch(self, 'res.users', self.session.context, [['id', 'in', _.uniq(members_ids)]]);
-            dataset.read_slice(['id', 'name']).then(function(result) {
+            dataset.read_slice(['id', 'name']).done(function(result) {
                 _.each(result, function(v, k) {
                     // Set the proper value in the DOM
                     self.$el.find('img[data-member_id=' + v.id + ']').attr('title', v.name).tipsy({
@@ -41,7 +41,7 @@ openerp.project = function(openerp) {
 
             // Find their matching names
             var dataset = new openerp.web.DataSetSearch(self, 'project.category', self.session.context, [['id', 'in', _.uniq(categ_ids)]]);
-            dataset.read_slice(['id', 'name']).then(function(result) {
+            dataset.read_slice(['id', 'name']).done(function(result) {
                 _.each(result, function(v, k) {
                     // Set the proper value in the DOM and display the element
                     self.$el.find('span[data-categ_id=' + v.id + ']').text(v.name);
index 3f85e5c..439ff6d 100644 (file)
@@ -199,8 +199,8 @@ class project_work(osv.osv):
 
                 if amount_unit and 'amount' in amount_unit.get('value',{}):
                     vals_line['amount'] = amount_unit['value']['amount']
-            if line_id.sheet_id.state in ['confirm' 'done']:
-                self.pool.get('hr.analytic.timesheet').write(cr, uid, [line_id.id], vals_line, context=context)
+
+            self.pool.get('hr.analytic.timesheet').write(cr, uid, [line_id.id], vals_line, context=context)
 
         return super(project_work,self).write(cr, uid, ids, vals, context)
 
@@ -241,6 +241,7 @@ class task(osv.osv):
             if vals.get('project_id',False):
                 project_obj = self.pool.get('project.project').browse(cr, uid, vals['project_id'], context=context)
                 acc_id = project_obj.analytic_account_id.id
+
             for task_obj in self.browse(cr, uid, ids, context=context):
                 if len(task_obj.work_ids):
                     for task_work in task_obj.work_ids:
index daad446..fab7eea 100644 (file)
@@ -40,7 +40,7 @@
             <field name="name">Automated Purchase Order Notification Mail</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_to">${object.partner_id.email}</field>
+            <field name="email_recipients">${object.partner_id.id}</field>
             <field name="model_id" ref="purchase.model_purchase_order"/>
             <field name="auto_delete" eval="True"/>
             <field name="body_html"><![CDATA[
index 7d17364..5bb3831 100644 (file)
@@ -46,7 +46,7 @@ class purchase_order(osv.osv):
             cur = order.pricelist_id.currency_id
             for line in order.order_line:
                val1 += line.price_subtotal
-               for c in self.pool.get('account.tax').compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id.id, order.partner_id)['taxes']:
+               for c in self.pool.get('account.tax').compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id, order.partner_id)['taxes']:
                     val += c.get('amount', 0.0)
             res[order.id]['amount_tax']=cur_obj.round(cr, uid, cur, val)
             res[order.id]['amount_untaxed']=cur_obj.round(cr, uid, cur, val1)
@@ -293,6 +293,27 @@ class purchase_order(osv.osv):
         fiscal_position = supplier.property_account_position and supplier.property_account_position.id or False
         return {'value':{'pricelist_id': pricelist, 'fiscal_position': fiscal_position}}
 
+    def invoice_open(self, cr, uid, ids, context=None):
+        mod_obj = self.pool.get('ir.model.data')
+        act_obj = self.pool.get('ir.actions.act_window')
+
+        result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree2')
+        id = result and result[1] or False
+        result = act_obj.read(cr, uid, [id], context=context)[0]
+        inv_ids = []
+        for po in self.browse(cr, uid, ids, context=context):
+            inv_ids+= [invoice.id for invoice in po.invoice_ids]
+        if not inv_ids:
+            raise osv.except_osv(_('Error!'), _('Please create Invoices.'))
+         #choose the view_mode accordingly
+        if len(inv_ids)>1:
+            result['domain'] = "[('id','in',["+','.join(map(str, inv_ids))+"])]"
+        else:
+            res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_supplier_form')
+            result['views'] = [(res and res[1] or False, 'form')]
+            result['res_id'] = inv_ids and inv_ids[0] or False
+        return result
+
     def view_invoice(self, cr, uid, ids, context=None):
         '''
         This function returns an action that display existing invoices of given sale order ids. It can either be a in a list or in a form view, if there is only one invoice to show.
@@ -374,6 +395,7 @@ class purchase_order(osv.osv):
             'default_res_id': ids[0],
             'default_use_template': True,
             'default_template_id': template_id,
+            'default_composition_mode': 'comment',
             })
         return {
             'view_type': 'form',
@@ -547,7 +569,6 @@ class purchase_order(osv.osv):
             'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none',
             'type': 'in',
             'partner_id': order.dest_address_id.id or order.partner_id.id,
-            'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none',
             'purchase_id': order.id,
             'company_id': order.company_id.id,
             'move_lines' : [],
@@ -803,7 +824,7 @@ class purchase_order_line(osv.osv):
         cur_obj=self.pool.get('res.currency')
         tax_obj = self.pool.get('account.tax')
         for line in self.browse(cr, uid, ids, context=context):
-            taxes = tax_obj.compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty)
+            taxes = tax_obj.compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id, line.order_id.partner_id)
             cur = line.order_id.pricelist_id.currency_id
             res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
         return res
index 7e94a0b..db71a96 100644 (file)
                         <page string="Incoming Shipments &amp; Invoices">
                             <group>
                                 <group>
-                                    <field name="dest_address_id" string="Customer Address" on_change="onchange_dest_address_id(dest_address_id)"/>
+                                    <field name="dest_address_id" string="Customer Address" on_change="onchange_dest_address_id(dest_address_id)" groups="stock.group_locations"/>
                                     <field name="minimum_planned_date"/>
                                     <field name="location_id" groups="stock.group_locations"/>
                                     <field name="shipped" groups="base.group_no_one"/>
                                     <field name="date_approve" groups="base.group_no_one"/>
                                 </group>
                             </group>
-                            <separator string="Invoices"/>
-                            <field name="invoice_ids" context="{'type':'in_invoice', 'journal_type':'purchase'}"/>
                         </page>
                     </notebook>
                 </sheet>
index 19b059d..9e781d6 100644 (file)
@@ -43,14 +43,13 @@ class purchase_config_settings(osv.osv_memory):
         'group_costing_method':fields.boolean("Compute product cost price based on average cost",
             implied_group='product.group_costing_method',
             help="""Allows you to compute product cost price based on average cost."""),
-        'group_purchase_delivery_address': fields.boolean("Allow a different address for incoming products and invoicings",
+        'group_purchase_delivery_address': fields.boolean("Allow a different address for incoming products and invoicing",
             implied_group='purchase.group_delivery_invoice_address',
             help="Allows you to specify different delivery and invoice addresses on a purchase order."),
         'module_warning': fields.boolean("Alerts by products or supplier",
-            help="""Allow to configure warnings on products and trigger them when a user wants to purchase a given product or a given supplier.
-            Example: Product: this product is deprecated, do not purchase more than 5.
-                    Supplier: don't forget to ask for an express delivery."""),
-
+            help="""Allow to configure notification on products and trigger them when a user wants to purchase a given product or a given supplier.
+Example: Product: this product is deprecated, do not purchase more than 5.
+                Supplier: don't forget to ask for an express delivery."""),
         'module_purchase_double_validation': fields.boolean("Force two levels of approvals",
             help="""Provide a double validation mechanism for purchases exceeding minimum amount.
                 This installs the module purchase_double_validation."""),
index 9e5aa2e..188baec 100644 (file)
@@ -48,6 +48,8 @@
                         <button type="object"
                             name="view_picking"
                             string="Incoming Shipments" states="approved"/>
+                        <button type="object"  name="invoice_open"
+                            string="Invoices" attrs="{'invisible': [('state', '=', 'draft')]}"/> 
                     </div>
                 </xpath>
             </field>
index 9796d0b..da1a1ce 100644 (file)
@@ -25,7 +25,7 @@
             <field name="name">Automated Sale Order Notification Mail</field>
             <field name="email_from">${object.user_id.email or ''}</field>
             <field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
-            <field name="email_to">${object.partner_invoice_id.email}</field>
+            <field name="email_recipients">${object.partner_invoice_id.id}</field>
             <field name="model_id" ref="sale.model_sale_order"/>
             <field name="auto_delete" eval="True"/>
             <field name="body_html"><![CDATA[
index 9ff3863..d8e9d0f 100644 (file)
@@ -44,7 +44,7 @@ class sale_configuration(osv.osv_memory):
         'group_sale_pricelist':fields.boolean("Use pricelists to adapt your price per customers",
             implied_group='product.group_sale_pricelist',
             help="""Allows to manage different prices based on rules per category of customers.
-                Example: 10% for retailers, promotion of 5 EUR on this product, etc."""),
+Example: 10% for retailers, promotion of 5 EUR on this product, etc."""),
         'group_uom':fields.boolean("Allow using different units of measures",
             implied_group='product.group_uom',
             help="""Allows you to select and maintain different units of measure for products."""),
@@ -52,9 +52,9 @@ class sale_configuration(osv.osv_memory):
             implied_group='sale.group_discount_per_so_line',
             help="Allows you to apply some discount per sale order line."),
         'module_warning': fields.boolean("Allow configuring alerts by customer or products",
-            help="""Allow to configure warnings on products and trigger them when a user wants to sale a given product or a given customer.
-            Example: Product: this product is deprecated, do not purchase more than 5.
-            Supplier: don't forget to ask for an express delivery."""),
+            help="""Allow to configure notification on products and trigger them when a user wants to sale a given product or a given customer.
+Example: Product: this product is deprecated, do not purchase more than 5.
+                Supplier: don't forget to ask for an express delivery."""),
         'module_sale_margin': fields.boolean("Display margins on sales orders",
             help="""This adds the 'Margin' on sales order.
                 This gives the profitability by calculating the difference between the Unit Price and Cost Price.
index 033dd77..c1af024 100644 (file)
@@ -264,7 +264,24 @@ class sale_order(osv.osv):
 
         return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
 
+    def copy_quotation(self, cr, uid, ids, context=None):
+        id = self.copy(cr, uid, ids[0], context=None)
+        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': id,
+            'view_type': 'form',
+            'view_mode': 'form',
+            'view_id': view_id,
+            'target': 'current',
+            'nodestroy': True,
+        }
+
     def onchange_pricelist_id(self, cr, uid, ids, pricelist_id, order_lines, context=None):
+        context = context or {}
         if not pricelist_id:
             return {}
         value = {
@@ -601,6 +618,7 @@ class sale_order(osv.osv):
         }
 
     def action_wait(self, cr, uid, ids, context=None):
+        context = context or {}
         for o in self.browse(cr, uid, ids):
             if not o.order_line:
                 raise osv.except_osv(_('Error!'),_('You cannot confirm a sale order which has no line.'))
@@ -629,6 +647,7 @@ class sale_order(osv.osv):
             'default_res_id': ids[0],
             'default_use_template': True,
             'default_template_id': template_id,
+            'default_composition_mode': 'comment',
             'mark_so_as_sent': True
         })
         return {
@@ -903,7 +922,7 @@ class sale_order_line(osv.osv):
 
         result = {}
         warning_msgs = {}
-        product_obj = product_obj.browse(cr, uid, product, context=context)
+        product_obj = product_obj.browse(cr, uid, product, context=context_partner)
 
         uom2 = False
         if uom:
index a1f1462..da8d1b3 100644 (file)
                         <button name="action_quotation_send" string="Send by Mail" type="object" states="sent" 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" groups="base.group_user"/>
-                        <button name="action_button_confirm" states="draft" string="Confirm" type="object" groups="base.group_user"/>
-                        <button name="action_button_confirm" states="sent" string="Confirm" class="oe_highlight" type="object" 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" groups="base.group_user"/>
                         <button name="action_cancel" states="manual,progress" string="Cancel" type="object" groups="base.group_user"/>
                         <button name="invoice_cancel" states="invoice_except" string="Cancel" groups="base.group_user"/>
index 4e6d641..9ea2215 100644 (file)
@@ -123,6 +123,7 @@ class sale_advance_payment_inv(osv.osv_memory):
             # create the invoice
             inv_line_values = {
                 'name': res.get('name'),
+                'origin': sale.name,
                 'account_id': res['account_id'],
                 'price_unit': inv_amount,
                 'quantity': wizard.qtty or 1.0,
index 2597c1d..1022599 100644 (file)
@@ -64,7 +64,9 @@ class crm_make_sale(osv.osv_memory):
         """
         if context is None:
             context = {}
-
+        # update context: if come from phonecall, default state values can make the quote crash lp:1017353
+        context.pop('default_state', False)        
+        
         case_obj = self.pool.get('crm.lead')
         sale_obj = self.pool.get('sale.order')
         partner_obj = self.pool.get('res.partner')
index e73e681..e627b55 100644 (file)
@@ -15,7 +15,7 @@ openerp.share = function(session) {
         self.rpc('/web/session/eval_domain_and_context', {
             domains: [domain],
             contexts: [view.dataset.context]
-        }).then(function (result) {
+        }).done(function (result) {
             Share.create({
                 name: action.name,
                 record_name: rec_name,
@@ -24,8 +24,8 @@ openerp.share = function(session) {
                 user_type: user_type || 'embedded',
                 view_type: view.fields_view.type,
                 invite: invite || false,
-            }).then(function(share_id) {
-                var step1 = Share.call('go_step_1', [[share_id]]).then(function(result) {
+            }).done(function(share_id) {
+                var step1 = Share.call('go_step_1', [[share_id]]).done(function(result) {
                     var action = result;
                     self.do_action(action);
                 });
@@ -37,7 +37,7 @@ openerp.share = function(session) {
         if (!session.session.share_flag) {
             session.session.share_flag = $.Deferred(function() {
                 var func = new session.web.Model("share.wizard").get_func("has_share");
-                func(session.session.uid).pipe(function(res) {
+                func(session.session.uid).then(function(res) {
                     if(res) {
                         session.session.share_flag.resolve();
                     } else {
index 360b1ef..6a1f568 100644 (file)
@@ -74,7 +74,7 @@ class share_wizard(osv.TransientModel):
 
     def _user_type_selection(self, cr, uid, context=None):
         """Selection values may be easily overridden/extended via inheritance"""
-        return [('embedded', 'Direct link or embed code'), ('emails','Emails'), ]
+        return [('embedded', _('Direct link or embed code')), ('emails',_('Emails')), ]
 
     """Override of create() to auto-compute the action name"""
     def create(self, cr, uid, values, context=None):
index 250fe52..594843e 100644 (file)
@@ -41,7 +41,7 @@
                        <field name="sale_delay" attrs="{'readonly':[('sale_ok','=',False)]}"/>
                </group>
                <group name="delay" position="after">
-                       <group name="store" string="Storage Localization">
+                       <group name="store" string="Storage Location">
                         <field name="loc_rack"/>
                         <field name="loc_row"/>
                        <field name="loc_case"/>
                     </group>
                 </group>
                 <group name="status" position="after" version="7.0">
-                     <group name="store" string="Storage Localization">
+                     <group name="store" string="Storage Location">
                          <field name="company_id" groups="base.group_multi_company" widget="selection"/>
                          <field name="loc_rack" attrs="{'invisible':[('type','=','service')]}"/>
                          <field name="loc_row" attrs="{'invisible':[('type','=','service')]}"/>
index 606cb92..d18a55e 100644 (file)
@@ -1351,14 +1351,14 @@ class stock_picking(osv.osv):
                 wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
                 # Then we finish the good picking
                 self.write(cr, uid, [pick.id], {'backorder_id': new_picking})
-                self.action_move(cr, uid, [new_picking])
+                self.action_move(cr, uid, [new_picking], context=context)
                 wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_done', cr)
                 wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
                 delivered_pack_id = new_picking
                 back_order_name = self.browse(cr, uid, delivered_pack_id, context=context).name
                 self.back_order_send_note(cr, uid, ids, back_order_name, context)
             else:
-                self.action_move(cr, uid, [pick.id])
+                self.action_move(cr, uid, [pick.id], context=context)
                 wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_done', cr)
                 delivered_pack_id = pick.id
                 self.ship_done_send_note(cr, uid, ids, context)
@@ -1553,7 +1553,7 @@ class stock_production_lot(osv.osv):
         'product_id': lambda x, y, z, c: c.get('product_id', False),
     }
     _sql_constraints = [
-        ('name_ref_uniq', 'unique (name, ref)', 'The combination of serial number and internal reference must be unique !'),
+        ('name_ref_uniq', 'unique (name, ref)', 'The combination of Serial Number and internal reference must be unique !'),
     ]
     def action_traceability(self, cr, uid, ids, context=None):
         """ It traces the information of a product
@@ -2123,7 +2123,7 @@ class stock_move(osv.osv):
                 old_ptype = location_obj.picking_type_get(cr, uid, picking.move_lines[0].location_id, picking.move_lines[0].location_dest_id)
                 if old_ptype != picking.type:
                     old_pick_name = seq_obj.get(cr, uid, 'stock.picking.' + old_ptype)
-                    self.pool.get('stock.picking').write(cr, uid, [picking.id], {'name': old_pick_name}, context=context)
+                    self.pool.get('stock.picking').write(cr, uid, [picking.id], {'name': old_pick_name, 'type': old_ptype}, context=context)
             else:
                 pickid = False
             for move, (loc, dummy, delay, dummy, company_id, ptype) in todo:
@@ -2433,14 +2433,17 @@ class stock_move(osv.osv):
             if move.picking_id:
                 picking_ids.append(move.picking_id.id)
             if move.move_dest_id.id and (move.state != 'done'):
-                self.write(cr, uid, [move.id], {'move_history_ids': [(4, move.move_dest_id.id)]})
-                #cr.execute('insert into stock_move_history_ids (parent_id,child_id) values (%s,%s)', (move.id, move.move_dest_id.id))
-                if move.move_dest_id.state in ('waiting', 'confirmed'):
-                    self.force_assign(cr, uid, [move.move_dest_id.id], context=context)
-                    if move.move_dest_id.picking_id:
-                        wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
-                    if move.move_dest_id.auto_validate:
-                        self.action_done(cr, uid, [move.move_dest_id.id], context=context)
+                # Downstream move should only be triggered if this move is the last pending upstream move
+                other_upstream_move_ids = self.search(cr, uid, [('id','!=',move.id),('state','not in',['done','cancel']),
+                                            ('move_dest_id','=',move.move_dest_id.id)], context=context)
+                if not other_upstream_move_ids:
+                    self.write(cr, uid, [move.id], {'move_history_ids': [(4, move.move_dest_id.id)]})
+                    if move.move_dest_id.state in ('waiting', 'confirmed'):
+                        self.force_assign(cr, uid, [move.move_dest_id.id], context=context)
+                        if move.move_dest_id.picking_id:
+                            wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
+                        if move.move_dest_id.auto_validate:
+                            self.action_done(cr, uid, [move.move_dest_id.id], context=context)
 
             self._create_product_valuation_moves(cr, uid, move, context=context)
             if move.state not in ('confirmed','done','assigned'):
index 6c0b3e8..c3569c4 100644 (file)
@@ -17,7 +17,7 @@
         
         <record id="seq_type_picking_internal" model="ir.sequence.type">
             <field name="name">Picking INT</field>
-            <field name="code">stock.picking.internal</field>
+            <field name="code">stock.picking</field>
         </record>
 
         <!--
@@ -42,7 +42,7 @@
         
         <record id="seq_picking_internal" model="ir.sequence">
             <field name="name">Picking INT</field>
-            <field name="code">stock.picking.internal</field>
+            <field name="code">stock.picking</field>
             <field name="prefix">INT/</field>
             <field name="padding">5</field>
             <field name="company_id" eval="False"/>
index c63c385..9c2a672 100644 (file)
                         </group>
                     </group>
                     <notebook>
-                    <page string="General Informations">
+                    <page string="General Information">
                         <field name="inventory_line_id">
                             <tree string="Products" editable="bottom">
                                 <field domain="[('usage','=','internal')]" name="location_id" groups="stock.group_locations"/>
index 11907b6..8422412 100644 (file)
@@ -79,8 +79,6 @@ class stock_fill_inventory(osv.osv_memory):
 
         inventory_line_obj = self.pool.get('stock.inventory.line')
         location_obj = self.pool.get('stock.location')
-        product_obj = self.pool.get('product.product')
-        stock_location_obj = self.pool.get('stock.location')
         move_obj = self.pool.get('stock.move')
         uom_obj = self.pool.get('product.uom')
         if ids and len(ids):
index 863e51e..ec58c37 100644 (file)
@@ -14,7 +14,7 @@ openerp.web_linkedin = function(instance) {
         },
         test_linkedin: function() {
             var self = this;
-            return this.test_api_key().pipe(function() {
+            return this.test_api_key().then(function() {
                 if (self.linkedin_added)
                     return self.linkedin_def.promise();
                 var tag = document.createElement('script');
@@ -37,7 +37,7 @@ openerp.web_linkedin = function(instance) {
             if (this.api_key) {
                 return $.when();
             }
-            return new instance.web.Model("ir.config_parameter").call("get_param", ["web.linkedin.apikey"]).pipe(function(a) {
+            return new instance.web.Model("ir.config_parameter").call("get_param", ["web.linkedin.apikey"]).then(function(a) {
                 if (!!a) {
                     self.api_key = a;
                     return true;
@@ -69,13 +69,13 @@ openerp.web_linkedin = function(instance) {
         },
         search_linkedin: function() {
             var self = this;
-            this.display_dm.add(instance.web_linkedin.tester.test_linkedin()).then(function() {
+            this.display_dm.add(instance.web_linkedin.tester.test_linkedin()).done(function() {
                 var pop = new instance.web_linkedin.LinkedinPopup(self, self.get("value"));
                 pop.open();
                 pop.on("selected", this, function(entity) {
                     self.selected_entity(entity);
                 });
-            }, _.bind(this.linkedin_disabled, this));
+            }).fail(_.bind(this.linkedin_disabled, this));
         },
         linkedin_disabled: function() {
             instance.web.dialog($(QWeb.render("LinkedIn.DisabledWarning")), {
@@ -87,7 +87,7 @@ openerp.web_linkedin = function(instance) {
         },
         selected_entity: function(entity) {
             var self = this;
-            this.create_on_change(entity).then(function(to_change) {
+            this.create_on_change(entity).done(function(to_change) {
                 self.view.set_values(to_change);
             });
         },
@@ -101,7 +101,7 @@ openerp.web_linkedin = function(instance) {
                 to_change.image = false;
                 if (entity.logoUrl) {
                     defs.push(self.rpc('/web_linkedin/binary/url2binary',
-                                       {'url': entity.logoUrl}).pipe(function(data){
+                                       {'url': entity.logoUrl}).then(function(data){
                         to_change.image = data;
                     }));
                 }
@@ -120,7 +120,7 @@ openerp.web_linkedin = function(instance) {
                     }).error(function() {
                         children_def.reject();
                     });
-                defs.push(children_def.pipe(function(result) {
+                defs.push(children_def.then(function(result) {
                     result = _.reject(result.people.values || [], function(el) {
                         return ! el.formattedName;
                     });
@@ -128,7 +128,7 @@ openerp.web_linkedin = function(instance) {
                         el.__type = "people";
                         return self.create_on_change(el);
                     });
-                    return $.when.apply($, defs).pipe(function() {
+                    return $.when.apply($, defs).then(function() {
                         var p_to_change = _.toArray(arguments);
                         to_change.child_ids = p_to_change;
                     });
@@ -144,7 +144,7 @@ openerp.web_linkedin = function(instance) {
                 to_change.image = false;
                 if (entity.pictureUrl) {
                     defs.push(self.rpc('/web_linkedin/binary/url2binary',
-                                       {'url': entity.pictureUrl}).pipe(function(data){
+                                       {'url': entity.pictureUrl}).then(function(data){
                         to_change.image = data;
                     }));
                 }
@@ -163,7 +163,7 @@ openerp.web_linkedin = function(instance) {
                 to_change.linkedinUrl = entity.publicProfileUrl;
                 */
             }
-            return $.when.apply($, defs).pipe(function() {
+            return $.when.apply($, defs).then(function() {
                 return to_change;
             });
         },
@@ -186,7 +186,7 @@ openerp.web_linkedin = function(instance) {
             this._super();
             var self = this;
             this.on("authentified", this, this.authentified);
-            instance.web_linkedin.tester.test_authentication().then(function() {
+            instance.web_linkedin.tester.test_authentication().done(function() {
                 self.trigger("authentified");
             });
         },
@@ -200,7 +200,7 @@ openerp.web_linkedin = function(instance) {
                     encodeURI(this.text), this.limit)).result(function (result) {
                 cdef.resolve(result);
             });
-            var def = cdef.pipe(function(companies) {
+            var def = cdef.then(function(companies) {
                 var lst = companies.companies.values || [];
                 lst = _.first(lst, self.limit);
                 lst = _.map(lst, function(el) {
@@ -214,7 +214,7 @@ openerp.web_linkedin = function(instance) {
                 params({"keywords": this.text, "count": this.limit}).result(function(result) {
                 pdef.resolve(result);
             });
-            var def2 = pdef.pipe(function(people) {
+            var def2 = pdef.then(function(people) {
                 var plst = people.people.values || [];
                 plst = _.first(plst, self.limit);
                 plst = _.map(plst, function(el) {
index 2f92950..e7cbbac 100644 (file)
@@ -46,7 +46,7 @@ instance.web_shortcuts.Shortcuts = instance.web.Widget.extend({
     load: function() {
         var self = this;
         this.$el.find('.oe_systray_shortcuts_items').empty();
-        return this.rpc('/web/shortcuts/list', {}).then(function(shortcuts) {
+        return this.rpc('/web/shortcuts/list', {}).done(function(shortcuts) {
             _.each(shortcuts, function(sc) {
                 self.trigger('display', sc);
             });
@@ -80,7 +80,7 @@ instance.web_shortcuts.Shortcuts = instance.web.Widget.extend({
             id = $link.data('id');
         self.session.active_id = id;
         // TODO: Use do_action({menu_id: id, type: 'ir.actions.menu'})
-        self.rpc('/web/menu/action', {'menu_id': id}).then(function(ir_menu_data) {
+        self.rpc('/web/menu/action', {'menu_id': id}).done(function(ir_menu_data) {
             if (ir_menu_data.action.length){
                 instance.webclient.on_menu_action({action_id: ir_menu_data.action[0][2].id});
             }
@@ -96,7 +96,7 @@ instance.web.UserMenu.include({
     do_update: function() {
         var self = this;
         this._super.apply(this, arguments);
-        this.update_promise.then(function() {
+        this.update_promise.done(function() {
             if (self.shortcuts) {
                 self.shortcuts.trigger('load');
             } else {
@@ -110,7 +110,7 @@ instance.web.UserMenu.include({
 instance.web.ViewManagerAction.include({
     switch_mode: function (view_type, no_store) {
         var self = this;
-        this._super.apply(this, arguments).then(function() {
+        this._super.apply(this, arguments).done(function() {
             self.shortcut_check(self.views[view_type]);
         });
     },